samesite
3 TopicsiRule to set SameSite for compatible clients and remove it for incompatible clients (LTM|ASM|APM)
A bunch of us have been refining approaches to help customers handle the new browser enforcement of the HTTP cookie SameSite attribute. I think we have a pretty solid approach now to handle compatible and incompatible user-agents. The iRule: Allows the admin to set the SameSite attribute on BIG-IP and web application cookies (all cookies, explicitly named cookies or cookies that start with a string) for user-agents that handle the SameSite attribute Allows the admin to remove the SameSite attribute for user-agents which do not support SameSite=None. The behavior for an incompatible client receiving a cookie with SameSite not set should be the same as a compatible client handling SameSite=None (the incompatible client should send the cookie on third party requests) The iRule uses Simon Kowallik's updated string matching logic to handle the incompatible user-agent from Chomium's blog: https://www.chromium.org/updates/same-site/incompatible-clients Note this iRule only modifies BIG-IP and web application cookies found in Set-Cookie headers. It does not attempt to modify cookies that the BIG-IP or web application sets via Javascript or other methods. BIG-IP ASM is known to set some cookies via Javascript. If you require support for this, please open a case with F5 support (https://support.f5.com) and request your case be added to: BZ875909: Allow admin configuration of SameSite attribute on ASM system cookies set via Set-Cookie and Javascript Updates to the iRule can be found in the irules-toolbox repo on GitHub. This specific version is for v12+, but there is a pre-v12 version in the repo as well. Configuration options in the iRule: samesite_security: Set this to Strict, Lax or None. The description for these values is in the iRule quoted below: # Set BIG-IP and app cookies found in Set-Cookie headers using this iRule to: # # none: Cookies will be sent in both first-party context and cross-origin requests; #however, the value must be explicitly set to None and all browser requests must #follow the HTTPS protocol and include the Secure attribute which requires an encrypted #connection. Cookies that don't adhere to that requirement will be rejected. #Both attributes are required together. If just None is specified without Secure or #if the HTTPS protocol is not used, the third-party cookie will be rejected. # # lax: Cookies will be sent automatically only in a first-party context and with HTTP GET requests. #SameSite cookies will be withheld on cross-site sub-requests, such as calls to load images or iframes, #but will be sent when a user navigates to the URL from an external site, e.g., by following a link. # # strict: browser never sends cookies in requests to third party domains # #Above definitions from: https://docs.microsoft.com/en-us/microsoftteams/platform/resources/samesite-cookie-update # # Note: this iRule does not modify cookies set on the client using Javascript or other methods outside of Set-Cookie headers! set samesite_security "none" Uncomment the next command if you're using this iRule on an APM virtual server with an access profile: # Uncomment when using this iRule on an APM-enabled virtual server so the MRHSession cookies will be rewritten # The iRule cannot be saved on a virtual server with this option uncommented if there is no Access profile also enabled #ACCESS::restrict_irule_events disable Now define whether you want to rewrite all web application and BIG-IP cookies found in the Set-Cookie header(s). Set this to 1 to rewrite SameSite on all cookies in Set-Cookie headers. Else, if you want to define specifically named or prefixed cookies, set this option to 0, and proceed to the next two config options, #2 and #3 # 1. If you want to set SameSite on all BIG-IP and web application cookies for compliant user-agents, set this option to 1 # Else, if you want to use the next two options for rewriting explicit named cookies or cookie prefixes, set this option to 0 set set_samesite_on_all 0 If you don't want to rewrite all cookies using option #1 above, you can choose to rewrite explicitly named cookies in option #2. Set the exact cookie names in the named_cookie list. Replace MRHSession and LastMRH_Session, which are examples of the cookies APM uses. If you do not want to rewrite exact cookie names, comment out the first example and uncomment the second example "set named_cookies {}" # 2. Rewrite SameSite on specific named cookies # # To enable this, list the specific named cookies in the list command and comment out the second set command below # To disable this, set this variable to {} and comment out the first set command below set named_cookies [list {MRHSession} {LastMRH_Session}] #set named_cookies {} If you don't want to rewrite all cookies using option #1 above, you can choose to rewrite cookies using a prefix in option #3. Set the cookie name prefixes in the named_cookie list. Replace BIGipServer and TS, which are examples of the cookie prefixes LTM uses for persistence and ASM uses for session tracking, with the prefixes of the cookie names you want to rewrite. If you do not want to rewrite using cookie name prefixes, comment out the first example and uncomment the second example "set named_cookies {}" # 3. Rewrite cookies with a prefix like BIG-IP persistence cookies # To enable this, list the cookie name prefixes in the list command and comment out the second set command below # To disable this, set this variable to {} and comment out the first set command below set cookie_prefixes [list {BIGipServer} {TS}] #set cookie_prefixes {} If your application or BIG-IP configuration sets cookies in the Set-Cookie headers with SameSite=None, incompatible user-agents will either reject the cookie or treat the cookie as if it was set for SameSite=Strict (https://www.chromium.org/updates/same-site/incompatible-clients). You can set remove_samesite_for_incompatible_user_agents to 1 to have this iRule remove SameSite attributes from all cookies sent to incompatible browsers. # For incompatible user-agents, this iRule can remove the SameSite attribute from all cookies sent to the client via Set-Cookie headers # This is only necessary if BIG-IP or the web application being load balanced sets SameSite=None for all clients # set to 1 to enable, 0 to disable set remove_samesite_for_incompatible_user_agents 1 While testing, you can set samesite_debug to 1 to test and get debug written to /var/log/ltm. Make sure to disable this option when you're done testing, before putting the iRule into production! # Log debug to /var/log/ltm? 1=yes, 0=no # set to 0 after testing set samesite_debug 1 The full iRule: (Updates can be found in the irules-toolbox repo on GitHub. This specific version is for v12+, but there is a pre-v12 version in the repo as well.) # iRule: samesite_cookie_handling # author: Simon Kowallik # version: 1.3 # # History: version - author - description # 1.0 - Simon Kowallik - initial version # 1.1 - Aaron Hooley - updated to add support for setting SameSite to Strict|Lax|None for BIG-IP and app cookies in Set-Cookie headers # - Add option to remove SameSite=None cookies for incompatible browsers # 1.2 - Aaron Hooley - Added option to rewrite all cookies without naming them explicitly or with prefixes # 1.3 - Aaron Hooley - set samesite_compatible to 0 by default instead of a null string # # What the iRule does: # Sets SameSite to Strict, Lax or None (and sets Secure when SameSite=None) for compatible user-agents # Optionally removes SameSite attribute from all cookies for incompatible user-agents so they'll handle cookies as if they were SameSite=None # # The iRule should work for: # - LTM for web app cookies and persistence cookies, except those that the web app sets via Javascript # - ASM for web app cookies and all ASM cookies except those that ASM or the web app sets via Javascript # - APM for web app cookies and all APM cookies you configure in the config variable $named_cookies, except those that the web app sets via Javascript # # The iRule requires BIG-IP v12 or greater to use the HTTP::cookie attribute command # # RFC "standards" # https://tools.ietf.org/html/draft-west-cookie-incrementalism-00 # https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-05 # further reading: # https://web.dev/samesite-cookies-explained/ # https://web.dev/samesite-cookie-recipes/ # https://blog.chromium.org/2019/10/developers-get-ready-for-new.html # https://www.chromium.org/updates/same-site # https://www.chromium.org/updates/same-site/incompatible-clients proc checkSameSiteCompatible {user_agent} { # Procedure to check if a user-agent supports SameSite=None on cookies # # usage: # set isSameSiteCompatible [call checkSameSiteCompatible {User-Agent-String}] # # check for incompatible user-agents: https://www.chromium.org/updates/same-site/incompatible-clients # based on https://devcentral.f5.com/s/articles/HTTP-cookie-SameSite-test-detection-of-browsers-with-incompatible-SameSite-None-handling switch -glob -- [set user_agent [string tolower $user_agent]] { {*chrome/5[1-9].[0-9]*} - {*chrome/6[0-6].[0-9]*} - {*chromium/5[1-9].[0-9]*} - {*chromium/6[0-6].[0-9]*} - {*ip?*; cpu *os 12*applewebkit*} - {*macintosh;*mac os x 10_14*version*safari*} - {mozilla*macintosh;*mac os x 10_14*applewebkit*khtml, like gecko*} { # no samesite support return 0 } {*ucbrowser/*} { switch -glob -- $user_agent { {*ucbrowser/[1-9].*} - {*ucbrowser/1[0-1].*} - {*ucbrowser/12.[0-9].*} - {*ucbrowser/12.1[0-1].*} - {*ucbrowser/12.12.*} - {*ucbrowser/12.13.[0-2]*} { # no samesite support return 0 } } } } # If the current user-agent didn't match any known incompatible browser list, assume it can handle SameSite=None return 1 # CPU Cycles on Executing (>100k test runs) # Average 22000-42000 (fastest to slowest path) # Maximum 214263 # Minimum 13763 } # the iRule code when CLIENT_ACCEPTED priority 100 { # Set BIG-IP and app cookies found in Set-Cookie headers using this iRule to: # # none: Cookies will be sent in both first-party context and cross-origin requests; # however, the value must be explicitly set to None and all browser requests must # follow the HTTPS protocol and include the Secure attribute which requires an encrypted # connection. Cookies that don't adhere to that requirement will be rejected. # Both attributes are required together. If just None is specified without Secure or # if the HTTPS protocol is not used, the third-party cookie will be rejected. # # lax: Cookies will be sent automatically only in a first-party context and with HTTP GET requests. # SameSite cookies will be withheld on cross-site sub-requests, such as calls to load images or iframes, # but will be sent when a user navigates to the URL from an external site, e.g., by following a link. # # strict: browser never sends cookies in requests to third party domains # # Above definitions from: https://docs.microsoft.com/en-us/microsoftteams/platform/resources/samesite-cookie-update # # Note: this iRule does not modify cookies set on the client using Javascript or other methods outside of Set-Cookie headers! set samesite_security "none" # Uncomment when using this iRule on an APM-enabled virtual server so the MRHSession cookies will be rewritten # The iRule cannot be saved on a virtual server with this option uncommented if there is no Access profile also enabled #ACCESS::restrict_irule_events disable # 1. If you want to set SameSite on all BIG-IP and web application cookies for compliant user-agents, set this option to 1 # Else, if you want to use the next two options for rewriting explicit named cookies or cookie prefixes, set this option to 0 set set_samesite_on_all 0 # 2. Rewrite SameSite on specific named cookies # # To enable this, list the specific named cookies in the list command and comment out the second set command below # To disable this, set this variable to {} and comment out the first set command below set named_cookies [list {MRHSession} {LastMRH_Session}] #set named_cookies {} # 3. Rewrite cookies with a prefix like BIG-IP persistence cookies # To enable this, list the cookie name prefixes in the list command and comment out the second set command below # To disable this, set this variable to {} and comment out the first set command below set cookie_prefixes [list {BIGipServer} {TS}] #set cookie_prefixes {} # For incompatible user-agents, this iRule can remove the SameSite attribute from all cookies sent to the client via Set-Cookie headers # This is only necessary if BIG-IP or the web application being load balanced sets SameSite=None for all clients # set to 1 to enable, 0 to disable set remove_samesite_for_incompatible_user_agents 1 # Log debug to /var/log/ltm? 1=yes, 0=no # set to 0 after testing set samesite_debug 1 # You shouldn't have to make changes to configuration below here # Track the user-agent and whether it supports the SameSite cookie attribute set samesite_compatible 0 set user_agent {} if { $samesite_debug }{ set prefix "[IP::client_addr]:[TCP::client_port]:" log local0. "$prefix [string repeat "=" 40]" log local0. "$prefix \$samesite_security=$samesite_security; \$set_samesite_on_all=$set_samesite_on_all; \$named_cookies=$named_cookies; \$cookie_prefixes=$cookie_prefixes, \ \$remove_samesite_for_incompatible_user_agents=$remove_samesite_for_incompatible_user_agents" } } # Run this test event before any other iRule HTTP_REQUEST events to set the User-Agent header value # Comment out this event when done testing user-agents #when HTTP_REQUEST priority 2 { # known compatible # HTTP::header replace user-agent {my compatible user agent string} # known INcompatible # HTTP::header replace user-agent {chrome/51.10} #} # Run this iRule before any other iRule HTTP_REQUEST events when HTTP_REQUEST priority 100 { # If we're setting samesite=none, we need to check the user-agent to see if it's compatible if { not [string equal -nocase $samesite_security "none"] }{ # Not setting SameSite=None, so exit this event return } # Inspect user-agent once per TCP session for higher performance if the user-agent hasn't changed if { $samesite_compatible == 0 or $user_agent ne [HTTP::header value {User-Agent}]} { set user_agent [HTTP::header value {User-Agent}] set samesite_compatible [call checkSameSiteCompatible $user_agent] if { $samesite_debug }{ log local0. "$prefix Got \$samesite_compatible=$samesite_compatible and saved current \$user_agent: $user_agent" } } } # Run this response event with priority 900 after all other iRules to parse the final cookies from the application and BIG-IP when HTTP_RESPONSE_RELEASE priority 900 { # Log the pre-existing Set-Cookie header values if { $samesite_debug }{ log local0. "$prefix Set-Cookie value(s): [HTTP::header values {Set-Cookie}]" } if { $samesite_compatible } { # user-agent is compatible with SameSite=None, set SameSite on matching cookies if { $set_samesite_on_all }{ if { $samesite_debug }{ log local0. "$prefix Setting SameSite=$samesite_security on all cookies and exiting" } foreach cookie [HTTP::cookie names] { if { $samesite_debug }{ log local0. "$prefix Set SameSite=$samesite_security on $cookie" } # Remove any prior instances of SameSite attributes HTTP::cookie attribute $cookie remove {samesite} # Insert a new SameSite attribute HTTP::cookie attribute $cookie insert {samesite} $samesite_security # If samesite attribute is set to None, then the Secure flag must be set for browsers to accept the cookie if {[string equal -nocase $samesite_security "none"]} { HTTP::cookie secure $cookie enable } } # Exit this event in this iRule as we've already rewritten all cookies with SameSite return } # Match named cookies exactly if { $named_cookies ne {} }{ foreach cookie $named_cookies { if { [HTTP::cookie exists $cookie] } { # Remove any pre-existing SameSite attributes from this cookie as most clients use the most strict value if multiple instances are set HTTP::cookie attribute $cookie remove {SameSite} # Insert the SameSite attribute HTTP::cookie attribute $cookie insert {SameSite} $samesite_security # If samesite attribute is set to None, then the Secure flag must be set for browsers to accept the cookie if {[string equal -nocase $samesite_security "none"]} { HTTP::cookie secure $cookie enable } if { $samesite_debug }{ log local0. "$prefix Matched explicitly named cookie $cookie, set SameSite=$samesite_security" } if { $samesite_debug }{ log local0. "$prefix " } } } } # Match a cookie prefix (cookie name starts with a prefix from the $cookie_prefixes list) if { $cookie_prefixes ne {} }{ foreach cookie [HTTP::cookie names] { foreach cookie_prefix $cookie_prefixes { if { $cookie starts_with $cookie_prefix } { # Remove any pre-existing SameSite attributes from this cookie as most clients use the most strict value if multiple instances are set HTTP::cookie attribute $cookie remove {SameSite} # Insert the SameSite attribute HTTP::cookie attribute $cookie insert {SameSite} $samesite_security # If samesite attribute is set to None, then the Secure flag must be set for browsers to accept the cookie if { [string equal -nocase $samesite_security "none"] } { HTTP::cookie secure $cookie enable } if { $samesite_debug }{ log local0. "$prefix Matched prefixed cookie $cookie, with prefix $cookie_prefix, set SameSite=$samesite_security, breaking from loop" } break } } } } } else { # User-agent can't handle SameSite=None if { $remove_samesite_for_incompatible_user_agents }{ # User-agent can't handle SameSite=None, so remove SameSite attribute from all cookies if SameSite=None # This will use CPU cycles on BIG-IP so only enable it if you know BIG-IP or the web application is setting # SameSite=None for all clients including incompatible ones foreach cookie [HTTP::cookie names] { if { [string tolower [HTTP::cookie attribute $cookie value SameSite]] eq "none" }{ HTTP::cookie attribute $cookie remove SameSite if { $samesite_debug }{ log local0. "$prefix Removing SameSite for incompatible client from cookie=$cookie" } } } } } # Log the modified Set-Cookie header values if { $samesite_debug }{ log local0. "$prefix Final Set-Cookies: [HTTP::header values {Set-Cookie}]" } }14KViews9likes21CommentsIncreased Security With First Party Cookies
HTTP cookies are an essential part of many web based applications, useful for tracking session and state information. But they can also be exploited to leak information to third party sites using a method known as Cross Site Request Forgery (CSRF). A CSRF attack takes advantage of the web browser behavior which results in cookies being sent to third party sites when a page contains mixed content. This results in cross-site information leakage, and depending on the content of the cookies, could provide an attacker with information to hijack a user session. SameSite Attribute As of this writing, there is a Internet draft standard for directing clients to only send ‘first party’ cookies. In a nutshell, the standard defines a new, backwards-compatible attribute for the Set-Cookie header named SameSite. When the SameSite attribute is present, compliant browsers will only send that cookie with requests where the requested resource and the top-level browsing context match the cookie. This becomes another layer of a “defense in depth” strategy, mitigating CSRF and cross-site script including (XSSI) attacks. SameSite is supported in recent Chrome and Firefox browsers. SameSite can be specified alone, or with explicit values “Strict” or “Lax”, corresponding to differing levels of lock-down. Specifying SameSite can increase security, but it is not appropriate for all applications. One example would be “mash-up” applications, those which intentionally pull and embed content from different sites, may require cross-site cookies to function correctly. Also, some single sign-on features may require cross-context authentication that involves cookies. So how can you secure your apps? Big IP provides 3 ways to add SameSite attribute to Set-Cookie headers, two of which are described below: iRules and LTM Policy. Mentioned in another article, the Application Security module also provides a setting to enable SameSite. iRule to add SameSite attribute Here is iRule which can handle multiple Set-Cookie headers in a response. If a Set-Cookie header already has SameSite attribute present, it is passed through unmodified. This allows an administrator to set a baseline security level, say by specifying “SameSite=Lax” in an iRule, but allows for individual apps to control their security level by generating headers with their own Set-Cookie header, with say “SameSite=Strict”. when HTTP_RESPONSE { # Set-Cookie header can occur multiple times, treat as list set num [HTTP::header count Set-Cookie] if {$num > 0} { foreach set_cookie [HTTP::header values Set-Cookie] { # only modify if header does not have SameSite attribute set foundSameSite [string match -nocase "*SameSite*" $set_cookie ] if {[expr {!$foundSameSite} ]} { set set_cookie [concat $set_cookie "; SameSite"] } # collect modified and unmodified values in list newcookies lappend newcookies $set_cookie } if {$num == 1} { # overwrite existing HTTP::header Set-Cookie [lindex $newcookies 0] } else { # remove and replace HTTP::header remove Set-Cookie foreach set_cookie $newcookies { HTTP::header insert Set-Cookie $set_cookie } } } } LTM Policy Below is a sample LTM Policy which will tag “; SameSite” to the end of a Set-Cookie header that doesn’t have one already. One limitation to be aware of is that there can be multiple Set-Cookie headers in an HTTP response, and LTM policy can only replace the last one. Here is a screenshot from the GUI showing an LTM Policy rule which Here is the resulting policy as it would appear in the /config/bigip.conf configuration file: ltm policy first-party-cookies { requires { http } rules { r1 { actions { 0 { http-header response replace name Set-Cookie value "tcl:[HTTP::header Set-Cookie]; SameSite" } } conditions { 0 { http-header response name Set-Cookie not contains values { SameSite } } } } } status published strategy first-match }8.1KViews1like16CommentsLightboard Lessons: HTTP Cookie SameSite Attribute
In this episode of Lightboard Lessons, Jason covers the SameSite attribute on HTTP cookies, and the implications for site developers and end users when Chrome begins enforcing a default behavior set to "lax" later this month in a limited rollout for Chrome v80 stable users. This should be addressed in the applications, but BIG-IP can help via iRules and local traffic policies as briefly described in the video, as well as ASM module settings and through NGINX directives. Resources Start Here: Article: Handling Incompatible Clients AskF5 Knowledge Article on SameSite enforcement: K03346798 Article: Increased Security with First Party Cookies Additional iRule options: Codeshare: Setting SameSite on LTM Persistence Cookies Codeshare: Setting SameSite on All Web App & BIG-IP Cookies Codeshare: Add SameSite Attribute to APM Cookies Article: Detecting SameSite=None Incompatible Browsers ASM & NGINX Configuration Options ASM Manual info on SameSite NGINX proxy_cookie_path (Example: proxy_cookie_path / "/; secure; HttpOnly; SameSite=none";) NGINX sticky cookie (Example: sticky cookie srv_id expires=1h httponly secure “path=/; SameSite”;) Industry Insight Chromium Updates on SameSite (offsite) CSRF is (really) dead (offsite)2.2KViews5likes1Comment