diff --git a/src/SAML2/DOMDocumentFactory.php b/src/SAML2/DOMDocumentFactory.php index 811ae10b6..f69a49e4b 100644 --- a/src/SAML2/DOMDocumentFactory.php +++ b/src/SAML2/DOMDocumentFactory.php @@ -30,15 +30,27 @@ public static function fromString(string $xml) : DOMDocument { if (trim($xml) === '') { throw InvalidArgumentException::invalidType('non-empty string', $xml); + } elseif (preg_match('/<(\s*)!(\s*)DOCTYPE/', $xml)) { + throw new RuntimeException( + 'Dangerous XML detected, DOCTYPE nodes are not allowed in the XML body' + ); } elseif (PHP_VERSION_ID < 80000) { $entityLoader = libxml_disable_entity_loader(true); + } else { + libxml_set_external_entity_loader(null); } $internalErrors = libxml_use_internal_errors(true); libxml_clear_errors(); $domDocument = self::create(); - $options = LIBXML_DTDLOAD | LIBXML_DTDATTR | LIBXML_NONET | LIBXML_PARSEHUGE; + $options = LIBXML_NONET | LIBXML_PARSEHUGE; + + /* LIBXML_NO_XXE available from PHP 8.4 */ + if (defined('LIBXML_NO_XXE')) { + $options |= LIBXML_NO_XXE; + } + if (defined('LIBXML_COMPACT')) { $options |= LIBXML_COMPACT; } diff --git a/tests/SAML2/DOMDocumentFactoryTest.php b/tests/SAML2/DOMDocumentFactoryTest.php index fa0561a33..1834a22bf 100644 --- a/tests/SAML2/DOMDocumentFactoryTest.php +++ b/tests/SAML2/DOMDocumentFactoryTest.php @@ -99,6 +99,22 @@ public function testStringThatContainsDocTypeIsNotAccepted() : void } + /** + * @group domdocument + * @return void + */ + public function testStringThatContainsDocTypeIsNotAccepted2(): void + { + $xml = ' + %exfiltrate;]> + y'; + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'Dangerous XML detected, DOCTYPE nodes are not allowed in the XML body', + ); + DOMDocumentFactory::fromString($xml); + } + /** * @group domdocument * @return void