Skip to content

Instantly share code, notes, and snippets.

@bnorton
Last active March 18, 2020 17:01
Show Gist options
  • Save bnorton/b035f89b31a54d65a10caed12da2ede8 to your computer and use it in GitHub Desktop.
Save bnorton/b035f89b31a54d65a10caed12da2ede8 to your computer and use it in GitHub Desktop.
SameSite=None user agent sniffing for incompatible browsers (Ruby)
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
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