Last active
March 18, 2020 17:01
-
-
Save bnorton/b035f89b31a54d65a10caed12da2ede8 to your computer and use it in GitHub Desktop.
SameSite=None user agent sniffing for incompatible browsers (Ruby)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApplicationController < ActionController::Base | |
... | |
# Use when setting 3rd party cookies and make sure to tack .compact on the | |
# end to make sure that the :same_site key is not included when the value is missing | |
# | |
def set_cookie(key, value) | |
cookies.encrypted[key] = { | |
:value => value.to_s, | |
:expires => 1.year, :domain => :all, | |
:same_site => SameSite.value(request.headers['User-Agent']), | |
:secure => Rails.env.production? | |
}.compact | |
end | |
... | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment