Skip to content
Snippets Groups Projects
Commit 048b0a7a authored by Tim van Dijen's avatar Tim van Dijen
Browse files
parents a94ef4e5 d65f8671
No related branches found
No related tags found
No related merge requests found
...@@ -564,7 +564,7 @@ $config = [ ...@@ -564,7 +564,7 @@ $config = [
* Example: * Example:
* 'session.cookie.samesite' => 'None', * 'session.cookie.samesite' => 'None',
*/ */
'session.cookie.samesite' => null, 'session.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null,
/* /*
* Options to override the default settings for php sessions. * Options to override the default settings for php sessions.
...@@ -792,7 +792,7 @@ $config = [ ...@@ -792,7 +792,7 @@ $config = [
'language.cookie.secure' => false, 'language.cookie.secure' => false,
'language.cookie.httponly' => false, 'language.cookie.httponly' => false,
'language.cookie.lifetime' => (60 * 60 * 24 * 900), 'language.cookie.lifetime' => (60 * 60 * 24 * 900),
'language.cookie.samesite' => null, 'language.cookie.samesite' => \SimpleSAML\Utils\HTTP::canSetSameSiteNone() ? 'None' : null,
/** /**
* Custom getLanguage function called from SimpleSAML\Locale\Language::getLanguage(). * Custom getLanguage function called from SimpleSAML\Locale\Language::getLanguage().
......
...@@ -18,6 +18,51 @@ use SimpleSAML\XHTML\Template; ...@@ -18,6 +18,51 @@ use SimpleSAML\XHTML\Template;
*/ */
class HTTP class HTTP
{ {
/**
* Determine if the user agent can support cookies being sent with SameSite equal to "None".
* Browsers with out support may drop the cookie and or treat is a stricter setting
* Browsers with support may have additional requirements on setting it on non-secure websites.
*
* Based on the Azure teams experience rolling out support and Chromium's advice
* https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
* https://www.chromium.org/updates/same-site/incompatible-clients
* @return bool true if user agent supports a None value for SameSite.
*/
public static function canSetSameSiteNone(): bool
{
$useragent = $_SERVER['HTTP_USER_AGENT'] ?? null;
if (!$useragent) {
return true;
}
// All iOS 12 based browsers have no support
if (strpos($useragent, "CPU iPhone OS 12") !== false || strpos($useragent, "iPad; CPU OS 12") !== false) {
return false;
}
// Safari Mac OS X 10.14 has no support
// - Safari on Mac OS X.
if (strpos($useragent, "Macintosh; Intel Mac OS X 10_14") !== false) {
// regular safari
if (strpos($useragent, "Version/") !== false && strpos($useragent, "Safari") !== false) {
return false;
} elseif (preg_match('|AppleWebKit/[\.\d]+ \(KHTML, like Gecko\)$|', $useragent)) {
return false;
}
}
// Chrome based UCBrowser may have support (>= 12.13.2) even though its chrome version is old
$matches = [];
if (preg_match('|UCBrowser/(\d+\.\d+\.\d+)[\.\d]*|', $useragent, $matches)) {
return version_compare($matches[1], '12.13.2', '>=');
}
// Chrome 50-69 may have broken SameSite=None and don't require it to be set
if (strpos($useragent, "Chrome/5") !== false || strpos($useragent, "Chrome/6") !== false) {
return false;
}
return true;
}
/** /**
* Obtain a URL where we can redirect to securely post a form with the given data to a specific destination. * Obtain a URL where we can redirect to securely post a form with the given data to a specific destination.
* *
......
...@@ -36,12 +36,14 @@ authentication source: ...@@ -36,12 +36,14 @@ authentication source:
'es' => 'Entrar usando un SP SAML', 'es' => 'Entrar usando un SP SAML',
), ),
'css-class' => 'SAML', 'css-class' => 'SAML',
'AuthnContextClassRef' => array('urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI', 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'),
), ),
'example-admin' => array( 'example-admin' => array(
'text' => array( 'text' => array(
'en' => 'Log in using the admin password', 'en' => 'Log in using the admin password',
'es' => 'Entrar usando la contraseña de administrador', 'es' => 'Entrar usando la contraseña de administrador',
), ),
'AuthnContextClassRef' => 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
), ),
), ),
), ),
...@@ -77,7 +79,7 @@ compatible fashion so both cases should work. ...@@ -77,7 +79,7 @@ compatible fashion so both cases should work.
Each source in the sources array has a key and a value. As Each source in the sources array has a key and a value. As
mentioned above the key is the authsource identifier and the value mentioned above the key is the authsource identifier and the value
is another array with two optional keys: 'text' and 'css-class'. is another array with optional keys: 'text', 'css-class', 'help', and 'AuthnContextClassRef'.
The text element is another array with localized strings for one The text element is another array with localized strings for one
or more languages. These texts will be shown in the selectsource.php or more languages. These texts will be shown in the selectsource.php
view. Note that you should at least enter the text in the default view. Note that you should at least enter the text in the default
...@@ -87,7 +89,14 @@ the <li> element in the selectsource.php view. By default the ...@@ -87,7 +89,14 @@ the <li> element in the selectsource.php view. By default the
authtype of the authsource is used as the css class with colons authtype of the authsource is used as the css class with colons
replaced by dashes. So in the previous example, the css class used replaced by dashes. So in the previous example, the css class used
in the 'example-admin' authentication source would be in the 'example-admin' authentication source would be
'core-AdminPassword'. 'core-AdminPassword'. The help element is another array with localized
strings for one or more languages. These texts will be shown in the
selectsource.php view. The AuthnContextClassRef is either a string or
an array of strings containing [context class ref names](https://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf).
If an SP sets AuthnContextClassRef the list of authsources will be
filtered to only those containing context class refs that are part of the list set by the SP.
If a single authsource results from this filtering the user will be taken directly to the
authentication page for that source, and will never be shown the multiauth select page.
It is possible to add the parameter `source` to the calling URL, It is possible to add the parameter `source` to the calling URL,
when accessing a service, to allow the user to preselect the when accessing a service, to allow the user to preselect the
......
...@@ -4,6 +4,7 @@ declare(strict_types=1); ...@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SimpleSAML\Module\multiauth\Auth\Source; namespace SimpleSAML\Module\multiauth\Auth\Source;
use SAML2\Constants;
use Exception; use Exception;
use SimpleSAML\Assert\Assert; use SimpleSAML\Assert\Assert;
use SimpleSAML\Auth; use SimpleSAML\Auth;
...@@ -12,6 +13,7 @@ use SimpleSAML\Error; ...@@ -12,6 +13,7 @@ use SimpleSAML\Error;
use SimpleSAML\Module; use SimpleSAML\Module;
use SimpleSAML\Session; use SimpleSAML\Session;
use SimpleSAML\Utils; use SimpleSAML\Utils;
use SimpleSAML\Module\saml\Error\NoAuthnContext;
/** /**
* Authentication source which let the user chooses among a list of * Authentication source which let the user chooses among a list of
...@@ -113,11 +115,22 @@ class MultiAuth extends Auth\Source ...@@ -113,11 +115,22 @@ class MultiAuth extends Auth\Source
} }
} }
$class_ref = [];
if (array_key_exists('AuthnContextClassRef', $info)) {
$ref = $info['AuthnContextClassRef'];
if (is_string($ref)) {
$class_ref = [$ref];
} else {
$class_ref = $ref;
}
}
$this->sources[] = [ $this->sources[] = [
'source' => $source, 'source' => $source,
'text' => $text, 'text' => $text,
'help' => $help, 'help' => $help,
'css_class' => $css_class, 'css_class' => $css_class,
'AuthnContextClassRef' => $class_ref,
]; ];
} }
} }
...@@ -144,6 +157,30 @@ class MultiAuth extends Auth\Source ...@@ -144,6 +157,30 @@ class MultiAuth extends Auth\Source
$state['multiauth:preselect'] = $this->preselect; $state['multiauth:preselect'] = $this->preselect;
} }
if (
!is_null($state['saml:RequestedAuthnContext'])
&& array_key_exists('AuthnContextClassRef', $state['saml:RequestedAuthnContext'])
) {
$refs = array_values($state['saml:RequestedAuthnContext']['AuthnContextClassRef']);
$new_sources = [];
foreach ($this->sources as $source) {
if (count(array_intersect($source['AuthnContextClassRef'], $refs)) >= 1) {
$new_sources[] = $source;
}
}
$state[self::SOURCESID] = $new_sources;
$number_of_sources = count($new_sources);
if ($number_of_sources === 0) {
throw new NoAuthnContext(
Constants::STATUS_RESPONDER,
'No authentication sources exist for the requested AuthnContextClassRefs: ' . implode(', ', $refs)
);
} else if ($number_of_sources === 1) {
MultiAuth::delegateAuthentication($new_sources[0]['source'], $state);
}
}
// Save the $state array, so that we can restore if after a redirect // Save the $state array, so that we can restore if after a redirect
$id = Auth\State::saveState($state, self::STAGEID); $id = Auth\State::saveState($state, self::STAGEID);
......
...@@ -526,4 +526,57 @@ class HTTPTest extends ClearStateTestCase ...@@ -526,4 +526,57 @@ class HTTPTest extends ClearStateTestCase
$this->assertMatchesRegularExpression('/\b[Ss]ame[Ss]ite=Lax(;|$)/', $headers[2]); $this->assertMatchesRegularExpression('/\b[Ss]ame[Ss]ite=Lax(;|$)/', $headers[2]);
$this->assertMatchesRegularExpression('/\b[Ss]ame[Ss]ite=Strict(;|$)/', $headers[3]); $this->assertMatchesRegularExpression('/\b[Ss]ame[Ss]ite=Strict(;|$)/', $headers[3]);
} }
/**
* Test detecting if user agent supports None
* @dataProvider detectSameSiteProvider
* @param null|string $userAgent The user agent. Null means not set, like with CLI
* @param bool $supportsNone None can be set as a SameSite flag
*/
public function testDetectSameSiteNoneBehavior(?string $userAgent, bool $supportsNone): void
{
if ($userAgent) {
$_SERVER['HTTP_USER_AGENT'] = $userAgent;
}
$this->assertEquals($supportsNone, HTTP::canSetSameSiteNone(), $userAgent ?? 'No user agent set');
}
public function detectSameSiteProvider(): array
{
// @codingStandardsIgnoreStart
return [
[null, true],
['some-new-browser', true],
//Browsers that can handle 'None'
// Chrome
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36', true],
// Chome on windows
['Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', true],
// Chrome linux
['Mozilla/5.0 (X11; HasCodingOs 1.0; Linux x64) AppleWebKit/637.36 (KHTML, like Gecko) Chrome/70.0.3112.101 Safari/637.36 HasBrowser/5.0', true],
// Safari iOS 13
['Mozilla/5.0 (iPhone; CPU iPhone OS 13_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.2 Mobile/15E148 Safari/604.1', true],
// Mac OS X with support
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15', true],
// UC Browser with support
['Mozilla/5.0 (Linux; U; Android 9; en-US; SM-A705FN Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.13.2.1208 Mobile Safari/537.36', true],
['Mozilla/5.0 (Linux; U; Android 10; en-US; RMX2020 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.13.5.1209 Mobile Safari/537.36', true],
// Embedded Mac with support
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko)', true],
// Browser without support
// Old Safari on mac
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15', false],
// Old Safari on iOS 12 (phone and ipad
['Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1', false],
['Mozilla/5.0 (iPad; CPU OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/16A5288q Safari/605.1.15', false],
// Chromium without support
['Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/65.0.3325.181 Chrome/65.0.3325.181 Safari/537.36', false],
// UC Browser without support
['Mozilla/5.0 (Linux; U; Android 8.1.0; zh-CN; EML-AL00 Build/HUAWEIEML-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 baidu.sogo.uc.UCBrowser/11.9.4.974 UWS/2.13.1.48 Mobile Safari/537.36 AliApp(DingTalk/4.5.11) com.alibaba.android.rimet/10487439 Channel/227200 language/zh-CN', false],
['Mozilla/5.0 (Linux; U; Android 7.1.1; en-US; CPH1723 Build/N6F26Q) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.13.0.1207 Mobile Safari/537.36', false],
// old embedded browser
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko)', false]
];
// @codingStandardsIgnoreEnd
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment