Skip to content

Commit

Permalink
Cater for SameSite=None incompatible browsers
Browse files Browse the repository at this point in the history
Not all browsers are compatible with SameSite=None. This will handle those that are not compatible and leave the parameter blank.
  • Loading branch information
ignetic committed Aug 18, 2020
1 parent 8daab4a commit 0e49d28
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 22 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ With recent changes to Google Chrome, cookies are now defaulted to SameSite=Lax.

This addon can resolve issues where offsite cookies are required, such as offsite payment gateways and with 3D Secure.

Note that not all browsers are compatible with SameSite=None. This addon will handle those that are not compatible and leave the parameter blank.
https://www.chromium.org/updates/same-site/incompatible-clients

3 changes: 3 additions & 0 deletions samesite_cookies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ With recent changes to Google Chrome, cookies are now defaulted to SameSite=Lax.

This addon can resolve issues where offsite cookies are required, such as offsite payment gateways and with 3D Secure.

Note that not all browsers are compatible with SameSite=None. This addon will handle those that are not compatible and leave the parameter blank.
https://www.chromium.org/updates/same-site/incompatible-clients

2 changes: 1 addition & 1 deletion samesite_cookies/addon.setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'author_url' => 'https://github.com/ignetic',
'name' => 'SameSite Cookies',
'description' => 'Add SameSite attribute to ExpressionEngine cookies',
'version' => '1.1',
'version' => '1.2',
'namespace' => '\\',
'settings_exist' => TRUE,
);
58 changes: 37 additions & 21 deletions samesite_cookies/ext.samesite_cookies.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

require_once 'vendor/uvii/samesitenone/src/SameSiteNone.php';

use Uvii\SameSiteNone;

class Samesite_cookies_ext
{

public $settings = array();
public $name = 'SameSite Cookies';
public $version = '1.1';
public $version = '1.2';
public $description = 'Add SameSite attribute to ExpressionEngine cookies';
public $settings_exist = 'y';
public $docs_url = '';
Expand Down Expand Up @@ -76,30 +80,42 @@ function settings()
public function set_cookie_end($data)
{
$return = FALSE;
$cookies = array();

// Not all browsers are compatible with SameSite=None
// https://www.chromium.org/updates/same-site/incompatible-clients
$userAgent = ee()->session->userdata('user_agent');
$SameSiteNoneSafe = SameSiteNone::isSafe($userAgent);

if (isset($this->settings['cookies']) && ! empty($this->settings['cookies']))
{
$cookies = explode("\n", str_replace(",", "\n", trim($this->settings['cookies'])));
$cookies = array_map('trim', $cookies);

$cookieName = $data['prefix'].$data['name'];
$data['samesite'] = (isset($this->settings['samesite']) ? $this->settings['samesite'] : '');

if (isset($this->settings['secure_cookies']) && $this->settings['secure_cookies'] === 'yes')
{
$data['secure_cookie'] = 1;
}

if (isset($this->settings['all_cookies']) && $this->settings['all_cookies'] === 'apply_all')
{
$return = $this->set_samesite_cookie($data);
ee()->extensions->end_script = TRUE;
}
else if (in_array($cookieName, $cookies))
{
$return = $this->set_samesite_cookie($data);
ee()->extensions->end_script = TRUE;
}
}

$data['samesite'] = (isset($this->settings['samesite']) ? $this->settings['samesite'] : '');

if ( ! $SameSiteNoneSafe && $data['samesite'] == 'None')
{
$data['samesite'] = '';
}

if (isset($this->settings['secure_cookies']) && $this->settings['secure_cookies'] === 'yes')
{
$data['secure_cookie'] = 1;
}

if (isset($this->settings['all_cookies']) && $this->settings['all_cookies'] === 'apply_all')
{
$return = $this->set_samesite_cookie($data);
ee()->extensions->end_script = TRUE;
}
else if (in_array($data['prefix'].$data['name'], $cookies))
{
$return = $this->set_samesite_cookie($data);
ee()->extensions->end_script = TRUE;
}

return $return;
}

Expand All @@ -110,7 +126,7 @@ private function set_samesite_cookie($data)
// thus the SameSite setting must be hacked in with the path option.
return setcookie($data['prefix'].$data['name'], $data['value'],
$data['expire'],
$data['path'] . '; SameSite=' . $data['samesite'],
$data['path'] . ( ! empty($data['samesite']) ? '; SameSite=' . $data['samesite'] : ''),
$data['domain'],
$data['secure_cookie'],
$data['httponly']
Expand Down
131 changes: 131 additions & 0 deletions samesite_cookies/vendor/uvii/samesitenone/src/SameSiteNone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php
declare(strict_types = 1);

namespace Uvii;

class SameSiteNone
{
public $uaStr = '';

public function __construct(String $uaStr = '') {
$this->uaStr = $uaStr;
}

public static function isSafe(String $useragent): bool
{
return ((new self($useragent))->shouldSendSameSiteNone());
}

function shouldSendSameSiteNone(): bool
{
return !$this->isSameSiteNoneIncompatible();
}

function isSameSiteNoneIncompatible(): bool
{
return $this->hasWebKitSameSiteBug() ||
$this->dropsUnrecognizedSameSiteCookies();
}

function hasWebKitSameSiteBug(): bool
{
return $this->isIosVersion(12) ||
($this->isMacosxVersion(10,14) &&
($this->isSafari() ||
$this->isMacEmbeddedBrowser()
)
);
}

function dropsUnrecognizedSameSiteCookies(): bool
{
if ($this->isUcBrowser()) {
return !$this->isUcBrowserVersionAtLeast(12,13,2);
}
return $this->isChromiumBased() &&
$this->isChromiumVersionAtLeast(51) &&
!$this->isChromiumVersionAtLeast(67);
}

public function isIosVersion(int $major): bool
{

$regex = "/\(iP.+; CPU .*OS (\d+)[_\d]*.*\) AppleWebKit\//";
$ver = 0;
if (preg_match($regex, $this->uaStr, $matches)) {
$ver = intval($matches[1]);
}
return $ver === $major;
}

public function isMacosxVersion(int $major, int $minor): bool
{

$regex = "/\(Macintosh;.*Mac OS X (\d+)_(\d+)[_\d]*.*\) AppleWebKit\//";
$major_version = 0;
$minor_version = 0;
if (preg_match($regex, $this->uaStr, $matches)) {
$major_version = intval($matches[1]);
$minor_version = intval($matches[2]);
}
return ($major_version === $major) &&
($minor_version === $minor);
}

public function isSafari(): bool
{
$regex = "/Version\/.* Safari\//";
return (1 === preg_match($regex, $this->uaStr)) &&
!$this->isChromiumBased();
}

public function isMacEmbeddedBrowser(): bool
{
$regex = "/^Mozilla\/[\.\d]+ \(Macintosh;.*Mac OS X [_\d]+\) AppleWebKit\/[\.\d]+ \(KHTML, like Gecko\)$/";
return (1 === preg_match($regex, $this->uaStr));
}

public function isChromiumBased(): bool
{
$regex = "/Chrom(e|ium)/";
return (1 === preg_match($regex, $this->uaStr));
}

public function isChromiumVersionAtLeast(int $major): bool
{
$regex = "/Chrom[^ \/]+\/(\d+)[\.\d]*/";
$ver = 0;
if (preg_match($regex, $this->uaStr, $matches)) {
$ver = intval($matches[1]);
}
return $ver >= $major;
}

public function isUcBrowser(): bool
{
$regex = "/UCBrowser/";
return (1 === preg_match($regex, $this->uaStr)) ? true: false;
}

public function isUcBrowserVersionAtLeast(int $major, int $minor, int $build): bool
{
$regex = "/UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* /";
$major_version = 0;
$minor_version = 0;
$build_version = 0;
if (preg_match($regex, $this->uaStr, $matches)) {
$major_version = intval($matches[1]);
$minor_version = intval($matches[2]);
$build_version = intval($matches[3]);
}
if ($major_version != $major) {
return $major_version > $major;
}
if ($minor_version != $minor) {
return $minor_version > $minor;
}
return $build_version >= $build;
}
}


0 comments on commit 0e49d28

Please sign in to comment.