OneLogin_Saml2_Response::isValid PHP Method

isValid() public method

Determines if the SAML Response is valid using the certificate.
public isValid ( string | null $requestId = null ) : boolean
$requestId string | null The ID of the AuthNRequest sent by this SP to the IdP
return boolean Validate the document
    public function isValid($requestId = null)
    {
        $this->_error = null;
        try {
            // Check SAML version
            if ($this->document->documentElement->getAttribute('Version') != '2.0') {
                throw new Exception('Unsupported SAML version');
            }
            if (!$this->document->documentElement->hasAttribute('ID')) {
                throw new Exception('Missing ID attribute on SAML Response');
            }
            $status = $this->checkStatus();
            $singleAssertion = $this->validateNumAssertions();
            if (!$singleAssertion) {
                throw new Exception('SAML Response must contain 1 assertion');
            }
            $idpData = $this->_settings->getIdPData();
            $idPEntityId = $idpData['entityId'];
            $spData = $this->_settings->getSPData();
            $spEntityId = $spData['entityId'];
            $signedElements = $this->processSignedElements();
            $responseTag = '{' . OneLogin_Saml2_Constants::NS_SAMLP . '}Response';
            $assertionTag = '{' . OneLogin_Saml2_Constants::NS_SAML . '}Assertion';
            $hasSignedResponse = in_array($responseTag, $signedElements);
            $hasSignedAssertion = in_array($assertionTag, $signedElements);
            if ($this->_settings->isStrict()) {
                $security = $this->_settings->getSecurityData();
                if ($security['wantXMLValidation']) {
                    $errorXmlMsg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd";
                    $res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
                    if (!$res instanceof DOMDocument) {
                        throw new Exception($errorXmlMsg);
                    }
                    # If encrypted, check also the decrypted document
                    if ($this->encrypted) {
                        $res = OneLogin_Saml2_Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
                        if (!$res instanceof DOMDocument) {
                            throw new Exception($errorXmlMsg);
                        }
                    }
                }
                $currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
                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)) {
                    if ($requestId != $responseInResponseTo) {
                        throw new Exception("The InResponseTo of the Response: {$responseInResponseTo}, does not match the ID of the AuthNRequest sent by the SP: {$requestId}");
                    }
                }
                if (!$this->encrypted && $security['wantAssertionsEncrypted']) {
                    throw new Exception("The assertion of the Response is not encrypted and the SP requires it");
                }
                if ($security['wantNameIdEncrypted']) {
                    $encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData');
                    if ($encryptedIdNodes->length != 1) {
                        throw new Exception("The NameID of the Response is not encrypted and the SP requires it");
                    }
                }
                // Validate Conditions element exists
                if (!$this->checkOneCondition()) {
                    throw new Exception("The Assertion must include a Conditions element");
                }
                // Validate Asserion timestamps
                $validTimestamps = $this->validateTimestamps();
                if (!$validTimestamps) {
                    throw new Exception('Timing issues (please check your clock settings)');
                }
                // Validate AuthnStatement element exists and is unique
                if (!$this->checkOneAuthnStatement()) {
                    throw new Exception("The Assertion must include an AuthnStatement element");
                }
                // EncryptedAttributes are not supported
                $encryptedAttributeNodes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute');
                if ($encryptedAttributeNodes->length > 0) {
                    throw new Exception("There is an EncryptedAttribute in the Response and this SP not support them");
                }
                // Check destination
                if ($this->document->documentElement->hasAttribute('Destination')) {
                    $destination = trim($this->document->documentElement->getAttribute('Destination'));
                    if (empty($destination)) {
                        throw new Exception("The response has an empty Destination value");
                    } else {
                        if (strpos($destination, $currentURL) !== 0) {
                            $currentURLNoRouted = OneLogin_Saml2_Utils::getSelfURLNoQuery();
                            if (strpos($destination, $currentURLNoRouted) !== 0) {
                                throw new Exception("The response was received at {$currentURL} instead of {$destination}");
                            }
                        }
                    }
                }
                // Check audience
                $validAudiences = $this->getAudiences();
                if (!empty($validAudiences) && !in_array($spEntityId, $validAudiences)) {
                    throw new Exception("{$spEntityId} is not a valid audience for this Response");
                }
                // Check the issuers
                $issuers = $this->getIssuers();
                foreach ($issuers as $issuer) {
                    $trimmedIssuer = trim($issuer);
                    if (empty($trimmedIssuer) || $trimmedIssuer !== $idPEntityId) {
                        throw new Exception("Invalid issuer in the Assertion/Response");
                    }
                }
                // Check the session Expiration
                $sessionExpiration = $this->getSessionNotOnOrAfter();
                if (!empty($sessionExpiration) && $sessionExpiration <= time()) {
                    throw new Exception("The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response");
                }
                // Check the SubjectConfirmation, at least one SubjectConfirmation must be valid
                $anySubjectConfirmation = false;
                $subjectConfirmationNodes = $this->_queryAssertion('/saml:Subject/saml:SubjectConfirmation');
                foreach ($subjectConfirmationNodes as $scn) {
                    if ($scn->hasAttribute('Method') && $scn->getAttribute('Method') != OneLogin_Saml2_Constants::CM_BEARER) {
                        continue;
                    }
                    $subjectConfirmationDataNodes = $scn->getElementsByTagName('SubjectConfirmationData');
                    if ($subjectConfirmationDataNodes->length == 0) {
                        continue;
                    } else {
                        $scnData = $subjectConfirmationDataNodes->item(0);
                        if ($scnData->hasAttribute('InResponseTo')) {
                            $inResponseTo = $scnData->getAttribute('InResponseTo');
                            if ($responseInResponseTo != $inResponseTo) {
                                continue;
                            }
                        }
                        if ($scnData->hasAttribute('Recipient')) {
                            $recipient = $scnData->getAttribute('Recipient');
                            if (!empty($recipient) && strpos($recipient, $currentURL) === false) {
                                continue;
                            }
                        }
                        if ($scnData->hasAttribute('NotOnOrAfter')) {
                            $noa = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotOnOrAfter'));
                            if ($noa <= time()) {
                                continue;
                            }
                        }
                        if ($scnData->hasAttribute('NotBefore')) {
                            $nb = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotBefore'));
                            if ($nb > time()) {
                                continue;
                            }
                        }
                        $anySubjectConfirmation = true;
                        break;
                    }
                }
                if (!$anySubjectConfirmation) {
                    throw new Exception("A valid SubjectConfirmation was not found on this Response");
                }
                if ($security['wantAssertionsSigned'] && !$hasSignedAssertion) {
                    throw new Exception("The Assertion of the Response is not signed and the SP requires it");
                }
                if ($security['wantMessagesSigned'] && !$hasSignedResponse) {
                    throw new Exception("The Message of the Response is not signed and the SP requires it");
                }
            }
            // Detect case not supported
            if ($this->encrypted) {
                $encryptedIDNodes = OneLogin_Saml2_Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID');
                if ($encryptedIDNodes->length > 0) {
                    throw new Exception('Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.');
                }
            }
            if (empty($signedElements) || !$hasSignedResponse && !$hasSignedAssertion) {
                throw new Exception('No Signature found. SAML Response rejected');
            } else {
                $cert = $idpData['x509cert'];
                $fingerprint = $idpData['certFingerprint'];
                $fingerprintalg = $idpData['certFingerprintAlgorithm'];
                # If find a Signature on the Response, validates it checking the original response
                if ($hasSignedResponse && !OneLogin_Saml2_Utils::validateSign($this->document, $cert, $fingerprint, $fingerprintalg, OneLogin_Saml2_Utils::RESPONSE_SIGNATURE_XPATH)) {
                    throw new Exception("Signature validation failed. SAML Response rejected");
                }
                # If find a Signature on the Assertion (decrypted assertion if was encrypted)
                $documentToCheckAssertion = $this->encrypted ? $this->decryptedDocument : $this->document;
                if ($hasSignedAssertion && !OneLogin_Saml2_Utils::validateSign($documentToCheckAssertion, $cert, $fingerprint, $fingerprintalg, OneLogin_Saml2_Utils::ASSERTION_SIGNATURE_XPATH)) {
                    throw new Exception("Signature validation failed. SAML Response rejected");
                }
            }
            return true;
        } catch (Exception $e) {
            $this->_error = $e->getMessage();
            $debug = $this->_settings->isDebugActive();
            if ($debug) {
                echo $this->_error;
            }
            return false;
        }
    }

Usage Example

示例#1
0
 /**
  * Process the SAML Response sent by the IdP.
  *
  * @param string $requestId The ID of the AuthNRequest sent by this SP to the IdP
  */
 public function processResponse($requestId = null)
 {
     $this->_errors = array();
     if (isset($_POST) && isset($_POST['SAMLResponse'])) {
         // AuthnResponse -- HTTP_POST Binding
         $response = new OneLogin_Saml2_Response($this->_settings, $_POST['SAMLResponse']);
         if ($response->isValid($requestId)) {
             $this->_attributes = $response->getAttributes();
             $this->_nameid = $response->getNameId();
             $this->_authenticated = true;
         } else {
             $this->_errors[] = 'invalid_response';
         }
     } else {
         $this->_errors[] = 'invalid_binding';
         throw new OneLogin_Saml2_Error('SAML Response not found, Only supported HTTP_POST Binding', OneLogin_Saml2_Error::SAML_RESPONSE_NOT_FOUND);
     }
 }
All Usage Examples Of OneLogin_Saml2_Response::isValid