Merge pull request #450 from nextcloud/enh/noid/bump-3rdparty

actually bump deps
This commit is contained in:
blizzz 2020-09-10 08:51:56 +02:00 committed by GitHub
commit 6ae09ed278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 563 additions and 131 deletions

View File

@ -13,6 +13,9 @@ class ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {

View File

@ -46,22 +46,22 @@
},
{
"name": "onelogin/php-saml",
"version": "3.1.1",
"version_normalized": "3.1.1.0",
"version": "3.4.1",
"version_normalized": "3.4.1.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "c9026b26395a65184550055d9a01bdf9dbd30861"
"reference": "5fbf3486704ac9835b68184023ab54862c95f213"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/c9026b26395a65184550055d9a01bdf9dbd30861",
"reference": "c9026b26395a65184550055d9a01bdf9dbd30861",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/5fbf3486704ac9835b68184023ab54862c95f213",
"reference": "5fbf3486704ac9835b68184023ab54862c95f213",
"shasum": ""
},
"require": {
"php": ">=5.4",
"robrichards/xmlseclibs": ">=3.0.3"
"robrichards/xmlseclibs": ">=3.0.4"
},
"require-dev": {
"pdepend/pdepend": "^2.5.0",
@ -76,7 +76,7 @@
"ext-gettext": "Install gettext and php5-gettext libs to handle translations",
"ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
},
"time": "2019-03-11T09:41:32+00:00",
"time": "2019-11-25T17:30:07+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -98,24 +98,24 @@
},
{
"name": "robrichards/xmlseclibs",
"version": "3.0.3",
"version_normalized": "3.0.3.0",
"version": "3.1.1",
"version_normalized": "3.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/robrichards/xmlseclibs.git",
"reference": "406c68ac9124db033d079284b719958b829cb830"
"reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/406c68ac9124db033d079284b719958b829cb830",
"reference": "406c68ac9124db033d079284b719958b829cb830",
"url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/f8f19e58f26cdb42c54b214ff8a820760292f8df",
"reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"php": ">= 5.4"
},
"time": "2018-11-15T11:59:02+00:00",
"time": "2020-09-05T13:00:25+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

View File

@ -1,5 +1,42 @@
CHANGELOG
=========
v3.4.1
* Add setSchemasPath to Auth class and fix backward compatibility
v.3.4.0
* Support rejecting unsolicited SAMLResponses.
* Support stric destination matching.
* Reject SAMLResponse if requestID was provided to the validotr but the InResponseTo attributeof the SAMLResponse is missing
* Check destination against the getSelfURLNoQuery as well on LogoutRequest and LogoutResponse as we do on Response
* Improve getSelfRoutedURLNoQuery method
* Only add responseUrl to the settings if ResponseLocation present in the IdPMetadataParser
* Remove use of $_GET on static method validateBinarySign
* Fix error message when Assertion and NameId are both encrypted (not supported)
v.3.3.1
* Update xmlseclibs to 3.0.4
* Remove Comparison atribute from RequestedAuthnContext when setting has empty value
v.3.3.0
* Set true as the default value for strict setting
* Relax comparision of false on SignMetadata
* Fix CI
v.3.2.1
* Add missed nameIdValueReq parameter to buildAuthnRequest method
v.3.2.0
* Add support for Subjects on AuthNRequests by the new parameter nameIdValueReq
* Support SLO ResponseLocation
* [#344](https://github.com/onelogin/php-saml/issues/344) Raise errors on IdPMetadataParser::parseRemoteXML and IdPMetadataParser::parseFileXML
* [#356](https://github.com/onelogin/php-saml/issues/356) Support 'x509cert' and 'privateKey' on signMetadata security setting
v.3.1.1
* Force to use at least xmlseclibs 3.0.3 for security reasons
* [#367](https://github.com/onelogin/php-saml/pull/367) Move the creation of the AuthnRequest to separate function
* Set strict=true on config examples
* Move phpunit.xml
v.3.1.0
* Security improvement suggested by Nils Engelbertz to prevent DDOS by expansion of internally defined entities (XEE)
* Fix setting_example.php servicename parameter
@ -8,6 +45,51 @@ v.3.0.0
* Remove mcrypt dependency. Compatible with PHP 7.2
* xmlseclibs now is not part of the toolkit and need to be installed from original source
v.2.18.0
* Support rejecting unsolicited SAMLResponses.
* Support stric destination matching.
* Reject SAMLResponse if requestID was provided to the validotr but the InResponseTo attributeof the SAMLResponse is missing
* Check destination against the getSelfURLNoQuery as well on LogoutRequest and LogoutResponse as we do on Response
* Improve getSelfRoutedURLNoQuery method
* Only add responseUrl to the settings if ResponseLocation present in the IdPMetadataParser
* Remove use of $_GET on static method validateBinarySign
* Fix error message when Assertion and NameId are both encrypted (not supported)
v.2.17.1
* Update xmlseclibs to 3.0.4
* Remove Comparison atribute from RequestedAuthnContext when setting has empty value
v.2.17.0
* Set true as the default value for strict setting
* Support 'x509cert' and 'privateKey' on signMetadata security settings
* Relax comparision of false on SignMetadata
* Fix CI
v.2.16.0
* Support SLO ResponseLocation
* [#344](https://github.com/onelogin/php-saml/issues/344) Raise errors on IdPMetadataParser::parseRemoteXML and IdPMetadataParser::parseFileXML
* Adjusted acs endpoint to extract NameQualifier and SPNameQualifier from SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs
* Add support for Subjects on AuthNRequests by the new parameter
* Set strict=true on config examples
v.2.15.0
* Security improvement suggested by Nils Engelbertz to prevent DDOS by expansion of internally defined entities (XEE)
* Fix bug on settings_example.php
v.2.14.0
* Add parameter to the decryptElement method to make optional the formatting
* [#283](https://github.com/onelogin/php-saml/pull/283) New method of importing a decrypted assertion into the XML document to replace the EncryptedAssertion. Fix signature issues on Signed Encrypted Assertions with default namespace
* Allow the getSPMetadata() method to always include the encryption Key Descriptor
* Change some Fatal Error to Exceptions
* [#265](https://github.com/onelogin/php-saml/issues/265) Support parameters at getSPMetadata method
* Avoid calling static method using this
v.2.13.0
* Update xmlseclibs with some fixes.
* Add extra protection verifying the Signature algorithm used on SignedInfo element, not only rely on the xmlseclibs verify / verifySignature methods.
* Add getAttributesWithFriendlyName method which returns the set of SAML attributes indexed by FriendlyName
* Fix bug on parseRemoteXML and parseFileXML. Internal calls to parseXML missed the desiredNameIdFormat parameter
v.2.12.0
* Improve Time management. Use DateTime/DateTimeZone classes.
* Escape error messages in debug mode

View File

@ -10,6 +10,12 @@ and supported by OneLogin Inc.
Warning
-------
Version 3.4.0 introduces the 'rejectUnsolicitedResponsesWithInResponseTo' setting parameter, by default disabled, that will allow invalidate unsolicited SAMLResponse. This version as well will reject SAMLResponse if requestId was provided to the validator but the SAMLResponse does not contain a InResponseTo attribute. And an additional setting parameter 'destinationStrictlyMatches', by default disabled, that will force that the Destination URL should strictly match to the address that process the SAMLResponse.
Version 3.3.1 updates xmlseclibs to 3.0.4 (CVE-2019-3465), but php-saml was not directly affected since it implements additional checks that prevent to exploit that vulnerability.
Version 3.3.0 sets strict mode active by default
Update php-saml to 3.1.0, this version includes a security patch related to XEE attacks.
This version is compatible with PHP 7.X and does not include xmlseclibs (you will need to install it via composer, dependency described in composer.json)
@ -84,7 +90,13 @@ Installation
### Code ###
#### Option 1. Download from github ####
#### Option 1. clone the repository from github ####
git clone git@github.com:onelogin/php-saml.git
Then pull the 3.X.X branch/tag
#### Option 2. Download from github ####
The toolkit is hosted on github. You can download it from:
@ -96,7 +108,10 @@ Copy the core of the library inside the php application. (each application has i
structure so take your time to locate the PHP SAML toolkit in the best place).
See the "Guide to add SAML support to my app" to know how.
#### Option 2. Composer ####
Take in mind that the compressed file only contains the main files.
If you plan to play with the demos, use the Option 1.
#### Option 3. Composer ####
The toolkit supports [composer](https://getcomposer.org/). You can find the `onelogin/php-saml` package at https://packagist.org/packages/onelogin/php-saml
@ -337,6 +352,9 @@ $settings = array(
'singleLogoutService' => array(
// URL Location of the IdP where SLO Request will be sent.
'url' => '',
// URL location of the IdP where SLO Response will be sent (ResponseLocation)
// if not set, url for the SLO Request will be used
'responseUrl' => '',
// SAML protocol binding to be used when returning the <Response>
// message. OneLogin Toolkit supports the HTTP-Redirect binding
// only for this endpoint.
@ -417,10 +435,14 @@ $advancedSettings = array(
'logoutResponseSigned' => false,
/* Sign the Metadata
False || True (use sp certs) || array(
keyFileName => 'metadata.key',
certFileName => 'metadata.crt'
)
False || True (use sp certs) || array (
'keyFileName' => 'metadata.key',
'certFileName' => 'metadata.crt'
)
|| array (
'x509cert' => '',
'privateKey' => ''
)
*/
'signMetadata' => false,
@ -460,6 +482,16 @@ $advancedSettings = array(
// attribute will not be rejected for this fact.
'relaxDestinationValidation' => false,
// If true, Destination URL should strictly match to the address to
// which the response has been sent.
// Notice that if 'relaxDestinationValidation' is true an empty Destintation
// will be accepted.
'destinationStrictlyMatches' => false,
// If true, SAMLResponses with an InResponseTo value will be rejectd if not
// AuthNRequest ID provided to the validation method.
'rejectUnsolicitedResponsesWithInResponseTo' => false,
// Algorithm that the toolkit will use on signing process. Options:
// 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
@ -602,13 +634,14 @@ $auth = new OneLogin\Saml2\Auth();
$auth->login($newTargetUrl);
```
The login method can receive other five optional parameters:
The login method can receive other six optional parameters:
* `$parameters` - An array of parameters that will be added to the `GET` in the HTTP-Redirect.
* `$forceAuthn` - When true the `AuthNRequest` will set the `ForceAuthn='true'`
* `$isPassive` - When true the `AuthNRequest` will set the `Ispassive='true'`
* `$strict` - True if we want to stay (returns the url string) False to redirect
* `$setNameIdPolicy` - When true the AuthNRequest will set a nameIdPolicy element.
* `$nameIdValueReq` - Indicates to the IdP the subject that should be authenticated.
If a match on the future SAMLResponse ID and the AuthNRequest ID to be sent is required, that AuthNRequest ID must to be extracted and saved.
@ -707,8 +740,8 @@ if (!$auth->isAuthenticated()) {
$_SESSION['samlUserdata'] = $auth->getAttributes();
$_SESSION['samlNameId'] = $auth->getNameId();
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
$_SESSION['samlNameidNameQualifier' = $auth->getNameIdNameQualifier();
$_SESSION['samlNameidSPNameQualifier' = $auth->getNameIdSPNameQualifier();
$_SESSION['samlNameidNameQualifier'] = $auth->getNameIdNameQualifier();
$_SESSION['samlNameidSPNameQualifier'] = $auth->getNameIdSPNameQualifier();
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();
if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
@ -968,7 +1001,7 @@ A more complex logout with all the parameters:
```
$auth = new OneLogin\Saml2\Auth();
$returnTo = null;
$paramters = array();
$parameters = array();
$nameId = null;
$sessionIndex = null;
$nameIdFormat = null;
@ -990,13 +1023,13 @@ if (isset($_SESSION['samlNameIdNameQualifier'])) {
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
$nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
}
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
```
If a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored.
```php
$sloBuiltUrl = $auth->logout(null, $paramters, $nameId, $sessionIndex, true);
$sloBuiltUrl = $auth->logout(null, $parameters, $nameId, $sessionIndex, true);
$_SESSION['LogoutRequestID'] = $auth->getLastRequestID();
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
@ -1098,7 +1131,7 @@ php-saml toolkit uses a bunch of methods in OneLogin\Saml2\Utils that try to gue
* `getSelfURLNoQuery` Returns the URL of the current host + current view.
* `getSelfRoutedURLNoQuery` Returns the routed URL of the current host + current view.
getSelfURLNoQuery and getSelfRoutedURLNoQuery are used to calculate the currentURL in order to valdate SAML elements like Destination or Recipient.
getSelfURLNoQuery and getSelfRoutedURLNoQuery are used to calculate the currentURL in order to validate SAML elements like Destination or Recipient.
When the PHP application is behind a proxy or a load balancer we can execute `setProxyVars(true)` and `setSelfPort` and `isHTTPS` will take care of the `$_SERVER["HTTP_X_FORWARDED_PORT"]` and `$_SERVER['HTTP_X_FORWARDED_PROTO']` vars (otherwise they are ignored).
@ -1400,7 +1433,7 @@ Once the SP is configured, the metadata of the SP is published at the
process, the `index.php` view.
2.2 in the second link we access to (`attrs.php`) have the same process
described at 2.1 with the diference that as `RelayState` is set the `attrs.php`.
described at 2.1 with the difference that as `RelayState` is set the `attrs.php`.
3. The SAML Response is processed in the ACS (`index.php?acs`), if the Response
is not valid, the process stops here and a message is shown. Otherwise we
@ -1427,7 +1460,7 @@ Once the SP is configured, the metadata of the SP is published at the
session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.
Notice that all the SAML Requests and Responses are handled by a unique file,
the `index.php` file and how `GET` paramters are used to know the action that
the `index.php` file and how `GET` parameters are used to know the action that
must be done.

View File

@ -33,10 +33,14 @@ $advancedSettings = array(
'logoutResponseSigned' => false,
/* Sign the Metadata
False || True (use sp certs) || array(
keyFileName => 'metadata.key',
certFileName => 'metadata.crt'
)
False || True (use sp certs) || array (
'keyFileName' => 'metadata.key',
'certFileName' => 'metadata.crt'
)
|| array (
'x509cert' => '',
'privateKey' => ''
)
*/
'signMetadata' => false,
@ -81,6 +85,16 @@ $advancedSettings = array(
// attribute will not be rejected for this fact.
'relaxDestinationValidation' => false,
// If true, Destination URL should strictly match to the address to
// which the response has been sent.
// Notice that if 'relaxDestinationValidation' is true an empty Destintation
// will be accepted.
'destinationStrictlyMatches' => false,
// If true, SAMLResponses with an InResponseTo value will be rejectd if not
// AuthNRequest ID provided to the validation method.
'rejectUnsolicitedResponsesWithInResponseTo' => false,
// Algorithm that the toolkit will use on signing process. Options:
// 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'

View File

@ -16,11 +16,11 @@
},
"require": {
"php": ">=5.4",
"robrichards/xmlseclibs": ">=3.0.3"
"robrichards/xmlseclibs": ">=3.0.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1",
"php-coveralls/php-coveralls": "^1.0.2 || ^2.0",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1",
"sebastian/phpcpd": "^2.0 || ^3.0 || ^4.0",
"phploc/phploc": "^2.1 || ^3.0 || ^4.0",
"pdepend/pdepend": "^2.5.0",

View File

@ -0,0 +1,18 @@
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="OneLogin PHP-SAML Test Suite">
<directory>./tests/src</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="./tests/build/coverage" lowUpperBound="35" highLowerBound="70"/>
<log type="coverage-xml" target="./tests/build/logfile.xml"/>
<log type="coverage-clover" target="./tests/build/logs/clover.xml"/>
<log type="coverage-php" target="./tests/build/logs/coverage.cov"/>
</logging>
</phpunit>

View File

@ -27,7 +27,7 @@ $settings = array(
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-Redirect binding only
// HTTP-POST binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
),
// If you need to specify requested attributes, set a
@ -86,13 +86,16 @@ $settings = array(
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-POST binding only
// HTTP-Redirect binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
),
// SLO endpoint info of the IdP.
'singleLogoutService' => array(
// URL Location of the IdP where the SP will send the SLO Request
'url' => '',
// URL location of the IdP where the SP SLO Response will be sent (ResponseLocation)
// if not set, url for the SLO Request will be used
'responseUrl' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-Redirect binding only

View File

@ -206,6 +206,17 @@ class Auth
$this->_settings->setStrict($value);
}
/**
* Set schemas path
*
* @param string $path
* @return $this
*/
public function setSchemasPath($path)
{
$this->_paths['schemas'] = $path;
}
/**
* Process the SAML Response sent by the IdP.
*
@ -322,7 +333,7 @@ class Auth
$parameters['Signature'] = $signature;
}
return $this->redirectTo($this->getSLOurl(), $parameters, $stay);
return $this->redirectTo($this->getSLOResponseUrl(), $parameters, $stay);
}
} else {
$this->_errors[] = 'invalid_binding';
@ -520,14 +531,15 @@ class Auth
* @param bool $isPassive When true the AuthNRequest will set the Ispassive='true'
* @param bool $stay True if we want to stay (returns the url string) False to redirect
* @param bool $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
*
* @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
*
*
* @throws Error
*/
public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true)
public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true, $nameIdValueReq = null)
{
$authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
$authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
$this->_lastRequest = $authnRequest->getXML();
$this->_lastRequestID = $authnRequest->getId();
@ -632,6 +644,20 @@ class Auth
return $url;
}
/**
* Gets the SLO response url.
*
* @return string|null The response url of the Single Logout Service
*/
public function getSLOResponseUrl()
{
$idpData = $this->_settings->getIdPData();
if (isset($idpData['singleLogoutService']) && isset($idpData['singleLogoutService']['responseUrl'])) {
return $idpData['singleLogoutService']['responseUrl'];
}
return $this->getSLOurl();
}
/**
* Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider.
*
@ -649,12 +675,13 @@ class Auth
* @param bool $forceAuthn When true the AuthNRequest will set the ForceAuthn='true'
* @param bool $isPassive When true the AuthNRequest will set the Ispassive='true'
* @param bool $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
*
* @return AuthnRequest The AuthnRequest object
*/
public function buildAuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy)
public function buildAuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq = null)
{
return new AuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy);
return new AuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq);
}
/**
@ -704,7 +731,7 @@ class Auth
* @throws Exception
* @throws Error
*/
private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type="SAMLRequest")
private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type = "SAMLRequest")
{
$key = $this->_settings->getSPkey();
if (empty($key)) {

View File

@ -44,12 +44,13 @@ class AuthnRequest
/**
* Constructs the AuthnRequest object.
*
* @param Settings $settings SAML Toolkit Settings
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
* @param Settings $settings SAML Toolkit Settings
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
* @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
*/
public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true)
public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null)
{
$this->_settings = $settings;
@ -60,6 +61,17 @@ class AuthnRequest
$id = Utils::generateUniqueID();
$issueInstant = Utils::parseTime2SAML(time());
$subjectStr = "";
if (isset($nameIdValueReq)) {
$subjectStr = <<<SUBJECT
<saml:Subject>
<saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation>
</saml:Subject>
SUBJECT;
}
$nameIdPolicyStr = '';
if ($setNameIdPolicy) {
$nameIDPolicyFormat = $spData['NameIDFormat'];
@ -68,6 +80,7 @@ class AuthnRequest
}
$nameIdPolicyStr = <<<NAMEIDPOLICY
<samlp:NameIDPolicy
Format="{$nameIDPolicyFormat}"
AllowCreate="true" />
@ -114,14 +127,20 @@ ISPASSIVE;
$authnComparison = $security['requestedAuthnContextComparison'];
}
$authnComparisonAttr = '';
if (!empty($authnComparison)) {
$authnComparisonAttr = sprintf('Comparison="%s"', $authnComparison);
}
if ($security['requestedAuthnContext'] === true) {
$requestedAuthnStr = <<<REQUESTEDAUTHN
<samlp:RequestedAuthnContext Comparison="$authnComparison">
<samlp:RequestedAuthnContext $authnComparisonAttr>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
REQUESTEDAUTHN;
} else {
$requestedAuthnStr .= " <samlp:RequestedAuthnContext Comparison=\"$authnComparison\">\n";
$requestedAuthnStr .= " <samlp:RequestedAuthnContext $authnComparisonAttr>\n";
foreach ($security['requestedAuthnContext'] as $contextValue) {
$requestedAuthnStr .= " <saml:AuthnContextClassRef>".$contextValue."</saml:AuthnContextClassRef>\n";
}
@ -142,9 +161,7 @@ REQUESTEDAUTHN;
Destination="{$idpData['singleSignOnService']['url']}"
ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
AssertionConsumerServiceURL="{$acsUrl}">
<saml:Issuer>{$spEntityId}</saml:Issuer>
{$nameIdPolicyStr}
{$requestedAuthnStr}
<saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr}
</samlp:AuthnRequest>
AUTHNREQUEST;

View File

@ -56,6 +56,7 @@ class IdPMetadataParser
throw new Exception(curl_error($ch), curl_errno($ch));
}
} catch (Exception $e) {
throw new Exception('Error on parseRemoteXML. '.$e->getMessage());
}
return $metadataInfo;
}
@ -84,6 +85,7 @@ class IdPMetadataParser
$metadataInfo = self::parseXML($data, $entityId, $desiredNameIdFormat, $desiredSSOBinding, $desiredSLOBinding);
}
} catch (Exception $e) {
throw new Exception('Error on parseFileXML. '.$e->getMessage());
}
return $metadataInfo;
}
@ -158,6 +160,10 @@ class IdPMetadataParser
'url' => $sloNodes->item(0)->getAttribute('Location'),
'binding' => $sloNodes->item(0)->getAttribute('Binding')
);
if ($sloNodes->item(0)->hasAttribute('ResponseLocation')) {
$metadataInfo['idp']['singleLogoutService']['responseUrl'] = $sloNodes->item(0)->getAttribute('ResponseLocation');
}
}
$keyDescriptorCertSigningNodes = Utils::query($dom, './md:KeyDescriptor[not(contains(@use, "encryption"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate', $idpDescriptor);

View File

@ -104,7 +104,7 @@ class LogoutRequest
$nameIdFormat = Constants::NAMEID_ENTITY;
}
/* From saml-core-2.0-os 8.3.6, when the entity Format is used:
/* From saml-core-2.0-os 8.3.6, when the entity Format is used:
"The NameQualifier, SPNameQualifier, and SPProvidedID attributes MUST be omitted.
*/
if (!empty($nameIdFormat) && $nameIdFormat == Constants::NAMEID_ENTITY) {
@ -278,7 +278,7 @@ LOGOUTREQUEST;
* @param string|null $key The SP key
*
* @return string Name ID Value
*
*
* @throws Error
* @throws Exception
* @throws ValidationError
@ -295,7 +295,7 @@ LOGOUTREQUEST;
* @param string|DOMDocument $request Logout Request Message
*
* @return string|null $issuer The Issuer
*
*
* @throws Exception
*/
public static function getIssuer($request)
@ -324,7 +324,7 @@ LOGOUTREQUEST;
* @param string|DOMDocument $request Logout Request Message
*
* @return array The SessionIndex value
*
*
* @throws Exception
*/
public static function getSessionIndexes($request)
@ -350,7 +350,7 @@ LOGOUTREQUEST;
* @param bool $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature
*
* @return bool If the Logout Request is or not valid
*
*
* @throws Exception
* @throws ValidationError
*/
@ -368,7 +368,7 @@ LOGOUTREQUEST;
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
$res = Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive(), $this->_settings->getSchemasPath());
if (!$res instanceof DOMDocument) {
throw new ValidationError(
"Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd",
@ -393,11 +393,25 @@ LOGOUTREQUEST;
// Check destination
if ($dom->documentElement->hasAttribute('Destination')) {
$destination = $dom->documentElement->getAttribute('Destination');
if (!empty($destination) && strpos($destination, $currentURL) === false) {
throw new ValidationError(
"The LogoutRequest was received at $currentURL instead of $destination",
ValidationError::WRONG_DESTINATION
);
if (empty($destination)) {
if (!$security['relaxDestinationValidation']) {
throw new ValidationError(
"The LogoutRequest has an empty Destination value",
ValidationError::EMPTY_DESTINATION
);
}
} else {
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURL);
if (strncmp($destination, $currentURL, $urlComparisonLength) !== 0) {
$currentURLNoRouted = Utils::getSelfURLNoQuery();
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURLNoRouted);
if (strncmp($destination, $currentURLNoRouted, $urlComparisonLength) !== 0) {
throw new ValidationError(
"The LogoutRequest was received at $currentURL instead of $destination",
ValidationError::WRONG_DESTINATION
);
}
}
}
}

View File

@ -65,10 +65,9 @@ class LogoutResponse
*
* @param Settings $settings Settings.
* @param string|null $response An UUEncoded SAML Logout response from the IdP.
*
*
* @throws Error
* @throws Exception
*
*/
public function __construct(\OneLogin\Saml2\Settings $settings, $response = null)
{
@ -140,7 +139,7 @@ class LogoutResponse
* @param bool $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature
*
* @return bool Returns if the SAML LogoutResponse is or not valid
*
*
* @throws ValidationError
*/
public function isValid($requestId = null, $retrieveParametersFromServer = false)
@ -154,7 +153,7 @@ class LogoutResponse
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
$res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive(), $this->_settings->getSchemasPath());
if (!$res instanceof DOMDocument) {
throw new ValidationError(
"Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd",
@ -185,14 +184,27 @@ class LogoutResponse
$currentURL = Utils::getSelfRoutedURLNoQuery();
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = $this->document->documentElement->getAttribute('Destination');
if (!empty($destination) && strpos($destination, $currentURL) === false) {
throw new ValidationError(
"The LogoutResponse was received at $currentURL instead of $destination",
ValidationError::WRONG_DESTINATION
);
if (empty($destination)) {
if (!$security['relaxDestinationValidation']) {
throw new ValidationError(
"The LogoutResponse has an empty Destination value",
ValidationError::EMPTY_DESTINATION
);
}
} else {
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURL);
if (strncmp($destination, $currentURL, $urlComparisonLength) !== 0) {
$currentURLNoRouted = Utils::getSelfURLNoQuery();
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURLNoRouted);
if (strncmp($destination, $currentURLNoRouted, $urlComparisonLength) !== 0) {
throw new ValidationError(
"The LogoutResponse was received at $currentURL instead of $destination",
ValidationError::WRONG_DESTINATION
);
}
}
}
}

View File

@ -200,7 +200,7 @@ METADATA_TEMPLATE;
* @param string $digestAlgorithm Digest algorithm method
*
* @return string Signed Metadata
*
*
* @throws Exception
*/
public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $digestAlgorithm = XMLSecurityDSig::SHA256)
@ -217,7 +217,7 @@ METADATA_TEMPLATE;
* @param bool $wantsEncrypted Whether to include the KeyDescriptor for encryption
*
* @return string Metadata with KeyDescriptors
*
*
* @throws Exception
*/
public static function addX509KeyDescriptors($metadata, $cert, $wantsEncrypted = true)

View File

@ -172,7 +172,7 @@ class Response
if ($security['wantXMLValidation']) {
$errorXmlMsg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd";
$res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
$res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive(), $this->_settings->getSchemasPath());
if (!$res instanceof DOMDocument) {
throw new ValidationError(
$errorXmlMsg,
@ -182,7 +182,7 @@ class Response
// If encrypted, check also the decrypted document
if ($this->encrypted) {
$res = Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
$res = Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive(), $this->_settings->getSchemasPath());
if (!$res instanceof DOMDocument) {
throw new ValidationError(
$errorXmlMsg,
@ -195,18 +195,33 @@ class Response
$currentURL = Utils::getSelfRoutedURLNoQuery();
$responseInResponseTo = null;
if ($this->document->documentElement->hasAttribute('InResponseTo')) {
$responseInResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
}
// Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
if (isset($requestId) && isset($responseInResponseTo) && $requestId != $responseInResponseTo) {
if (!isset($requestId) && isset($responseInResponseTo) && $security['rejectUnsolicitedResponsesWithInResponseTo']) {
throw new ValidationError(
"The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId",
"The Response has an InResponseTo attribute: " . $responseInResponseTo . " while no InResponseTo was expected",
ValidationError::WRONG_INRESPONSETO
);
}
// Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
if (isset($requestId) && $requestId != $responseInResponseTo) {
if ($responseInResponseTo == null) {
throw new ValidationError(
"No InResponseTo at the Response, but it was provided the requestId related to the AuthNRequest sent by the SP: $requestId",
ValidationError::WRONG_INRESPONSETO
);
} else {
throw new ValidationError(
"The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId",
ValidationError::WRONG_INRESPONSETO
);
}
}
if (!$this->encrypted && $security['wantAssertionsEncrypted']) {
throw new ValidationError(
"The assertion of the Response is not encrypted and the SP requires it",
@ -263,10 +278,11 @@ class Response
);
}
} else {
if (strpos($destination, $currentURL) !== 0) {
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURL);
if (strncmp($destination, $currentURL, $urlComparisonLength) !== 0) {
$currentURLNoRouted = Utils::getSelfURLNoQuery();
if (strpos($destination, $currentURLNoRouted) !== 0) {
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURLNoRouted);
if (strncmp($destination, $currentURLNoRouted, $urlComparisonLength) !== 0) {
throw new ValidationError(
"The response was received at $currentURL instead of $destination",
ValidationError::WRONG_DESTINATION
@ -383,7 +399,7 @@ class Response
$encryptedIDNodes = Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID');
if ($encryptedIDNodes->length > 0) {
throw new ValidationError(
'Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.',
'SAML Response that contains an encrypted Assertion with encrypted nameId is not supported.',
ValidationError::NOT_SUPPORTED
);
}
@ -448,7 +464,7 @@ class Response
/**
* @return string|null the ID of the assertion in the Response
*
*
* @throws ValidationError
*/
public function getAssertionId()

View File

@ -45,7 +45,7 @@ class Settings
*
* @var bool
*/
private $_strict = false;
private $_strict = true;
/**
* Activate debug mode
@ -164,7 +164,7 @@ class Settings
'base' => $basePath,
'config' => $basePath,
'cert' => $basePath.'certs/',
'lib' => $basePath.'src/'
'lib' => $basePath.'src/Saml2/'
);
if (defined('ONELOGIN_CUSTOMPATH')) {
@ -220,7 +220,21 @@ class Settings
*/
public function getSchemasPath()
{
return $this->_paths['lib'].'schemas/';
if (isset($this->_paths['schemas'])) {
return $this->_paths['schemas'];
}
return __DIR__ . '/schemas/';
}
/**
* Set schemas path
*
* @param string $path
* @return $this
*/
public function setSchemasPath($path)
{
$this->_paths['schemas'] = $path;
}
/**
@ -378,6 +392,16 @@ class Settings
$this->_security['relaxDestinationValidation'] = false;
}
// Strict Destination match validation
if (!isset($this->_security['destinationStrictlyMatches'])) {
$this->_security['destinationStrictlyMatches'] = false;
}
// InResponseTo
if (!isset($this->_security['rejectUnsolicitedResponsesWithInResponseTo'])) {
$this->_security['rejectUnsolicitedResponsesWithInResponseTo'] = false;
}
// encrypt expected
if (!isset($this->_security['wantAssertionsEncrypted'])) {
$this->_security['wantAssertionsEncrypted'] = false;
@ -520,6 +544,14 @@ class Settings
$errors[] = 'idp_slo_url_invalid';
}
if (isset($idp['singleLogoutService'])
&& isset($idp['singleLogoutService']['responseUrl'])
&& !empty($idp['singleLogoutService']['responseUrl'])
&& !filter_var($idp['singleLogoutService']['responseUrl'], FILTER_VALIDATE_URL)
) {
$errors[] = 'idp_slo_response_url_invalid';
}
if (isset($settings['security'])) {
$security = $settings['security'];
@ -588,8 +620,10 @@ class Settings
}
if (isset($security['signMetadata']) && is_array($security['signMetadata'])) {
if (!isset($security['signMetadata']['keyFileName'])
|| !isset($security['signMetadata']['certFileName'])
if ((!isset($security['signMetadata']['keyFileName'])
|| !isset($security['signMetadata']['certFileName'])) &&
(!isset($security['signMetadata']['privateKey'])
|| !isset($security['signMetadata']['x509cert']))
) {
$errors[] = 'sp_signMetadata_invalid';
}
@ -792,7 +826,7 @@ class Settings
*
* @param bool $alwaysPublishEncryptionCert When 'true', the returned
* metadata will always include an 'encryption' KeyDescriptor. Otherwise,
* the 'encryption' KeyDescriptor will only be included if
* the 'encryption' KeyDescriptor will only be included if
* $advancedSettings['security']['wantNameIdEncrypted'] or
* $advancedSettings['security']['wantAssertionsEncrypted'] are enabled.
* @param int|null $validUntil Metadata's valid time
@ -825,7 +859,7 @@ class Settings
}
//Sign Metadata
if (isset($this->_security['signMetadata']) && $this->_security['signMetadata'] !== false) {
if (isset($this->_security['signMetadata']) && $this->_security['signMetadata'] != false) {
if ($this->_security['signMetadata'] === true) {
$keyMetadata = $this->getSPkey();
$certMetadata = $cert;
@ -843,15 +877,8 @@ class Settings
Error::PUBLIC_CERT_FILE_NOT_FOUND
);
}
} else {
if (!isset($this->_security['signMetadata']['keyFileName'])
|| !isset($this->_security['signMetadata']['certFileName'])
) {
throw new Error(
'Invalid Setting: signMetadata value of the sp is not valid',
Error::SETTINGS_INVALID_SYNTAX
);
}
} else if (isset($this->_security['signMetadata']['keyFileName']) &&
isset($this->_security['signMetadata']['certFileName'])) {
$keyFileName = $this->_security['signMetadata']['keyFileName'];
$certFileName = $this->_security['signMetadata']['certFileName'];
@ -875,6 +902,29 @@ class Settings
}
$keyMetadata = file_get_contents($keyMetadataFile);
$certMetadata = file_get_contents($certMetadataFile);
} else if (isset($this->_security['signMetadata']['privateKey']) &&
isset($this->_security['signMetadata']['x509cert'])) {
$keyMetadata = Utils::formatPrivateKey($this->_security['signMetadata']['privateKey']);
$certMetadata = Utils::formatCert($this->_security['signMetadata']['x509cert']);
if (!$keyMetadata) {
throw new Error(
'Private key not found.',
Error::PRIVATE_KEY_FILE_NOT_FOUND
);
}
if (!$certMetadata) {
throw new Error(
'Public cert not found.',
Error::PUBLIC_CERT_FILE_NOT_FOUND
);
}
} else {
throw new Error(
'Invalid Setting: signMetadata value of the sp is not valid',
Error::SETTINGS_INVALID_SYNTAX
);
}
$signatureAlgorithm = $this->_security['signatureAlgorithm'];
@ -898,7 +948,7 @@ class Settings
assert(is_string($xml));
$errors = array();
$res = Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug);
$res = Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug, $this->getSchemasPath());
if (!$res instanceof DOMDocument) {
$errors[] = $res;
} else {

View File

@ -111,12 +111,13 @@ class Utils
* @param string|DOMDocument $xml The XML string or document which should be validated.
* @param string $schema The schema filename which should be used.
* @param bool $debug To disable/enable the debug mode
* @param string $schemaPath Change schema path
*
* @return string|DOMDocument $dom string that explains the problem or the DOMDocument
*
* @throws Exception
*/
public static function validateXML($xml, $schema, $debug = false)
public static function validateXML($xml, $schema, $debug = false, $schemaPath = null)
{
assert(is_string($xml) || $xml instanceof DOMDocument);
assert(is_string($schema));
@ -134,7 +135,12 @@ class Utils
}
}
$schemaFile = __DIR__ . '/schemas/' . $schema;
if (isset($schemaPath)) {
$schemaFile = $schemaPath . $schema;
} else {
$schemaFile = __DIR__ . '/schemas/' . $schema;
}
$oldEntityLoader = libxml_disable_entity_loader(false);
$res = $dom->schemaValidate($schemaFile);
libxml_disable_entity_loader($oldEntityLoader);
@ -622,7 +628,7 @@ class Utils
if (!empty($_SERVER['REQUEST_URI'])) {
$route = $_SERVER['REQUEST_URI'];
if (!empty($_SERVER['QUERY_STRING'])) {
$route = str_replace($_SERVER['QUERY_STRING'], '', $route);
$route = self::strLreplace($_SERVER['QUERY_STRING'], '', $route);
if (substr($route, -1) == '?') {
$route = substr($route, 0, -1);
}
@ -635,9 +641,26 @@ class Utils
}
$selfRoutedURLNoQuery = $selfURLhost . $route;
$pos = strpos($selfRoutedURLNoQuery, "?");
if ($pos !== false) {
$selfRoutedURLNoQuery = substr($selfRoutedURLNoQuery, 0, $pos-1);
}
return $selfRoutedURLNoQuery;
}
public static function strLreplace($search, $replace, $subject)
{
$pos = strrpos($subject, $search);
if ($pos !== false) {
$subject = substr_replace($subject, $replace, $pos, strlen($search));
}
return $subject;
}
/**
* Returns the URL of the current host + current view + query.
*
@ -1531,7 +1554,7 @@ class Utils
}
}
if ($objKey->verifySignature($signedQuery, base64_decode($_GET['Signature'])) === 1) {
if ($objKey->verifySignature($signedQuery, base64_decode($getData['Signature'])) === 1) {
$signatureValid = true;
break;
}

View File

@ -1,7 +1,7 @@
{
"php-saml": {
"version": "3.1.1",
"released": "20/02/2019"
"version": "3.4.1",
"released": "25/11/2019"
}
}

View File

@ -1,5 +1,33 @@
xmlseclibs.php
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
05, Sep 2020, 3.1.1
Features:
- Support OAEP (iggyvolz)
Bug Fixes:
- Fix AES128 (iggyvolz)
Improvements:
- Fix tests for older PHP
22, Apr 2020, 3.1.0
Features:
- Support AES-GCM. Requires PHP 7.1. (François Kooman)
Improvements:
- Fix Travis tests for older PHP versions.
- Use DOMElement interface to fix some IDEs reporting documentation errors
Bug Fixes:
- FIX missing InclusiveNamespaces PrefixList from Java + Apache WSS4J. (njake)
06, Nov 2019, 3.0.4
Security Improvements:
- Insure only a single SignedInfo element exists within a signature during
verification. Refs CVE-2019-3465.
Bug Fixes:
- Fix variable casing.
15, Nov 2018, 3.0.3
Bug Fixes:
- Fix casing of class name. (Willem Stuursma-Ruwen)

View File

@ -1,4 +1,4 @@
Copyright (c) 2007-2018, Robert Richards <rrichards@cdatazone.org>.
Copyright (c) 2007-2019, Robert Richards <rrichards@cdatazone.org>.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -5,8 +5,9 @@ xmlseclibs is a library written in PHP for working with XML Encryption and Signa
The author of xmlseclibs is Rob Richards.
# Branches
Both the master and the 2.0 branches are actively maintained.
* master: Removes mcrypt usage requiring 5.4+ (5.6.24+ recommended for security reasons)
Master is currently the only actively maintained branch.
* master/3.1: Added AES-GCM support requiring 7.1+
* 3.0: Removes mcrypt usage requiring 5.4+ (5.6.24+ recommended for security reasons)
* 2.0: Contains namespace support requiring 5.3+
* 1.4: Contains auto-loader support while also maintaining backwards compatiblity with the older 1.3 version using the xmlseclibs.php file. Supports PHP 5.2+

View File

@ -2,6 +2,7 @@
namespace RobRichards\XMLSecLibs;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMXPath;
use Exception;
@ -10,7 +11,7 @@ use RobRichards\XMLSecLibs\Utils\XPath as XPath;
/**
* xmlseclibs.php
*
* Copyright (c) 2007-2018, Robert Richards <rrichards@cdatazone.org>.
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -43,7 +44,7 @@ use RobRichards\XMLSecLibs\Utils\XPath as XPath;
* POSSIBILITY OF SUCH DAMAGE.
*
* @author Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2018 Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/

View File

@ -11,7 +11,7 @@ use RobRichards\XMLSecLibs\Utils\XPath as XPath;
/**
* xmlseclibs.php
*
* Copyright (c) 2007-2018, Robert Richards <rrichards@cdatazone.org>.
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -44,7 +44,7 @@ use RobRichards\XMLSecLibs\Utils\XPath as XPath;
* POSSIBILITY OF SUCH DAMAGE.
*
* @author Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2018 Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
@ -194,6 +194,11 @@ class XMLSecurityDSig
$query = ".//secdsig:Signature";
$nodeset = $xpath->query($query, $objDoc);
$this->sigNode = $nodeset->item($pos);
$query = "./secdsig:SignedInfo";
$nodeset = $xpath->query($query, $this->sigNode);
if ($nodeset->length > 1) {
throw new Exception("Invalid structure - Too many SignedInfo elements found");
}
return $this->sigNode;
}
return null;
@ -303,13 +308,28 @@ class XMLSecurityDSig
$xpath = $this->getXPathObj();
$query = "./secdsig:SignedInfo";
$nodeset = $xpath->query($query, $this->sigNode);
if ($nodeset->length > 1) {
throw new Exception("Invalid structure - Too many SignedInfo elements found");
}
if ($signInfoNode = $nodeset->item(0)) {
$query = "./secdsig:CanonicalizationMethod";
$nodeset = $xpath->query($query, $signInfoNode);
$prefixList = null;
if ($canonNode = $nodeset->item(0)) {
$canonicalmethod = $canonNode->getAttribute('Algorithm');
foreach ($canonNode->childNodes as $node)
{
if ($node->localName == 'InclusiveNamespaces') {
if ($pfx = $node->getAttribute('PrefixList')) {
$arpfx = array_filter(explode(' ', $pfx));
if (count($arpfx) > 0) {
$prefixList = array_merge($prefixList ? $prefixList : array(), $arpfx);
}
}
}
}
}
$this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
$this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod, null, $prefixList);
return $this->signedInfo;
}
}
@ -440,7 +460,7 @@ class XMLSecurityDSig
if ($node->localName == 'XPath') {
$arXPath = array();
$arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
$arXpath['namespaces'] = array();
$arXPath['namespaces'] = array();
$nslist = $xpath->query('./namespace::*', $node);
foreach ($nslist AS $nsnode) {
if ($nsnode->localName != "xml") {
@ -554,7 +574,7 @@ class XMLSecurityDSig
$refids = array();
$xpath = $this->getXPathObj();
$query = "./secdsig:SignedInfo/secdsig:Reference";
$query = "./secdsig:SignedInfo[1]/secdsig:Reference";
$nodeset = $xpath->query($query, $this->sigNode);
if ($nodeset->length == 0) {
throw new Exception("Reference nodes not found");
@ -578,7 +598,7 @@ class XMLSecurityDSig
}
}
$xpath = $this->getXPathObj();
$query = "./secdsig:SignedInfo/secdsig:Reference";
$query = "./secdsig:SignedInfo[1]/secdsig:Reference";
$nodeset = $xpath->query($query, $this->sigNode);
if ($nodeset->length == 0) {
throw new Exception("Reference nodes not found");

View File

@ -7,7 +7,7 @@ use Exception;
/**
* xmlseclibs.php
*
* Copyright (c) 2007-2018, Robert Richards <rrichards@cdatazone.org>.
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -40,7 +40,7 @@ use Exception;
* POSSIBILITY OF SUCH DAMAGE.
*
* @author Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2018 Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
@ -50,14 +50,19 @@ class XMLSecurityKey
const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
const AES128_GCM = 'http://www.w3.org/2009/xmlenc11#aes128-gcm';
const AES192_GCM = 'http://www.w3.org/2009/xmlenc11#aes192-gcm';
const AES256_GCM = 'http://www.w3.org/2009/xmlenc11#aes256-gcm';
const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
const RSA_OAEP = 'http://www.w3.org/2009/xmlenc11#rsa-oaep';
const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
const AUTHTAG_LENGTH = 16;
/** @var array */
private $cryptParams = array();
@ -142,6 +147,30 @@ class XMLSecurityKey
$this->cryptParams['keysize'] = 32;
$this->cryptParams['blocksize'] = 16;
break;
case (self::AES128_GCM):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['cipher'] = 'aes-128-gcm';
$this->cryptParams['type'] = 'symmetric';
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes128-gcm';
$this->cryptParams['keysize'] = 16;
$this->cryptParams['blocksize'] = 16;
break;
case (self::AES192_GCM):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['cipher'] = 'aes-192-gcm';
$this->cryptParams['type'] = 'symmetric';
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes192-gcm';
$this->cryptParams['keysize'] = 24;
$this->cryptParams['blocksize'] = 16;
break;
case (self::AES256_GCM):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['cipher'] = 'aes-256-gcm';
$this->cryptParams['type'] = 'symmetric';
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes256-gcm';
$this->cryptParams['keysize'] = 32;
$this->cryptParams['blocksize'] = 16;
break;
case (self::RSA_1_5):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
@ -165,6 +194,18 @@ class XMLSecurityKey
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::RSA_OAEP):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep';
$this->cryptParams['hash'] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::RSA_SHA1):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
@ -347,7 +388,7 @@ class XMLSecurityKey
case'symmetric':
if (strlen($this->key) < $this->cryptParams['keysize']) {
throw new Exception('Key must contain at least 25 characters for this cipher');
throw new Exception('Key must contain at least '.$this->cryptParams['keysize'].' characters for this cipher, contains '.strlen($this->key));
}
break;
@ -397,12 +438,22 @@ class XMLSecurityKey
private function encryptSymmetric($data)
{
$this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cryptParams['cipher']));
$data = $this->padISO10126($data, $this->cryptParams['blocksize']);
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
$authTag = null;
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) {
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms');
}
$authTag = openssl_random_pseudo_bytes(self::AUTHTAG_LENGTH);
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag);
} else {
$data = $this->padISO10126($data, $this->cryptParams['blocksize']);
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
}
if (false === $encrypted) {
throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string());
}
return $this->iv . $encrypted;
return $this->iv . $encrypted . $authTag;
}
/**
@ -416,11 +467,24 @@ class XMLSecurityKey
$iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']);
$this->iv = substr($data, 0, $iv_length);
$data = substr($data, $iv_length);
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
$authTag = null;
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) {
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms');
}
// obtain and remove the authentication tag
$offset = 0 - self::AUTHTAG_LENGTH;
$authTag = substr($data, $offset);
$data = substr($data, 0, $offset);
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag);
} else {
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
}
if (false === $decrypted) {
throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string());
}
return $this->unpadISO10126($decrypted);
return null !== $authTag ? $decrypted : $this->unpadISO10126($decrypted);
}
/**

View File

@ -2,7 +2,7 @@
/**
* xmlseclibs.php
*
* Copyright (c) 2007-2018, Robert Richards <rrichards@cdatazone.org>.
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -35,9 +35,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*
* @author Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2018 Robert Richards <rrichards@cdatazone.org>
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version 3.0.3-dev
* @version 3.1.1
*/
$xmlseclibs_srcdir = dirname(__FILE__) . '/src/';