Skip to content

Instantly share code, notes, and snippets.

@bnorton
Last active April 5, 2021 21:18
Show Gist options
  • Save bnorton/7dee72023787f367c48b3f5c2d71540f to your computer and use it in GitHub Desktop.
Save bnorton/7dee72023787f367c48b3f5c2d71540f to your computer and use it in GitHub Desktop.

To make SameSite=None cookies work in a rails application (or any rack-based ruby app) you need to also FILTER OUT any clients that don't work nicely with this new flag, namely clients who mistreat the None value or drop the cookie when incompatible values are seen.

class ApplicationControler < ActionController::Base
...
def set_third_party_cookie(name, value, expires_in: 1.year)
cookies.encrypted[name] = { :value => value.to_s, :expires_in => expires_in, :domain => :all, :same_site => SameSite.value(request.headers['User-Agent']), :secure => true, :httponly => true }.compact
end
...
end
...
##
# Until this commit is merged and released by rack
#
gem 'rack', git: 'https://github.com/rack/rack.git', ref: 'c859bbf7b53cb59df1837612a8c330dfb4147392'
...
class SameSite
def self.value(agent)
send_same_site_none?(agent) ? :none : nil
end
private
##
# Based on https://www.chromium.org/updates/same-site/incompatible-clients
#
# Don’t send `SameSite=None` to known incompatible clients.
#
def self.send_same_site_none?(useragent)
return true if useragent.blank?
!same_site_none_incompatible?(useragent)
end
# Classes of browsers known to be incompatible.
#
def self.same_site_none_incompatible?(useragent)
web_kit_same_site_bug?(useragent) ||
drops_unrecognized_cookies?(useragent)
end
def self.web_kit_same_site_bug?(useragent)
ios_version?(useragent, major: 12) ||
(macosx_version?(useragent, major: 10, minor: 14) &&
(safari?(useragent) || mac_embedded_browser?(useragent)))
end
def self.drops_unrecognized_cookies?(useragent)
return !uc_browser_version_at_least?(useragent, major: 12, minor: 13, build: 2) if uc_browser?(useragent)
chromium_based?(useragent) &&
chromium_version_at_least?(useragent, major: 51) &&
!chromium_version_at_least?(useragent, major: 67)
end
def self.ios_version?(useragent, major: nil)
/\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\// =~ useragent
$1 == major.to_s
end
def self.macosx_version?(useragent, major:nil, minor:nil)
/\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\// =~ useragent
$1 == major.to_s && $2 == minor.to_s
end
def self.safari?(useragent)
/Version\/.* Safari\// === useragent && !chromium_based?(useragent)
end
def self.mac_embedded_browser?(useragent)
/^(Mozilla\/[.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) )|AppleWebKit\/[.\d]+ \(KHTML, like Gecko\)$/ === useragent
end
def self.chromium_based?(useragent)
/Chrom(e|ium)/ === useragent
end
def self.chromium_version_at_least?(useragent, major: nil)
/Chrom[^ \/]+\/(\d+)[.\d]* / =~ useragent
$1.to_i >= major
end
def self.uc_browser?(useragent)
/UCBrowser\// =~ useragent
end
def self.uc_browser_version_at_least?(useragent, major: nil, minor: nil, build: nil)
/UCBrowser\/(\d+)\.(\d+)\.(\d+)[.\d]* / =~ useragent
major_version = $1.to_i
minor_version = $2.to_i
if major_version != major
return major_version > major
elsif minor_version != minor
return minor_version > minor
end
$3.to_i >= build
end
end
@brianmcgue
Copy link

In your mac_embedded_browser? method there's a pipe character (|) that should be a space ( ) based on the original incompatible clients code.

/^(Mozilla\/[.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) ) AppleWebKit\/[.\d]+ \(KHTML, like Gecko\)$/ === useragent

@flugsio
Copy link

flugsio commented Oct 19, 2020

I agree, the pipe should not be there. But there should also only be 1 space between the fist part and the second, so one of the spaces needs to be removed as well. I've tested with the 547 most common browser from this site and tried to manually verify all the incompatible cases.

I use this version (first parenthesis are not required, but makes it clearer)

/^(Mozilla\/[.\d]+ \(Macintosh;.*Mac OS X [_\d]+\)) AppleWebKit\/[.\d]+ \(KHTML, like Gecko\)$/ === useragent

These should all match, they End with (KHTML, like Gecko)

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1273795-webkit-based-browser-macos-webkit

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1253253-webkit-based-browser-macos-webkit

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko)"
https://developers.whatismybrowser.com/useragents/parse/1095784-webkit-based-browser-macos-webkit

This should not match, there's additional stuff at the end

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
https://developers.whatismybrowser.com/useragents/parse/1302439-chrome-macos-blink"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment