Increased Security With First Party Cookies
We had a similar experience as Mr. Olson. We had a modified (for our specific needs) version of the original iRule (with the ^'replace' fix added, per the comments) which appeared to be working but was ultimately breaking persistence to the pooled webservers (tomcat).
Additionally, we need to only add SameSite set-cookie header to tomcat (JSESSIONID) and F5 (BIGipServer~) session cookies, when it is missing.
Lastly, we have to check against a vendor supplied blacklist of clients/user-agents that do not honor or do not correctly interpret the SameSite attribute. Of note, not all browsers who do honor SameSite interpret it the same way, in the case of multiple SameSite headers, Edge will use the most strict value, while Chrome uses the last occurring value in the set-cookie string. Also of note, a recent Windows patch added SameSite=Lax to IIS requests, so keep that in mind if IIS/ASP is in play.
Here is our working iRule for the above scenario:
when HTTP_REQUEST {
# assume client request is samesite compatible, set isCompatClient True
set isCompatClient 1
# check user-agent against vendor provided blacklist of incompatible user-agents
if { ([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*cpu os 1[0-2].*version\/1[0-2].*safari.*$}) ||
([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*cpu iphone os 1[0-2].*version\/1[0-2].*safari.*$}) ||
([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*cpu os 12.*like mac os x.*$}) ||
([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*cpu iphone os 12.*$}) ||
([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*macintosh; intel mac os x 10_1[0-4].*version\/1[0-3].*safari.*$}) ||
([string tolower [HTTP::header "User-Agent"]] matches_regex {^.*chrom[^ \/]+\/(5[1-9]|6[0-6])[\.\d]*.*$}) } {
set isCompatClient 0
}
}
when HTTP_RESPONSE {
# SameSite value: None, Lax, Strict
set samesiteVal "None"
# variable definitions
set num_cookie_headers 0
set new_cookie_headers ""
# only modify cookies if browser is compatible with SameSite, otherwise do nothing
if { ($isCompatClient) } {
# check if set-cookie header exists. otherwise nothing to do
if { [HTTP::header exists "Set-Cookie"] } {
# set-cookie header can occur multiple times, get a count and treat them as list
set num_cookie_headers [HTTP::header count Set-Cookie]
# iterate through each Set-Cookie header and evaluate
foreach aCookieHeader [HTTP::header values Set-Cookie] {
# only consider session cookies
if { $aCookieHeader starts_with "JSESSIONID" or $aCookieHeader starts_with "BIGipServer" } {
# only consider cookie values not containing the string "samesite"
if { !([string tolower $aCookieHeader] contains "samesite") } {
# append samesite to original value
set aCookieHeader [concat $aCookieHeader "; SameSite=$samesiteVal"]
}
}
# collect modified values in list new_cookie_headers
lappend new_cookie_headers $aCookieHeader
}
# if there is only one cookie
if { $num_cookie_headers == 1 } {
# overwrite the single existing Set-Cookie header because there is only one in list
HTTP::header replace Set-Cookie [lindex $new_cookie_headers 0]
# else there is more than 1 cookie
} else {
# remove all Set-Cookie headers
HTTP::header remove Set-Cookie
# iterate through each Set-Cookie header in our new list
foreach aCookieHeader $new_cookie_headers {
# Re-create a Set-Cookie header that we just removed
HTTP::header insert Set-Cookie $aCookieHeader
}
}
}
}
}