validateXML() public static method

It will parse the string into a DOM document and validate this document against the schema.
public static validateXML ( string | DOMDocument $xml, string $schema, boolean $debug = false ) : string | DOMDocument
$xml string | DOMDocument The XML string or document which should be validated.
$schema string The schema filename which should be used.
$debug boolean To disable/enable the debug mode
return string | DOMDocument $dom string that explains the problem or the DOMDocument
Example #1
0
 /**
  * Checks if the Logout Request recieved is valid.
  *
  * @return boolean If the Logout Request is or not valid
  */
 public function isValid($retrieveParametersFromServer = false)
 {
     $this->_error = null;
     try {
         $dom = new DOMDocument();
         $dom = OneLogin_Saml2_Utils::loadXML($dom, $this->_logoutRequest);
         $idpData = $this->_settings->getIdPData();
         $idPEntityId = $idpData['entityId'];
         if ($this->_settings->isStrict()) {
             $security = $this->_settings->getSecurityData();
             if ($security['wantXMLValidation']) {
                 $res = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
                 if (!$res instanceof DOMDocument) {
                     throw new Exception("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd");
                 }
             }
             $currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
             // Check NotOnOrAfter
             if ($dom->documentElement->hasAttribute('NotOnOrAfter')) {
                 $na = OneLogin_Saml2_Utils::parseSAML2Time($dom->documentElement->getAttribute('NotOnOrAfter'));
                 if ($na <= time()) {
                     throw new Exception('Timing issues (please check your clock settings)');
                 }
             }
             // Check destination
             if ($dom->documentElement->hasAttribute('Destination')) {
                 $destination = $dom->documentElement->getAttribute('Destination');
                 if (!empty($destination)) {
                     if (strpos($destination, $currentURL) === false) {
                         throw new Exception("The LogoutRequest was received at {$currentURL} instead of {$destination}");
                     }
                 }
             }
             $nameId = $this->getNameId($dom, $this->_settings->getSPkey());
             // Check issuer
             $issuer = $this->getIssuer($dom);
             if (!empty($issuer) && $issuer != $idPEntityId) {
                 throw new Exception("Invalid issuer in the Logout Request");
             }
             if ($security['wantMessagesSigned']) {
                 if (!isset($_GET['Signature'])) {
                     throw new Exception("The Message of the Logout Request is not signed and the SP require it");
                 }
             }
         }
         if (isset($_GET['Signature'])) {
             if (!isset($_GET['SigAlg'])) {
                 $signAlg = XMLSecurityKey::RSA_SHA1;
             } else {
                 $signAlg = $_GET['SigAlg'];
             }
             if ($retrieveParametersFromServer) {
                 $signedQuery = 'SAMLRequest=' . OneLogin_Saml2_Utils::extractOriginalQueryParam('SAMLRequest');
                 if (isset($_GET['RelayState'])) {
                     $signedQuery .= '&RelayState=' . OneLogin_Saml2_Utils::extractOriginalQueryParam('RelayState');
                 }
                 $signedQuery .= '&SigAlg=' . OneLogin_Saml2_Utils::extractOriginalQueryParam('SigAlg');
             } else {
                 $signedQuery = 'SAMLRequest=' . urlencode($_GET['SAMLRequest']);
                 if (isset($_GET['RelayState'])) {
                     $signedQuery .= '&RelayState=' . urlencode($_GET['RelayState']);
                 }
                 $signedQuery .= '&SigAlg=' . urlencode($signAlg);
             }
             if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
                 throw new Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required');
             }
             $cert = $idpData['x509cert'];
             $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
             $objKey->loadKey($cert, false, true);
             if ($signAlg != XMLSecurityKey::RSA_SHA1) {
                 try {
                     $objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
                 } catch (Exception $e) {
                     throw new Exception('Invalid signAlg in the recieved Logout Request');
                 }
             }
             if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
                 throw new Exception('Signature validation failed. Logout Request rejected');
             }
         }
         return true;
     } catch (Exception $e) {
         $this->_error = $e->getMessage();
         $debug = $this->_settings->isDebugActive();
         if ($debug) {
             echo $this->_error;
         }
         return false;
     }
 }
Example #2
0
 /**
  * Tests the validateXML method of the OneLogin_Saml2_Utils
  *
  * @covers OneLogin_Saml2_Utils::validateXML
  */
 public function testValidateXML()
 {
     $metadataUnloaded = '<xml><EntityDescriptor>';
     $this->assertEquals(OneLogin_Saml2_Utils::validateXML($metadataUnloaded, 'saml-schema-metadata-2.0.xsd'), 'unloaded_xml');
     $metadataInvalid = file_get_contents(TEST_ROOT . '/data/metadata/noentity_metadata_settings1.xml');
     $this->assertEquals(OneLogin_Saml2_Utils::validateXML($metadataInvalid, 'saml-schema-metadata-2.0.xsd'), 'invalid_xml');
     $metadataExpired = file_get_contents(TEST_ROOT . '/data/metadata/expired_metadata_settings1.xml');
     $res = OneLogin_Saml2_Utils::validateXML($metadataExpired, 'saml-schema-metadata-2.0.xsd');
     $this->assertTrue($res instanceof DOMDocument);
     $metadataOk = file_get_contents(TEST_ROOT . '/data/metadata/metadata_settings1.xml');
     $res2 = OneLogin_Saml2_Utils::validateXML($metadataOk, 'saml-schema-metadata-2.0.xsd');
     $this->assertTrue($res instanceof DOMDocument);
     $dom = new DOMDocument();
     $dom->load($metadataOk);
     $res2 = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-metadata-2.0.xsd');
     $this->assertTrue($res instanceof DOMDocument);
 }
Example #3
0
 /**
  * Validates an XML SP Metadata.
  *
  * @param string $xml Metadata's XML that will be validate
  *
  * @return Array The list of found errors
  */
 public function validateMetadata($xml)
 {
     assert('is_string($xml)');
     $errors = array();
     $res = OneLogin_Saml2_Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug);
     if (!$res instanceof DOMDocument) {
         $errors[] = $res;
     } else {
         $dom = $res;
         $element = $dom->documentElement;
         if ($element->tagName !== 'md:EntityDescriptor') {
             $errors[] = 'noEntityDescriptor_xml';
         } else {
             $validUntil = $cacheDuration = $expireTime = null;
             if ($element->hasAttribute('validUntil')) {
                 $validUntil = OneLogin_Saml2_Utils::parseSAML2Time($element->getAttribute('validUntil'));
             }
             if ($element->hasAttribute('cacheDuration')) {
                 $cacheDuration = $element->getAttribute('cacheDuration');
             }
             $expireTime = OneLogin_Saml2_Utils::getExpireTime($cacheDuration, $validUntil);
             if (isset($expireTime) && time() > $expireTime) {
                 $errors[] = 'expired_xml';
             }
         }
     }
     // TODO: Support Metadata Sign Validation
     return $errors;
 }
Example #4
0
 /**
  * Determines if the SAML LogoutResponse is valid
  *
  * @param string $requestId The ID of the LogoutRequest sent by this SP to the IdP
  *
  * @throws Exception
  * @return bool Returns if the SAML LogoutResponse is or not valid
  */
 public function isValid($requestId = null)
 {
     $this->_error = null;
     try {
         $idpData = $this->_settings->getIdPData();
         $idPEntityId = $idpData['entityId'];
         if ($this->_settings->isStrict()) {
             $res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
             if (!$res instanceof DOMDocument) {
                 throw new Exception("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd");
             }
             $security = $this->_settings->getSecurityData();
             // Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided
             if (isset($requestId) && $this->document->documentElement->hasAttribute('InResponseTo')) {
                 $inResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
                 if ($requestId != $inResponseTo) {
                     throw new Exception("The InResponseTo of the Logout Response: {$inResponseTo}, does not match the ID of the Logout request sent by the SP: {$requestId}");
                 }
             }
             // Check issuer
             $issuer = $this->getIssuer();
             if (empty($issuer) || $issuer != $idPEntityId) {
                 throw new Exception("Invalid issuer in the Logout Response");
             }
             $currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
             // Check destination
             if ($this->document->documentElement->hasAttribute('Destination')) {
                 $destination = $this->document->documentElement->getAttribute('Destination');
                 if (!empty($destination)) {
                     if (strpos($destination, $currentURL) === false) {
                         throw new Exception("The LogoutResponse was received at {$currentURL} instead of {$destination}");
                     }
                 }
             }
             if ($security['wantMessagesSigned']) {
                 if (!isset($_GET['Signature'])) {
                     throw new Exception("The Message of the Logout Response is not signed and the SP requires it");
                 }
             }
         }
         if (isset($_GET['Signature'])) {
             if (!isset($_GET['SigAlg'])) {
                 $signAlg = XMLSecurityKey::RSA_SHA1;
             } else {
                 $signAlg = $_GET['SigAlg'];
             }
             $signedQuery = 'SAMLResponse=' . urlencode($_GET['SAMLResponse']);
             if (isset($_GET['RelayState'])) {
                 $signedQuery .= '&RelayState=' . urlencode($_GET['RelayState']);
             }
             $signedQuery .= '&SigAlg=' . urlencode($signAlg);
             if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
                 throw new Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required');
             }
             $cert = $idpData['x509cert'];
             $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
             $objKey->loadKey($cert, false, true);
             if ($signAlg != XMLSecurityKey::RSA_SHA1) {
                 try {
                     $objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
                 } catch (Exception $e) {
                     throw new Exception('Invalid signAlg in the recieved Logout Response');
                 }
             }
             if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
                 throw new Exception('Signature validation failed. Logout Response rejected');
             }
         }
         return true;
     } catch (Exception $e) {
         $this->_error = $e->getMessage();
         $debug = $this->_settings->isDebugActive();
         if ($debug) {
             echo $this->_error;
         }
         return false;
     }
 }
Example #5
0
 /**
  * Determines if the SAML Response is valid using the certificate.
  *
  * @param string $requestId The ID of the AuthNRequest sent by this SP to the IdP
  *
  * @throws Exception
  * @return bool 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 = array();
         if ($this->encrypted) {
             $signNodes = $this->decryptedDocument->getElementsByTagName('Signature');
         } else {
             $signNodes = $this->document->getElementsByTagName('Signature');
         }
         foreach ($signNodes as $signNode) {
             $signedElements[] = $signNode->parentNode->localName;
         }
         if (!empty($signedElements)) {
             // Check SignedElements
             if (!$this->validateSignedElements($signedElements)) {
                 throw new Exception('Found an unexpected Signature Element. SAML Response rejected');
             }
         }
         if ($this->_settings->isStrict()) {
             $security = $this->_settings->getSecurityData();
             if ($security['wantXMLValidation']) {
                 $res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
                 if (!$res instanceof DOMDocument) {
                     throw new Exception("Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd");
                 }
             }
             $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 == 0) {
                     throw new Exception("The NameID of the Response is not encrypted and the SP requires it");
                 }
             }
             // Validate Asserion timestamps
             $validTimestamps = $this->validateTimestamps();
             if (!$validTimestamps) {
                 throw new Exception('Timing issues (please check your clock settings)');
             }
             // 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 = $this->document->documentElement->getAttribute('Destination');
                 if (!empty($destination)) {
                     if (strpos($destination, $currentURL) !== 0) {
                         $currentURLrouted = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
                         if (strpos($destination, $currentURLrouted) !== 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) {
                 if (empty($issuer) || $issuer != $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'] && !in_array('Assertion', $signedElements)) {
                 throw new Exception("The Assertion of the Response is not signed and the SP requires it");
             }
             if ($security['wantMessagesSigned'] && !in_array('Response', $signedElements)) {
                 throw new Exception("The Message of the Response is not signed and the SP requires it");
             }
         }
         if (!empty($signedElements)) {
             $cert = $idpData['x509cert'];
             $fingerprint = $idpData['certFingerprint'];
             $fingerprintalg = $idpData['certFingerprintAlgorithm'];
             // Only validates the first signed element
             if (in_array('Response', $signedElements)) {
                 $documentToValidate = $this->document;
             } else {
                 $documentToValidate = $signNodes->item(0)->parentNode;
                 if ($this->encrypted) {
                     $encryptedIDNodes = OneLogin_Saml2_Utils::query($this->decryptedDocument, '/samlp:Response/saml:EncryptedAssertion/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 (!OneLogin_Saml2_Utils::validateSign($documentToValidate, $cert, $fingerprint, $fingerprintalg)) {
                 throw new Exception('Signature validation failed. SAML Response rejected');
             }
         } else {
             throw new Exception('No Signature found. SAML Response rejected');
         }
         return true;
     } catch (Exception $e) {
         $this->_error = $e->getMessage();
         $debug = $this->_settings->isDebugActive();
         if ($debug) {
             echo $this->_error;
         }
         return false;
     }
 }
Example #6
0
 /**
  * Tests the builder method of the OneLogin_Saml2_Metadata
  *
  * @covers OneLogin_Saml2_Metadata::builder
  */
 public function testBuilderWithAttributeConsumingServiceWithMultipleAttributeValue()
 {
     $settingsDir = TEST_ROOT . '/settings/';
     include $settingsDir . 'settings4.php';
     $settings = new OneLogin_Saml2_Settings($settingsInfo);
     $spData = $settings->getSPData();
     $security = $settings->getSecurityData();
     $organization = $settings->getOrganization();
     $contacts = $settings->getContacts();
     $metadata = OneLogin_Saml2_Metadata::builder($spData, $security['authnRequestsSigned'], $security['wantAssertionsSigned'], null, null, $contacts, $organization);
     $this->assertContains('<md:ServiceName xml:lang="en">Service Name</md:ServiceName>', $metadata);
     $this->assertContains('<md:ServiceDescription xml:lang="en">Service Description</md:ServiceDescription>', $metadata);
     $this->assertContains('<md:RequestedAttribute Name="urn:oid:0.9.2342.19200300.100.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="uid" isRequired="true" />', $metadata);
     $this->assertContains('<saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">userType</saml:AttributeValue>', $metadata);
     $this->assertContains('<saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">admin</saml:AttributeValue>', $metadata);
     $result = \OneLogin_Saml2_Utils::validateXML($metadata, 'saml-schema-metadata-2.0.xsd');
     $this->assertInstanceOf('DOMDocument', $result);
 }