Security.php 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Xml;
  7. use DOMDocument;
  8. /**
  9. * Class Security
  10. */
  11. class Security
  12. {
  13. /**
  14. * Heuristic scan to detect entity in XML
  15. *
  16. * @param string $xmlContent
  17. * @return bool
  18. */
  19. private function heuristicScan($xmlContent)
  20. {
  21. return strpos($xmlContent, '<!ENTITY') === false;
  22. }
  23. /**
  24. * Return true if PHP is running with PHP-FPM
  25. *
  26. * @return bool
  27. */
  28. private function isPhpFpm()
  29. {
  30. return substr(php_sapi_name(), 0, 3) === 'fpm';
  31. }
  32. /**
  33. * Security check loaded XML document
  34. *
  35. * @param string $xmlContent
  36. * @return bool
  37. *
  38. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  39. */
  40. public function scan($xmlContent)
  41. {
  42. /**
  43. * If running with PHP-FPM we perform an heuristic scan
  44. * We cannot use libxml_disable_entity_loader because of this bug
  45. * @see https://bugs.php.net/bug.php?id=64938
  46. */
  47. if ($this->isPhpFpm()) {
  48. return $this->heuristicScan($xmlContent);
  49. }
  50. $document = new DOMDocument();
  51. $loadEntities = libxml_disable_entity_loader(true);
  52. $useInternalXmlErrors = libxml_use_internal_errors(true);
  53. /**
  54. * Load XML with network access disabled (LIBXML_NONET)
  55. * error disabled with @ for PHP-FPM scenario
  56. */
  57. set_error_handler(
  58. function ($errno, $errstr) {
  59. if (substr_count($errstr, 'DOMDocument::loadXML()') > 0) {
  60. return true;
  61. }
  62. return false;
  63. },
  64. E_WARNING
  65. );
  66. $result = (bool)$document->loadXML($xmlContent, LIBXML_NONET);
  67. restore_error_handler();
  68. // Entity load to previous setting
  69. libxml_disable_entity_loader($loadEntities);
  70. libxml_use_internal_errors($useInternalXmlErrors);
  71. if (!$result) {
  72. return false;
  73. }
  74. foreach ($document->childNodes as $child) {
  75. if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
  76. if ($child->entities->length > 0) {
  77. return false;
  78. }
  79. }
  80. }
  81. return true;
  82. }
  83. }