Skip to content

Commit 7912450

Browse files
committed
Add IssueInstant check to responses
1 parent 5a4d237 commit 7912450

File tree

5 files changed

+112
-2
lines changed

5 files changed

+112
-2
lines changed

lib/Saml2/Error.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class OneLogin_Saml2_ValidationError extends Exception
100100
const NOT_SUPPORTED = 46;
101101
const KEY_ALGORITHM_ERROR = 47;
102102
const MISSING_ENCRYPTED_ELEMENT = 48;
103+
const INVALID_ISSUEINSTANT = 49;
103104

104105

105106
/**

lib/Saml2/LogoutResponse.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public function getStatus()
116116
*
117117
* @return bool Returns if the SAML LogoutResponse is or not valid
118118
*/
119-
public function isValid($requestId = null, $retrieveParametersFromServer = false)
119+
public function isValid($requestId = null, $retrieveParametersFromServer = false, $requestIssueInstant = null)
120120
{
121121
$this->_error = null;
122122
try {
@@ -147,6 +147,19 @@ public function isValid($requestId = null, $retrieveParametersFromServer = false
147147
}
148148
}
149149

150+
// Check if the IssueInstant of the Logout Response is later than the one of the Logout Request if provided (considering a configured clock skew)
151+
if (isset($requestIssueInstant)) {
152+
$issueInstant = $this->document->documentElement->getAttribute('IssueInstant');
153+
$issueInstantTime = OneLogin_Saml2_Utils::parseSAML2Time($issueInstant);
154+
$requestIssueInstantTime = OneLogin_Saml2_Utils::parseSAML2Time($requestIssueInstant);
155+
if ($requestIssueInstantTime > $issueInstantTime + $security['clockSkewTolerance']) {
156+
throw new OneLogin_Saml2_ValidationError(
157+
"The IssueInstant of the Logout Response: $issueInstant is not equal or later than the IssueInstant of the Logout Request: $requestIssueInstant (considering the configured clock skew)",
158+
OneLogin_Saml2_ValidationError::INVALID_ISSUEINSTANT
159+
);
160+
}
161+
}
162+
150163
// Check issuer
151164
$issuer = $this->getIssuer();
152165
if (!empty($issuer) && $issuer != $idPEntityId) {

lib/Saml2/Response.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public function __construct(OneLogin_Saml2_Settings $settings, $response)
9696
*
9797
* @return bool Validate the document
9898
*/
99-
public function isValid($requestId = null)
99+
public function isValid($requestId = null, $requestIssueInstant = null)
100100
{
101101
$this->_error = null;
102102
try {
@@ -178,6 +178,19 @@ public function isValid($requestId = null)
178178
);
179179
}
180180

181+
// Check if the IssueInstant of the Response is later than the one of the AuthNRequest if provided (considering a configured clock skew)
182+
if (isset($requestIssueInstant)) {
183+
$issueInstant = $this->document->documentElement->getAttribute('IssueInstant');
184+
$issueInstantTime = OneLogin_Saml2_Utils::parseSAML2Time($issueInstant);
185+
$requestIssueInstantTime = OneLogin_Saml2_Utils::parseSAML2Time($requestIssueInstant);
186+
if ($requestIssueInstantTime > $issueInstantTime + $security['clockSkewTolerance']) {
187+
throw new OneLogin_Saml2_ValidationError(
188+
"The IssueInstant of the Response: $issueInstant is not equal or later than the IssueInstant of the Request: $requestIssueInstant (considering the configured clock skew)",
189+
OneLogin_Saml2_ValidationError::INVALID_ISSUEINSTANT
190+
);
191+
}
192+
}
193+
181194
if (!$this->encrypted && $security['wantAssertionsEncrypted']) {
182195
throw new OneLogin_Saml2_ValidationError(
183196
"The assertion of the Response is not encrypted and the SP requires it",

tests/src/OneLogin/Saml2/LogoutResponseTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,48 @@ public function testIsInValidRequestId()
145145
$this->assertContains('The InResponseTo of the Logout Response:', $response2->getError());
146146
}
147147

148+
/**
149+
* Tests the isValid method of the OneLogin_Saml2_LogoutResponse class
150+
* Case invalid IssueInstant
151+
*
152+
* @covers OneLogin_Saml2_LogoutResponse::isValid
153+
*/
154+
public function testIsInvalidIssueInstant()
155+
{
156+
$_SERVER['HTTP_HOST'] = 'stuff.com';
157+
$_SERVER['HTTPS'] = 'off';
158+
$_SERVER['REQUEST_URI'] = '/endpoints/endpoints/sls.php';
159+
160+
$message = file_get_contents(TEST_ROOT . '/data/logout_responses/logout_response.xml.base64');
161+
162+
// Not strict, always valid
163+
$response = new OneLogin_Saml2_LogoutResponse($this->_settings, $message);
164+
$this->assertTrue($response->isValid(null, false, '2013-12-10T04:39:31Z'));
165+
$this->assertTrue($response->isValid(null, false, '2013-12-10T04:42:31Z'));
166+
167+
// Strict, valid/invalid with 0 seconds (default) of clock skew tolerance
168+
$this->_settings->setStrict(true);
169+
$response2 = new OneLogin_Saml2_LogoutResponse($this->_settings, $message);
170+
$this->assertTrue($response2->isValid(null, false, '2013-12-10T04:39:30Z'));
171+
$this->assertTrue($response2->isValid(null, false, '2013-12-10T04:39:31Z'));
172+
$this->assertFalse($response2->isValid(null, false, '2013-12-10T04:39:32Z'));
173+
$this->assertContains('The IssueInstant of the Logout Response', $response2->getError());
174+
175+
// Strict, valid/invalid with 180 seconds of configured clock skew tolerance
176+
include TEST_ROOT .'/settings/settings1.php';
177+
$settingsInfo['strict'] = true;
178+
$settingsInfo['security']['clockSkewTolerance'] = 180;
179+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
180+
$response3 = new OneLogin_Saml2_LogoutResponse($settings, $message);
181+
$this->assertTrue($response3->isValid(null, false, '2013-12-10T04:42:31Z'));
182+
$this->assertFalse($response3->isValid(null, false, '2013-12-10T04:42:32Z'));
183+
$this->assertContains('The IssueInstant of the Logout Response', $response3->getError());
184+
185+
unset($_SERVER['HTTP_HOST']);
186+
unset($_SERVER['HTTPS']);
187+
unset($_SERVER['REQUEST_URI']);
188+
}
189+
148190
/**
149191
* Tests the isValid method of the OneLogin_Saml2_LogoutResponse
150192
* Case invalid Issuer

tests/src/OneLogin/Saml2/ResponseTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,47 @@ public function testIsInValidRequestId()
12801280
$this->assertContains('No Signature found. SAML Response rejected', $response2->getError());
12811281
}
12821282

1283+
/**
1284+
* Tests the isValid method of the OneLogin_Saml2_Response class
1285+
* Case invalid IssueInstant
1286+
*
1287+
* @covers OneLogin_Saml2_Response::isValid
1288+
*/
1289+
public function testIsInvalidIssueInstant()
1290+
{
1291+
$_SERVER['HTTP_HOST'] = 'pitbulk.no-ip.org';
1292+
$_SERVER['HTTPS'] = 'https';
1293+
$_SERVER['REQUEST_URI'] = '/newonelogin/demo1/index.php?acs';
1294+
1295+
$xml = file_get_contents(TEST_ROOT . '/data/responses/valid_response.xml.base64');
1296+
1297+
// Not strict, always valid
1298+
$response = new OneLogin_Saml2_Response($this->_settings, $xml);
1299+
$this->assertTrue($response->isValid(null, '2014-02-19T01:37:01Z'));
1300+
$this->assertTrue($response->isValid(null, '2014-02-19T01:39:01Z'));
1301+
1302+
// Strict, valid/invalid with 0 seconds (default) of clock skew tolerance
1303+
$this->_settings->setStrict(true);
1304+
$response2 = new OneLogin_Saml2_Response($this->_settings, $xml);
1305+
$this->assertTrue($response2->isValid(null, '2014-02-19T01:37:00Z'));
1306+
$this->assertTrue($response2->isValid(null, '2014-02-19T01:37:01Z'));
1307+
$this->assertFalse($response2->isValid(null, '2014-02-19T01:37:02Z'));
1308+
$this->assertContains('The IssueInstant of the Response', $response2->getError());
1309+
1310+
// Strict, valid/invalid with 180 seconds of configured clock skew tolerance
1311+
include TEST_ROOT .'/settings/settings1.php';
1312+
$settingsInfo['strict'] = true;
1313+
$settingsInfo['security']['clockSkewTolerance'] = 180;
1314+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
1315+
$response3 = new OneLogin_Saml2_Response($settings, $xml);
1316+
$this->assertTrue($response3->isValid(null, '2014-02-19T01:40:01Z'));
1317+
$this->assertFalse($response3->isValid(null, '2014-02-19T01:40:02Z'));
1318+
$this->assertContains('The IssueInstant of the Response', $response3->getError());
1319+
1320+
unset($_SERVER['HTTP_HOST']);
1321+
unset($_SERVER['HTTPS']);
1322+
unset($_SERVER['REQUEST_URI']);
1323+
}
12831324

12841325
/**
12851326
* Tests the isValid method of the OneLogin_Saml2_Response class

0 commit comments

Comments
 (0)