/** * Tests the loadXML method of the OneLogin_Saml2_Utils * * @covers OneLogin_Saml2_Utils::loadXML */ public function testXMLAttacks() { $dom = new DOMDocument(); $attackXXE = '<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>'; try { $res = OneLogin_Saml2_Utils::loadXML($dom, $attackXXE); $this->assertTrue(false); } catch (Exception $e) { $this->assertEquals('Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks', $e->getMessage()); } $xmlWithDTD = '<?xml version="1.0"?> <!DOCTYPE results [ <!ELEMENT results (result+)> <!ELEMENT result (#PCDATA)> ]> <results> <result>test</result> </results>'; $res2 = OneLogin_Saml2_Utils::loadXML($dom, $xmlWithDTD); $this->assertTrue($res2 instanceof DOMDocument); $attackXEE = '<?xml version="1.0"?> <!DOCTYPE results [<!ENTITY harmless "completely harmless">]> <results> <result>This result is &harmless;</result> </results>'; try { $res3 = OneLogin_Saml2_Utils::loadXML($dom, $attackXEE); $this->assertTrue(false); } catch (Exception $e) { $this->assertEquals('Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks', $e->getMessage()); } }
/** * Constructs a Logout Response object (Initialize params from settings and if provided * load the Logout Response. * * @param OneLogin_Saml2_Settings $settings Settings. * @param string|null $response An UUEncoded SAML Logout response from the IdP. */ public function __construct(OneLogin_Saml2_Settings $settings, $response = null) { $this->_settings = $settings; if ($response) { $decoded = base64_decode($response); $this->_logoutResponse = gzinflate($decoded); $this->document = new DOMDocument(); $this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->_logoutResponse); } }
/** * Constructs the SAML Response object. * * @param OneLogin_Saml2_Settings $settings Settings. * @param string $response A UUEncoded SAML response from the IdP. */ public function __construct(OneLogin_Saml2_Settings $settings, $response) { $this->_settings = $settings; $this->response = base64_decode($response); $this->document = new DOMDocument(); $this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->response); // Quick check for the presence of EncryptedAssertion $encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion'); if ($encryptedAssertionNodes->length !== 0) { $this->decryptedDocument = clone $this->document; $this->encrypted = true; $this->_decryptAssertion($this->decryptedDocument); } }
/** * Constructs a Logout Response object (Initialize params from settings and if provided * load the Logout Response. * * @param OneLogin_Saml2_Settings $settings Settings. * @param string|null $response An UUEncoded SAML Logout response from the IdP. */ public function __construct(OneLogin_Saml2_Settings $settings, $response = null) { $this->_settings = $settings; $baseURL = $this->_settings->getBaseURL(); if (!empty($baseURL)) { OneLogin_Saml2_Utils::setBaseURL($baseURL); } if ($response) { $decoded = base64_decode($response); $inflated = @gzinflate($decoded); if ($inflated != false) { $this->_logoutResponse = $inflated; } else { $this->_logoutResponse = $decoded; } $this->document = new DOMDocument(); $this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->_logoutResponse); } }
/** * Constructs the SAML Response object. * * @param OneLogin_Saml2_Settings $settings Settings. * @param string $response A UUEncoded SAML response from the IdP. */ public function __construct(OneLogin_Saml2_Settings $settings, $response) { $this->_settings = $settings; $this->response = base64_decode($response); $this->document = new DOMDocument(); echo "1********<br />"; print_r($this->document); echo "2********<br />"; $this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->response); if (!$this->document) { throw new Exception('SAML Response could not be processed'); } // Quick check for the presence of EncryptedAssertion $encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion'); if ($encryptedAssertionNodes->length !== 0) { $this->decryptedDocument = clone $this->document; $this->encrypted = true; $this->_decryptAssertion($this->decryptedDocument); } }
/** * 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; } }
/** * 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($res2 instanceof DOMDocument); $metadataBadOrder = file_get_contents(TEST_ROOT . '/data/metadata/metadata_bad_order_settings1.xml'); $res3 = OneLogin_Saml2_Utils::validateXML($metadataBadOrder, 'saml-schema-metadata-2.0.xsd'); $this->assertFalse($res3 instanceof DOMDocument); $metadataSigned = file_get_contents(TEST_ROOT . '/data/metadata/signed_metadata_settings1.xml'); $res4 = OneLogin_Saml2_Utils::validateXML($metadataSigned, 'saml-schema-metadata-2.0.xsd'); $this->assertTrue($res4 instanceof DOMDocument); $dom = new DOMDocument(); OneLogin_Saml2_Utils::loadXML($dom, $metadataOk); $res5 = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-metadata-2.0.xsd'); $this->assertTrue($res5 instanceof DOMDocument); }
/** * Adds the x509 descriptors (sign/encriptation) to the metadata * The same cert will be used for sign/encrypt * * @param string $metadata SAML Metadata XML * @param string $cert x509 cert * * @return string Metadata with KeyDescriptors */ public static function addX509KeyDescriptors($metadata, $cert) { $xml = new DOMDocument(); $xml->preserveWhiteSpace = false; $xml->formatOutput = true; try { $xml = OneLogin_Saml2_Utils::loadXML($xml, $metadata); if (!$xml) { throw new Exception('Error parsing metadata'); } } catch (Exception $e) { throw new Exception('Error parsing metadata. ' . $e->getMessage()); } $formatedCert = OneLogin_Saml2_Utils::formatCert($cert, false); $x509Certificate = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'X509Certificate', $formatedCert); $keyData = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:X509Data'); $keyData->appendChild($x509Certificate); $keyInfo = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:KeyInfo'); $keyInfo->appendChild($keyData); $keyDescriptor = $xml->createElementNS(OneLogin_Saml2_Constants::NS_MD, "md:KeyDescriptor"); $SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0); $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild); $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild); $signing = $xml->getElementsByTagName('KeyDescriptor')->item(0); $signing->setAttribute('use', 'signing'); $encryption = $xml->getElementsByTagName('KeyDescriptor')->item(1); $encryption->setAttribute('use', 'encryption'); $signing->appendChild($keyInfo); $encryption->appendChild($keyInfo->cloneNode(true)); return $xml->saveXML(); }