icingaweb2/library/vendor/Zend/Xml/Security.php

157 lines
4.5 KiB
PHP

<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Xml
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
/**
* @category Zend
* @package Zend_Xml_SecurityScan
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Xml_Security
{
const ENTITY_DETECT = 'Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks';
/**
* Heuristic scan to detect entity in XML
*
* @param string $xml
* @throws Zend_Xml_Exception
*/
protected static function heuristicScan($xml)
{
if (strpos($xml, '<!ENTITY') !== false) {
throw new Zend_Xml_Exception(self::ENTITY_DETECT);
}
}
/**
* @param integer $errno
* @param string $errstr
* @param string $errfile
* @param integer $errline
* @return bool
*/
public static function loadXmlErrorHandler($errno, $errstr, $errfile, $errline)
{
if (substr_count($errstr, 'DOMDocument::loadXML()') > 0) {
return true;
}
return false;
}
/**
* Scan XML string for potential XXE and XEE attacks
*
* @param string $xml
* @param DomDocument $dom
* @throws Zend_Xml_Exception
* @return SimpleXMLElement|DomDocument|boolean
*/
public static function scan($xml, DOMDocument $dom = null)
{
// If running with PHP-FPM we perform an heuristic scan
// We cannot use libxml_disable_entity_loader because of this bug
// @see https://bugs.php.net/bug.php?id=64938
if (self::isPhpFpm()) {
self::heuristicScan($xml);
}
if (null === $dom) {
$simpleXml = true;
$dom = new DOMDocument();
}
if (!self::isPhpFpm()) {
$loadEntities = libxml_disable_entity_loader(true);
$useInternalXmlErrors = libxml_use_internal_errors(true);
}
// Load XML with network access disabled (LIBXML_NONET)
// error disabled with @ for PHP-FPM scenario
set_error_handler(array('Zend_Xml_Security', 'loadXmlErrorHandler'), E_WARNING);
$result = $dom->loadXml($xml, LIBXML_NONET);
restore_error_handler();
// Entity load to previous setting
if (!self::isPhpFpm()) {
libxml_disable_entity_loader($loadEntities);
libxml_use_internal_errors($useInternalXmlErrors);
}
if (!$result) {
return false;
}
// Scan for potential XEE attacks using ENTITY, if not PHP-FPM
if (!self::isPhpFpm()) {
foreach ($dom->childNodes as $child) {
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
if ($child->entities->length > 0) {
throw new Zend_Xml_Exception(self::ENTITY_DETECT);
}
}
}
}
if (isset($simpleXml)) {
$result = simplexml_import_dom($dom);
if (!$result instanceof SimpleXMLElement) {
return false;
}
return $result;
}
return $dom;
}
/**
* Scan XML file for potential XXE/XEE attacks
*
* @param string $file
* @param DOMDocument $dom
* @throws Zend_Xml_Exception
* @return SimpleXMLElement|DomDocument
*/
public static function scanFile($file, DOMDocument $dom = null)
{
if (!file_exists($file)) {
throw new Zend_Xml_Exception(
"The file $file specified doesn't exist"
);
}
return self::scan(file_get_contents($file), $dom);
}
/**
* Return true if PHP is running with PHP-FPM
*
* @return boolean
*/
public static function isPhpFpm()
{
if (substr(php_sapi_name(), 0, 3) === 'fpm') {
return true;
}
return false;
}
}