Add SAML support to your PHP software using this library.
Forget those complicated libraries and use this open source library provided
and supported by OneLogin Inc.
Warning
-------
This version is compatible with PHP >=7.3 and 8.X and does not include xmlseclibs (you will need to install it via composer, dependency described in composer.json)
Security Guidelines
-------------------
If you believe you have discovered a security vulnerability in this toolkit, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
Why add SAML support to my software?
------------------------------------
SAML is an XML-based standard for web browser single sign-on and is defined by
the OASIS Security Services Technical Committee. The standard has been around
since 2002, but lately it is becoming popular due its advantages:
* **Usability** - One-click access from portals or intranets, deep linking,
password elimination and automatically renewing sessions make life
easier for the user.
* **Security** - Based on strong digital signatures for authentication and
integrity, SAML is a secure single sign-on protocol that the largest
and most security conscious enterprises in the world rely on.
* **Speed** - SAML is fast. One browser redirect is all it takes to securely
sign a user into an application.
* **Phishing Prevention** - If you don’t have a password for an app, you
can’t be tricked into entering it on a fake login page.
* **IT Friendly** - SAML simplifies life for IT because it centralizes
authentication, provides greater visibility and makes directory
integration easier.
* **Opportunity** - B2B cloud vendor should support SAML to facilitate the
integration of their product.
General description
-------------------
OneLogin's SAML PHP toolkit let you build a SP (Service Provider) over
your PHP application and connect it to any IdP (Identity Provider).
* **saml2int** - Implements the SAML 2.0 Web Browser SSO Profile.
* **Session-less** - Forget those common conflicts between the SP and
the final app, the toolkit delegate session in the final app.
* **Easy to use** - Programmer will be allowed to code high-level and
low-level programming, 2 easy to use APIs are available.
* **Tested** - Thoroughly tested.
* **Popular** - OneLogin's customers use it. Many PHP SAML plugins uses it.
Integrate your PHP toolkit at OneLogin using this guide: [https://developers.onelogin.com/page/saml-toolkit-for-php](https://developers.onelogin.com/page/saml-toolkit-for-php)
Installation
------------
### Dependencies ###
*`php >= 5.4` and some core extensions like `php-xml`, `php-date`, `php-zlib`.
*`openssl`. Install the openssl library. It handles x509 certificates.
*`gettext`. Install that library and its php driver. It handles translations.
*`curl`. Install that library and its php driver if you plan to use the IdP Metadata parser.
### Code ###
#### 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:
* https://github.com/onelogin/php-saml/releases
Search for 3.X.X releases
Copy the core of the library inside the php application. (each application has its
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.
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
In order to import the saml toolkit to your current php project, execute
```
composer require onelogin/php-saml
```
Remember to select the 3.X.X branch
After installation has completed you will find at the `vendor/` folder a new folder named `onelogin` and inside the `php-saml`. Make sure you are including the autoloader provided by composer. It can be found at `vendor/autoload.php`.
**Important** In this option, the x509 certs must be stored at `vendor/onelogin/php-saml/certs`
and settings file stored at `vendor/onelogin/php-saml`.
Your settings are at risk of being deleted when updating packages using `composer update` or similar commands. So it is **highly** recommended that instead of using settings files, you pass the settings as an array directly to the constructor (explained later in this document). If you do not use this approach your settings are at risk of being deleted when updating packages using `composer update` or similar commands.
Compatibility
-------------
This 4.X.X supports PHP >=7.3 .
It is not compatible with PHP5.6 or PHP7.0.
Namespaces
----------
If you are using the library with a framework like Symfony that contains
namespaces, remember that calls to the class must be done by adding a backslash (`\`) to the
start, for example to use the static method getSelfURLNoQuery use:
\OneLogin\Saml2\Utils::getSelfURLNoQuery()
Security warning
----------------
In production, the `strict` parameter **MUST** be set as `"true"` and the
`signatureAlgorithm` and `digestAlgorithm` under `security` must be set to
something other than SHA1 (see https://shattered.io/ ). Otherwise your
environment is not secure and will be exposed to attacks.
In production also we highly recommended to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment.
Some implementations uses the RelayState parameter as a way to control the flow when SSO and SLO succeeded. So basically the
user is redirected to the value of the RelayState.
If you are using Signature Validation on the HTTP-Redirect binding, you will have the RelayState value integrity covered, otherwise, and
on HTTP-POST binding, you can't trust the RelayState so before
executing the validation, you need to verify that its value belong
a trusted and expected URL.
Read more about Open Redirect [CWE-601](https://cwe.mitre.org/data/definitions/601.html).
### Avoiding Reply attacks ###
A reply attack is basically try to reuse an intercepted valid SAML Message in order to impersonate a SAML action (SSO or SLO).
SAML Messages have a limited timelife (NotBefore, NotOnOrAfter) that
make harder this kind of attacks, but they are still possible.
In order to avoid them, the SP can keep a list of SAML Messages or Assertion IDs alredy valdidated and processed. Those values only need
to be stored the amount of time of the SAML Message life time, so
we don't need to store all processed message/assertion Ids, but the most recent ones.
The OneLogin_Saml2_Auth class contains the [getLastRequestID](https://github.com/onelogin/php-saml/blob/b8214b74dd72960fa6aa88ab454667c64cea935c/src/Saml2/Auth.php#L657), [getLastMessageId](https://github.com/onelogin/php-saml/blob/b8214b74dd72960fa6aa88ab454667c64cea935c/src/Saml2/Auth.php#L762) and [getLastAssertionId](https://github.com/onelogin/php-saml/blob/b8214b74dd72960fa6aa88ab454667c64cea935c/src/Saml2/Auth.php#L770) methods to retrieve the IDs
Checking that the ID of the current Message/Assertion does not exists in the list of the ones already processed will prevent reply
* Instead of use the whole x509cert you can use a fingerprint in order to
* validate a SAMLResponse, but we don't recommend to use that
* method on production since is exploitable by a collision attack.
* (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
* or add for example the -sha256 , -sha384 or -sha512 parameter)
*
* If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
* let the toolkit know which algorithm was used. Possible values: sha1, sha256, sha384 or sha512
* 'sha1' is the default value.
*
* Notice that if you want to validate any SAML Message sent by the HTTP-Redirect binding, you
* will need to provide the whole x509cert.
*/
// 'certFingerprint' => '',
// 'certFingerprintAlgorithm' => 'sha1',
/* In some scenarios the IdP uses different certificates for
* signing/encryption, or is under key rollover phase and
* more than one certificate is published on IdP metadata.
* In order to handle that the toolkit offers that parameter.
* (when used, 'x509cert' and 'certFingerprint' values are
* ignored).
*/
// 'x509certMulti' => array(
// 'signing' => array(
// 0 => '<cert1-string>',
// ),
// 'encryption' => array(
// 0 => '<cert2-string>',
// )
// ),
),
);
```
In addition to the required settings data (IdP, SP), there is extra
information that could be defined. In the same way that a template exists
for the basic info, there is a template for that advanced info located
at the base folder of the toolkit and named `advanced_settings_example.php`
that you can copy and rename it as `advanced_settings.php`
```php
<?php
$advancedSettings = array(
// Compression settings
'compress' => array(
'requests' => true,
'responses' => true
),
// Security settings
'security' => array(
/** signatures and encryptions offered */
// Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
// will be encrypted.
'nameIdEncrypted' => false,
// Indicates whether the <samlp:AuthnRequest> messages sent by this SP
// will be signed. [Metadata of the SP will offer this info]
'authnRequestsSigned' => false,
// Indicates whether the <samlp:logoutRequest> messages sent by this SP
// will be signed.
'logoutRequestSigned' => false,
// Indicates whether the <samlp:logoutResponse> messages sent by this SP
// will be signed.
'logoutResponseSigned' => false,
/* Sign the Metadata
False || True (use sp certs) || array (
'keyFileName' => 'metadata.key',
'certFileName' => 'metadata.crt'
)
|| array (
'x509cert' => '',
'privateKey' => ''
)
*/
'signMetadata' => false,
/** signatures and encryptions required **/
// Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>
// and <samlp:LogoutResponse> elements received by this SP to be signed.
'wantMessagesSigned' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be encrypted.
'wantAssertionsEncrypted' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be signed. [Metadata of the SP will offer this info]
'wantAssertionsSigned' => false,
// Indicates a requirement for the NameID element on the SAMLResponse
// received by this SP to be present.
'wantNameId' => true,
// Indicates a requirement for the NameID received by
// this SP to be encrypted.
'wantNameIdEncrypted' => false,
// Authentication context.
// Set to false and no AuthContext will be sent in the AuthNRequest.
// Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'.
// Set an array with the possible auth context values: array('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509').
'requestedAuthnContext' => false,
// Indicates if the SP will validate all received xmls.
// (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
'wantXMLValidation' => true,
// If true, SAMLResponses with an empty value at its Destination
// 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, the toolkit will not raised an error when the Statement Element
// contain atribute elements with name duplicated
'allowRepeatAttributeName' => false,
// If true, SAMLResponses with an InResponseTo value will be rejectd if not
// AuthNRequest ID provided to the validation method.
After that line we will be able to use the classes (and their methods) of the
toolkit (because the external and the Saml2 libraries files are loaded).
That toolkit depends on [xmlseclibs](https://github.com/robrichards/xmlseclibs) 3.X.X branch,
you will need to get its code and place on your project and reuse the _toolkit_loader.php
file to include xmlseclibs as well.
#### Initiate SSO ####
In order to send an `AuthNRequest` to the IdP:
```php
<?php
define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once(TOOLKIT_PATH . '_toolkit_loader.php'); // We load the SAML2 lib
$auth = new OneLogin\Saml2\Auth(); // Constructor of the SP, loads settings.php
// and advanced_settings.php
$auth->login(); // Method that sent the AuthNRequest
```
The `AuthNRequest` will be sent signed or unsigned based on the security info
of the `advanced_settings.php` (`'authnRequestsSigned'`).
The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. If we do not set a `'url'` param in the login method and we are using the default ACS provided by the toolkit (`endpoints/acs.php`), then the ACS endpoint will redirect the user to the file that launched the SSO request.
We can set a `'returnTo'` url to change the workflow and redirect the user to the other PHP file.
echo '<p><ahref="?slo">Logout</a></p>'; // Print some links with possible
} else { // actions
echo '<p><ahref="?sso">Login</a></p>';
echo '<p><ahref="?sso2">Login and access to attrs.php page</a></p>';
}
```
#### URL-guessing methods ####
php-saml toolkit uses a bunch of methods in OneLogin\Saml2\Utils that try to guess the URL where the SAML messages are processed.
*`getSelfHost` Returns the current host.
*`getSelfPort` Return the port number used for the request
*`isHTTPS` Checks if the protocol is https or http.
*`getSelfURLhost` Returns the protocol + the current host + the port (if different than common ports).
*`getSelfURL` Returns the URL of the current host + current view + query.
*`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 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).
Also a developer can use `setSelfProtocol`, `setSelfHost`, `setSelfPort` and `getBaseURLPath` to define a specific value to be returned by `isHTTPS`, `getSelfHost`, `getSelfPort` and `getBaseURLPath`. And define a `setBasePath` to be used on the `getSelfURL` and `getSelfRoutedURLNoQuery` to replace the data extracted from `$_SERVER["REQUEST_URI"]`.
At the settings the developer will be able to set a `'baseurl'` parameter that automatically will use `setBaseURL` to set values for `setSelfProtocol`, `setSelfHost`, `setSelfPort` and `setBaseURLPath`.
### Working behind load balancer ###
Is possible that asserting request URL and Destination attribute of SAML response fails when working behind load balancer with SSL offload.
You should be able to workaround this by configuring your server so that it is aware of the proxy and returns the original url when requested.
Or by using the method described on the previous section.
### SP Key rollover ###
If you plan to update the SP x509cert and privateKey you can define the new x509cert as `$settings['sp']['x509certNew']` and it will be
published on the SP metadata so Identity Providers can read them and get ready for rollover.
### IdP with multiple certificates ###
In some scenarios the IdP uses different certificates for
signing/encryption, or is under key rollover phase and more than one certificate is published on IdP metadata.
In order to handle that the toolkit offers the `$settings['idp']['x509certMulti']` parameter.
When that parameter is used, `'x509cert'` and `'certFingerprint'` values will be ignored by the toolkit.
The `x509certMulti` is an array with 2 keys:
-`signing`. An array of certs that will be used to validate IdP signature
-`encryption` An array with one unique cert that will be used to encrypt data to be sent to the IdP
### Replay attacks ###
In order to avoid replay attacks, you can store the ID of the SAML messages already processed, to avoid processing them twice. Since the Messages expires and will be invalidated due that fact, you don't need to store those IDs longer than the time frame that you currently accepting.
Get the ID of the last processed message/assertion with the `getLastMessageId`/`getLastAssertionId` methods of the Auth object.
### Main classes and methods ###
Described below are the main classes and methods that can be invoked.
#### Saml2 library ####
Lets describe now the classes and methods of the SAML2 library.
##### OneLogin\Saml2\Auth - Auth.php #####
Main class of OneLogin PHP Toolkit
*`Auth` - Initializes the SP SAML instance
*`login` - Initiates the SSO process.
*`logout` - Initiates the SLO process.
*`processResponse` - Process the SAML Response sent by the IdP.
*`processSLO` - Process the SAML Logout Response / Logout Request sent by the
IdP.
*`redirectTo` - Redirects the user to the url past by parameter or to the url
that we defined in our SSO Request.
*`isAuthenticated` - Checks if the user is authenticated or not.
*`getAttributes` - Returns the set of SAML attributes.
*`getAttribute` - Returns the requested SAML attribute
*`getNameId` - Returns the nameID
*`getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP.
*`getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String.
*`getNameIdSPNameQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String.
*`getSessionIndex` - Gets the SessionIndex from the AuthnStatement.
*`getErrors` - Returns if there were any error
*`getSSOurl` - Gets the SSO url.
*`getSLOurl` - Gets the SLO url.
*`getLastRequestID` - The ID of the last Request SAML message generated.
*`buildRequestSignature` - Generates the Signature for a SAML Request
*`buildResponseSignature` - Generates the Signature for a SAML Response
*`getSettings` - Returns the settings info
*`setStrict` - Set the strict mode active/disable
*`getLastRequestID` - Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider.
*`getLastRequestXML` - Returns the most recently-constructed/processed XML SAML request (AuthNRequest, LogoutRequest)
*`getLastResponseXML` - Returns the most recently-constructed/processed XML SAML response (SAMLResponse, LogoutResponse). If the SAMLResponse had an encrypted assertion, decrypts it.
The class does not validate in any way the URL that is introduced on methods like parseRemoteXML in order to retrieve the remove XML. Usually is the same administrator that handles the Service Provider the ones that set the URL that should belong to a trusted third-party IdP.
But there are other scenarios, like a SAAS app where the administrator of the app delegates on other administrators. In such case, extra protection should be taken in order to validate such URL inputs and avoid attacks like SSRF.