Increased 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.1KViews1like16CommentsBack to Basics: The Many Faces of Load Balancing Persistence
Finally! It all makes sense now! Thanks to cloud and the very generic "sticky sessions", many more people are aware of persistence as it relates to load balancing. It's a critical capability of load balancing without which stateful applications (which is most of them including VDI, most web applications, and data analysis tools) would simply fail to scale. Persistence is, in general, like the many moods of Spock. They all look pretty much the same from the outside - ensure that a user, once connected, continues to be connected to the same application instance to ensure access to whatever state is stored in that instance. But though they act the same (and Spock's expression appears the same) deep down, where it counts, persistence is very different depending on how it's implemented. It requires different processing, different inspection, different data, even. Understanding these differences is important because each one has a different impact on performance. The Many Faces of Persistence There are several industry de facto standard types of persistence: simple, SSL, and cookie. Then there are more advanced forms of persistence: SIP, WTS, Universal and Hash. Generally speaking the de facto standard types of persistence are applicable for use with just about any web application. The more advanced forms of persistence are specific to a protocol or rely on a capability that is not necessarily standardized across load balancing services. Without further adieu, let's dive in! Simple Persistence Simple persistence is generally based on network characteristics, like source IP address. It can also include the destination port, to give the load balancer a bit more capacity in terms of simultaneously applications supported. Best practices avoid simple persistence to avoid reoccurrence of the mega-proxy problem which had a tendency to overwhelm application instances. Network load balancing uses a form of simple persistence. SSL Session ID Persistence SSL Session ID persistence became necessary when SSL was broadly accepted as the de facto means of securing traffic in flight for web applications. Because SSL sessions need to be established and are very much tied to a session between client and server, failing to "stick" SSL-secured sessions results in renegotiation of the session, which takes a noticeable amount of time and annoys end-users. To avoid unnecessary renegotiation, load balancers use the SSL Session ID to ensure sessions are properly routed to the application instance to which they first connected. Cookie Persistence Cookie persistence is a technique invented by F5 (shameless plug) that uses the HTTP cookie header to persist connections across a session. Most application servers insert a session id into responses that is used by developers to access data stored in the server session (shopping carts, etc... ). This value is used by load balancing services to enable persistence. This technique avoids the issues associated with simple persistence because the session id is unique. Universal Persistence Universal persistence is the use of any piece of data (network, application protocol, payload) to persist a session. This technique requires the load balancer to be able to inspect and ultimately extract any piece of data from a request or response. This technique is the basis for application-specific persistence solutions addressing popular applications like SIP, WTS, and more recently, VMware View. SIP, WTS, Username Persistence Session Initiation Protocol (SIP) and Windows Terminal Server (WTS) persistence are application-specific persistence techniques that use data unique to a session to persist connections. Username persistence is a similar technique designed to address the needs of VDI - specifically VMware View solutions - in which sessions are persisted (as one might expect) based on username. When a type of persistence becomes very commonly used it is often moved from being a customized, universal persistence implementation to a native, productized persistence profile. This improves performance and scalability by removing the need to inspect and extract the values used to persist sessions from the data flow and results in an application-specific persistence type, such as SIP or WTS. Hash Persistence Hash persistence is the use of multiple values within a request to enable persistence. To avoid problems with simple persistence, for example, a hash value may be created based on Source IP, Destination IP, Destination Port. While not necessarily unique to every session, this technique results in a more even distribution of load across servers. Non-unique value-based persistence techniques (simple, hash) are generally used with stateless applications or streaming content (video, audio) as a means to more evenly distribute load. Unique value-based persistence techniques (universal, application-specific, SSL ID) are generally used with stateful applications that depend on the client being connected to the same application instance through the session's life. Cookie persistence can be used with both techniques, provided the application is web based and uses HTTP headers for each request (Web Sockets breaks this technique).4.1KViews0likes1CommentHow to generate the persistence cookie with an iRule
Problem this snippet solves: When you configure a cookie persistence profile to use the HTTP Cookie Insert or HTTP Cookie Rewrite method, the BIG-IP system inserts a cookie into the HTTP response. The cookie value contains the encoded IP address and port of the destination server. Exemple of a cookie value : 1677787402.36895.0000 (See SOL6917 for more information about this topic) Let's assume that you want your pool member to receive a copy of this cookie value in an HTTP header. Because for example you want your application to forge an url where the cookie value is in a GET parameter. (NOTE : I cannot modify the behavior of the application, I can only play with headers) Retrieving the cookie value is pretty easy with iRule : [HTTP::cookie value $cookie_name] But you'll notice that there is a little issue with this feature: when you are a new visitor, the persistence cookie is inserted in the HTTP response ... Meaning that for the very first hit made by the visitor, there will be NO cookie value to retrieve ... In my scenario it was an issue to miss this cookie value on the first hit, so I had to come up with a solution to forge the cookie value based on pool member IP and port when the persistence cookie is missing. I chose to adapt the code found here and there (thanks !) EDIT : Well I figured out that if you are not using a default route-domain the persistence cookie value will be different (see https://support.f5.com/csp/article/K6917 ) Here is the alternative code bloc to use IPv4 non-default route domains: set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" set PORT [LB::server port] set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" How to use this snippet: To summarize what the iRule does : if the persistence cookie doesn't exist (most likely because it's the very first hit), then calculate it from member IP and PORT (it obviously has to be after the "When LB_SELECTED" statement) ; else just read the existing cookie. You can set the $cookie_name parameter manually, or let the iRule identify it Code : when LB_SELECTED { #set cookie_name SERVERID # following function could determine persistence cookie name being used if not manually set by the previous line if {not [info exists cookie_name]} { if { [set cookie_name [PROFILE::persist mode cookie cookie_name]] eq "" } { set cookie_name "BIGipServer[getfield [LB::server pool] "/" 3]" } #Default cookie name requires the getfield "/" 3 purge otherwise it's /Common/pool_name } if { [set COOKIE [HTTP::cookie value $cookie_name]] == "" } { scan [LB::server addr] {%d.%d.%d.%d} a b c d set ADDR [expr { $a + $b * 256 + $c * 65536 + $d * 16777216 }] set PORT [ntohs [LB::server port]] set COOKIE "${ADDR}.${PORT}.0000" ## Following bloc must be used instead if you are using non-default route domains, see K6917 #set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" #set PORT [LB::server port] #set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" ######### unset a b c d ADDR PORT #log local0. "$cookie_name = $COOKIE created for [HTTP::uri]" } else { #log local0. "$cookie_name = $COOKIE already exists for [HTTP::uri]" } HTTP::header insert X-F5-persist $COOKIE } Tested this on version: 11.52.2KViews2likes1CommentRemoval Cookies on Client Browser
Hello I'm trying to erase a bunc of cookies which belongs to expired session. Cookies are sending by the servers (CyberArk) and they are session cookies. Here in this scenario the F5 is used as Auth provider with an APM policy along with the load balancing. The APM policy is in LTM+APM mode so there is no webtop, connectivity profile and advanced resource assaing agent. This is not a new deployment but after the upgrade of Cyberark software in pool members, this design started to act a bit weird. When predefined idle timeout expires on CyberArk, the consequent requests get an error message on client and client sees an empty white page. After claening every thing (Ctrl+Shift+Del or using incognito mode) on the browser the problem vanishes. According to Cyberark support, the client sends a token that is not valid anymore. We identified some cookies that look like carrying these tokens and we wrote an irule to tell the client for clean all those subjected cookies when an APM session started. Seems like the iRule sends all needed cleaning declarations to the client browser and we saw all those cookies removed from cookie store of browser. But somehow some of them (CA11111, CA22222 and CA66666) is still carrying old token informations. According to my google-fu, there is no special removing methods besides mine. Also, found no information about that whether need to specify all those attributes used at the set-cookie moment as well while removing them. I mean those "domain=", "path=", "secure" and other attributes sent by server along with cookie at the beginning. The cookies sent by server: Set-Cookie: CA33333=; path=/PasswordVault/; SameSite=Strict; secure; HttpOnly Set-Cookie: __AntiXsrfToken=; expires=Tue, 11-May-1993 08:57:48 GMT; path=/; secure; HttpOnly Set-Cookie: CA44444=64D55E4839F5ED0032A7D0A7863EB07336F49030; path=/PasswordVault/; SameSite=Strict; secure; HttpOnly Set-Cookie: CA11111=00000002531296421D1226B831C822BA3BEE6FD4245F97F9A49C4C416052B0EC975B1B0C00000000; path=/PasswordVault/; SameSite=Strict; secure; HttpOnly Set-Cookie: CA22222=A1AC061D681C256A9DDF259B64D55E4839F5ED0032A7D0A7863EB07336F49030; path=/PasswordVault/; SameSite=Strict; secure; HttpOnly Set-Cookie: CA55555=cyberark; path=/PasswordVault/; SameSite=Strict; secure Set-Cookie: CA66666=jjxH6-chSXEEGbEjXXl7gyZv8xtT1XfiWqaUz7FPTVqntHw0AfdtPowY5YM0TJv5RHhFJPgoN1Oly2AJzxicXX5RroibSQeh1b4Ua_PTbA3L4fjEVTin3TXQ0bK9PU-VO6koC5iPZ0tOehb8AijWe0zJKaPJ_2hbqIBjgxVsitpxv3VBgXxEFqYQ9If8sE4o2wYS00mu0gVjRZKS9KSVLrbZPDVve0PgNT2alYsAv8Ic1O3mfqkgEYuAuJMndKMxGmE-7ehbwZX373XionLWaq3Viz67yk6UUH8qYCKhf2gpSnkh5PO-u9_e2M5O8uYYEVpTcA4O50Q0IAeU_V4zsg2; path=/PasswordVault/; SameSite=None; secure Set-Cookie: 6a5a355a-0547-40ce-9770-fc22d1f3bbea=8096DBD1E9E9ECF050757DDD2538169332D568558048455B5EC4A9CCB22A74F285F74A13FFF1DAC916C7558EBB15FD0F5EE388C0200435FA4822BD64B5833B0F824A23313EDBDDF519B5170AC7F177FF8D85DF020BEDDD01767EE977A710D5DB3DD6FE3D8A7C0D26442CE3EA472FD456FE69930D39769576D155C488AB79BB08818D36C8253800517365B75AF827BBF6; path=/PasswordVault/; secure; HttpOnly; SameSite=Lax My iRule: when CLIENT_ACCEPTED { set status 0 ACCESS::restrict_irule_events disable } when ACCESS_SESSION_STARTED { set status 1 } when HTTP_RESPONSE_RELEASE { if { $status == 1 } { HTTP::header insert "Set-Cookie" "CA11111=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "CA22222=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "CA33333=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "CA44444=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "CA55555=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "CA66666=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "6a5a355a-0547-40ce-9770-fc22d1f3bbea=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" HTTP::header insert "Set-Cookie" "pam_persist=deleted;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/" } My questions are: Is there any specific requirement to delete a cookie or can i use above iRule to erase all of them? Should i specify all those attributes along with the cookies while deleting? Some of the above cookies send from server when a specific request made by client. While deleting them is there any specific rule/policy to follow? Like deleting the cookie at the request sent by client. I tried to use "HTTP::cookie remove" method but somehow i did not see any delete (Set-Cookie header) message coming from F5 for cookies. How "HTTP::cookie remove" method actually deletes a cookie? This is for the APM specialists. In a LTM+APM policy, is there any way to determine the moment of the session expiration happend and initiate a HTTP response for cookie clean message to the client? Thanks advance.Solved1.7KViews0likes4CommentsSAML Cookie Persistence after browser/system restart and across service providers
I am fairly new to the F5 world and in the beginning of setting up our LTM's as SAML IdP's for a variety of services. Our first use-case is Jive, which we have working and all the attributes are pulling across just fine, authentication is fine, everything is functional as is. I'm having a hard time translating what we want the user experience to be into the next phase of the configuration. Our hope was that we could authenticate a user to the LTM, they would be provided a cookie that was set to expire in 24 hours, that cookie would provide SSO access to other services that we'll be adding, and once the 24 hours is up the user would be asked to authenticate again regardless of which service they are logging in to. I've set the Maximum Session Timeout to 86400 seconds (24 hours) and set the cookie to persistent, but when I log in with a test account I don't see a new cookie created on the user system and closing the browser loses the session. In addition, I don't have another sandbox service provider to test with currently to ensure that the cookie we are hoping will be creating would be valid for that other service as well. Am I wrong in thinking that the F5 can provide a persistent cookie that survives beyond browser or systems restarts? Can the F5 only provide SSO for that time period and across SAML partners as long as that browser session is open? I presume I'm asking some pretty elementary stuff so forgive my lack of current knowledge. Any pointers on where I can read up on that or help managing my expectations would be appreciated.1.6KViews0likes17CommentsBIG-IP 17.0 ASM Cookie based allow requests
Is it possible to allow requests through the ASM if the client sending the request has a unique cookie with a particular value? I want to whitelist these requests based on this cookie. If this is possible would someone please share with me how this is accomplished?Solved1.2KViews0likes1CommentHow to add Httponly and Secure attributes to HTTP cookies (for 11.5.x)
Problem this snippet solves: Problem this snippet solves: The script adds Httponly and Secure attributes to cookies issued by the server. In v12.x software there is a better way to achieve the same outcome with using HTTP::cookie commands (even though adding Httponly requires additional tweaks because of the issue with cookie version field see discussion here). However, in v11.5 and earlier releases HTTP::cookie commands do not work as expected (in particular, upper case chracters cookie attributes e.g. "Expire" and "GMT" are parsed with errors, as discussed in 19-Oct-2013 post by DanW here). Furthermore, in these software versions F5 Persistent Cookies do not have "Httponly" attributes and adding them using HTTP::cookie command appears to be impossible (as "HTTP::cookie version" command cannot be used for F5-generated cookies). Note: the HTTP::cookie commands repairs non-RFC-compliant attributes "httponly=<any text>" and "secure=<any text>" by replacing them with "Httponly" and "Secure" respectively. The script below does not perform such replacements and leaves these non-RFC-compliant attributes unmodified (without adding duplicates of the attributes). We consider fixing non-RFC-compliant syntax to be out of the scope. Browsers we tested ignored the <any text> values (in "httponly=<any text>" and "secure=<any text>" attributes). How to use this snippet: How to use this snippet: The same instance of this iRule can be applied to a mixture of HTTP or HTTPS Virtual Servers and will automatically disable insertion of “Secure” attribute for the HTTP VSs. Tested on Versions: 11.5.2 HF1; 11.5.4 HF2; 12.1.1 HF1 Code : when CLIENT_ACCEPTED { set httpsVs [PROFILE::exists clientssl] # to determine whether the connection is via an HTTP or HTTPS VS # it can be done with [SSL::cert count] and catch in HTTP_RESPONSE # event, but there would have been a bigger perfomance impact } when HTTP_RESPONSE { set setckval [HTTP::header values "Set-Cookie"] HTTP::header remove "Set-Cookie" # this command removes all cookies from the responce, so we have to remove and then # re-insert in any case (rather then do it selectively on an as-needed per-cookie basis) foreach cookie1 $setckval { set cookie1 [string trimright [string trimright $cookie1] ";"] # to avoid obtaining multiple semicolons (e.g. ";;Secure" or "; ;Secure") in the output # if the orginal cookie had a trailing semicolon, possibly, followed by spaces # trailing semicolons, strictly saying, do not meet rules described in RFC 2965 # but there are reports of applications using them (e.g. Cisco Bug: CSCso95114) set list1 [lrange [split $cookie1 ";"] 1 end] # the first field (cookie name=value pair) is skipped to ensure that even cookies # that happen to have names "httponly" or "secure" are processed properly set hasHttpOnly false if { $httpsVs } { set hasSecure false} else { set hasSecure true } #i.e. insertion of "Secure" is disabled for non-HTTPS VSs foreach item1 $list1 { set titem1 [string tolower [string trim $item1]] # accordring to RFC 2965 leading and trailing WSP characters in atrributes # should be ignored and attributes should be treated as case-insensitive; if { ($titem1 eq "httponly") or ($titem1 starts_with "httponly=") } { set hasHttpOnly true } if { ($titem1 eq "secure") or ($titem1 starts_with "secure=")} { set hasSecure true } } if { not $hasHttpOnly } { set cookie1 "${cookie1}; Httponly" #log local0. "Missing Httponly attribute is being added to cookie: $cookie1" } if { not $hasSecure } { set cookie1 "${cookie1}; Secure" #log local0. "Missing Secure attribute is being added to cookie: $cookie1" } #log local0. "The following cookie is being rewritten: $cookie1" HTTP::header insert "Set-Cookie" $cookie1 } }1.1KViews0likes8CommentsCookie Encryption - duplicate cookies
TL;DR has anyone written code to deduplicate cookies before the cookie encryption feature is used? We had a ticket C1649037 to do with the Cookie Encryption breaking if the cookie was duplicated in the server's response headers. e.g. Server says: set-cookie A hello set-cookie A hello F5 told to encrypt A says: set-cookie encrypted gfddsgde34fwqf34 set-cooke A hello e.g. F5's Cookie encryption was only encrypting one copy of a set-cookie header in the server's response. Its a minor edge case, although for the vendor we spotted it with duplicate the session cookie in response to a successful login request so the session cookie doesn’t get encrypted. Also leaking the plain text of some cookies may make cryptanalysis even easier ;) Since the ticket was raised, we mentioned it to PortSwigger Web Security, who write BurpSuite security testing tool, and they added a duplicate cookie test (they are really good with these kinds of requests). Since then it has become clear: 1) the vendor is not going to remove the duplicate cookies any time soon. 2) duplicated cookie headers are very common in web applications, although usually it is deletion where it goes wrong as the session headers are written and then the deletion. 3) HTTP/2 header compression makes it all the more complicated to diagnose as you need to ensure you have the same protocol version as the browser seeing the issue, since HTTP/2 effectively dedups cookies for you. As such I’m minded to finally address the issue by fixing the F5's behaviour. Presumably de-duplicating cookie headers before encryption. Has anyone written this already? Should be easy, but I vaguely recall at the time that the cookies were presented as an associative array. The “best” solution would be for a supported change to the F5 Cooke Encryption feature, that always prefers later cookies headers with the same name when encrypting a request from the server to the client, as this is the RFC compliance resolution that the browser should do. Cookie encryption is notoriously tricky to do right and generally shouldn't be relied on. Here we are treating it as an additional measure to discourage poking at cookies, and to avoid the issue with sensitive data in the cookies being stored in the browser for long duration. Rather than trying to obscure a known cookie parsing weakness (down this road lies madness and being hacked).Solved1.1KViews0likes2CommentsiRule needed to clear specific cookies from particular domain
Recently we moved our peoplesoft system to a subdomain of our DNS space. So instead of all our VIPs being for example, prod.abc.com they are now prod.ps.abc.com Peoplesoft uses a cookie for single-signon and some other features. The primary cookie is PS_TOKEN, but there are others as well. This move, for the most part, was seamless. However we have a particular case generally involving Safari on mac where the browser can submit the old domain token (cookie), which will sometimes cause the browser to "loop" the guest authentication page hundreds, or thousands of times a minute. We have demonstrated that clearing the old domain cookies will solve the issue. We have demonstrated this using a static webpage with some javascript that is hosted on an address on the old domain (abc.com). If a browser with the "old" cookie visits our page in between, then it is cleared and works. This is a rather manual solution, and redirecting everyone there before logging in would seem to be one solution - but that will not work as we have some deep links that would be broken in that case. What I desire (and have tried to create a few ways) is an iRule to: Check for the existence of the cookie PS_TOKEN (and possibly others) Check that the cookie domain of that cookie(s) is .abc.com If so, delete it (or if necessary set it's expiration to -1, which is what our js had to do) Then pass the request on through to wherever it was headed to start with. Ideally using the pool already defined for the particular virtual server. I haven't been able to get even the basics to seem to work. So I dropped back to seeing if the cookie is even being read by the F5, so here is where I sit now: when HTTP_REQUEST { if {[HTTP::cookie domain "PS_TOKEN"] contains ".abc.com"} { HTTP::respond 200 content {found abccom cookie} } else {HTTP::respond 200 content {did not find cookie} } } This never finds the cookie (at least it doesn't tell me it did). Any help and direction is most appreciated.1KViews0likes8Comments