|
1
|
|
|
<?php |
|
2
|
|
|
namespace POData\UriProcessor; |
|
3
|
|
|
|
|
4
|
|
|
class XML2Array |
|
5
|
|
|
{ |
|
6
|
|
|
/** |
|
7
|
|
|
* @var string |
|
8
|
|
|
*/ |
|
9
|
|
|
private static $encoding = 'UTF-8'; |
|
10
|
|
|
/** |
|
11
|
|
|
* @var \DOMDocument |
|
12
|
|
|
*/ |
|
13
|
|
|
private static $xml = null; |
|
14
|
|
|
/** |
|
15
|
|
|
* Convert an XML to Array. |
|
16
|
|
|
* |
|
17
|
|
|
* @param string $input_xml |
|
18
|
|
|
* |
|
19
|
|
|
* @return array |
|
20
|
|
|
* |
|
21
|
|
|
* @throws \Exception |
|
22
|
|
|
*/ |
|
23
|
|
|
public static function createArray($input_xml) |
|
24
|
|
|
{ |
|
25
|
|
|
if (empty($input_xml)) { |
|
26
|
|
|
return []; |
|
27
|
|
|
} |
|
28
|
|
|
$xml = self::getXMLRoot(); |
|
29
|
|
|
if (is_string($input_xml)) { |
|
30
|
|
|
try { |
|
31
|
|
|
$xml->loadXML($input_xml); |
|
|
|
|
|
|
32
|
|
|
if (!is_object($xml) || empty($xml->documentElement)) { |
|
33
|
|
|
throw new \Exception(); |
|
34
|
|
|
} |
|
35
|
|
|
} catch (\Exception $ex) { |
|
36
|
|
|
throw new \Exception('[XML2Array] Error parsing the XML string.' . PHP_EOL . $ex->getMessage()); |
|
37
|
|
|
} |
|
38
|
|
|
} elseif (is_object($input_xml)) { |
|
39
|
|
|
if (get_class($input_xml) != 'DOMDocument') { |
|
40
|
|
|
throw new \Exception('[XML2Array] The input XML object should be of type: DOMDocument.'); |
|
41
|
|
|
} |
|
42
|
|
|
$xml = self::$xml = $input_xml; |
|
43
|
|
|
} else { |
|
44
|
|
|
throw new \Exception('[XML2Array] Invalid input'); |
|
45
|
|
|
} |
|
46
|
|
|
$array[$xml->documentElement->tagName] = self::convert($xml->documentElement); |
|
|
|
|
|
|
47
|
|
|
self::$xml = null; // clear the xml node in the class for 2nd time use. |
|
48
|
|
|
return $array; |
|
49
|
|
|
} |
|
50
|
|
|
/** |
|
51
|
|
|
* Initialize the root XML node [optional]. |
|
52
|
|
|
* |
|
53
|
|
|
* @param string $version |
|
54
|
|
|
* @param string $encoding |
|
55
|
|
|
* @param bool $standalone |
|
56
|
|
|
* @param bool $format_output |
|
57
|
|
|
*/ |
|
58
|
|
|
public static function init($version = '1.0', $encoding = 'utf-8', $standalone = false, $format_output = true) |
|
59
|
|
|
{ |
|
60
|
|
|
self::$xml = new \DomDocument($version, $encoding); |
|
61
|
|
|
self::$xml->xmlStandalone = $standalone; |
|
62
|
|
|
self::$xml->formatOutput = $format_output; |
|
63
|
|
|
self::$encoding = $encoding; |
|
64
|
|
|
} |
|
65
|
|
|
/** |
|
66
|
|
|
* Convert an Array to XML. |
|
67
|
|
|
* |
|
68
|
|
|
* @param \DOMNode $node - XML as a string or as an object of DOMDocument |
|
69
|
|
|
* |
|
70
|
|
|
* @return array |
|
71
|
|
|
*/ |
|
72
|
|
|
private static function convert(\DOMNode $node) |
|
73
|
|
|
{ |
|
74
|
|
|
$output = []; |
|
75
|
|
|
switch ($node->nodeType) { |
|
76
|
|
|
case XML_CDATA_SECTION_NODE: |
|
77
|
|
|
$output['@cdata'] = trim($node->textContent); |
|
78
|
|
|
break; |
|
79
|
|
|
case XML_TEXT_NODE: |
|
80
|
|
|
$output = trim($node->textContent); |
|
81
|
|
|
break; |
|
82
|
|
|
case XML_ELEMENT_NODE: |
|
83
|
|
|
// for each child node, call the covert function recursively |
|
84
|
|
|
for ($i = 0, $m = $node->childNodes->length; $i < $m; ++$i) { |
|
85
|
|
|
$child = $node->childNodes->item($i); |
|
86
|
|
|
$v = self::convert($child); |
|
87
|
|
|
if (isset($child->tagName)) { |
|
88
|
|
|
$t = $child->tagName; |
|
89
|
|
|
// assume more nodes of same kind are coming |
|
90
|
|
|
if (!array_key_exists($t, $output)) { |
|
91
|
|
|
$output[$t] = []; |
|
92
|
|
|
} |
|
93
|
|
|
$output[$t][] = $v; |
|
94
|
|
|
} else { |
|
95
|
|
|
//check if it is not an empty node |
|
96
|
|
|
if (!empty($v)) { |
|
97
|
|
|
$output = $v; |
|
98
|
|
|
} |
|
99
|
|
|
} |
|
100
|
|
|
} |
|
101
|
|
|
if (is_array($output)) { |
|
102
|
|
|
// if only one node of its kind, assign it directly instead if array($value); |
|
103
|
|
|
foreach ($output as $t => $v) { |
|
104
|
|
|
if (is_array($v) && count($v) == 1) { |
|
105
|
|
|
$output[$t] = $v[0]; |
|
106
|
|
|
} |
|
107
|
|
|
} |
|
108
|
|
|
if (empty($output)) { |
|
109
|
|
|
//for empty nodes |
|
110
|
|
|
$output = ''; |
|
111
|
|
|
} |
|
112
|
|
|
} |
|
113
|
|
|
// loop through the attributes and collect them |
|
114
|
|
|
if ($node->attributes->length) { |
|
|
|
|
|
|
115
|
|
|
$a = []; |
|
116
|
|
|
foreach ($node->attributes as $attrName => $attrNode) { |
|
117
|
|
|
$a[$attrName] = $attrNode->value; |
|
118
|
|
|
} |
|
119
|
|
|
// if its an leaf node, store the value in @value instead of directly storing it. |
|
120
|
|
|
if (!is_array($output)) { |
|
121
|
|
|
$output = ['@value' => $output]; |
|
122
|
|
|
} |
|
123
|
|
|
$output['@attributes'] = $a; |
|
124
|
|
|
} |
|
125
|
|
|
break; |
|
126
|
|
|
} |
|
127
|
|
|
return $output; |
|
128
|
|
|
} |
|
129
|
|
|
/** |
|
130
|
|
|
* Get the root XML node, if there isn't one, create it. |
|
131
|
|
|
* |
|
132
|
|
|
* @return \DOMDocument |
|
133
|
|
|
*/ |
|
134
|
|
|
private static function getXMLRoot() |
|
135
|
|
|
{ |
|
136
|
|
|
if (empty(self::$xml)) { |
|
137
|
|
|
self::init(); |
|
138
|
|
|
} |
|
139
|
|
|
return self::$xml; |
|
140
|
|
|
} |
|
141
|
|
|
} |
|
142
|
|
|
|
$input_xmlcan contain request data and is used in xml context(s) leading to a potential security vulnerability.8 paths for user data to reach this point
$this->parameters['HTTP_AUTHORIZATION']seems to return tainted data, and$authorizationHeaderis assigned in ServerBag.php on line 62$this->parameters['HTTP_AUTHORIZATION']seems to return tainted data, and$authorizationHeaderis assignedin vendor/ServerBag.php on line 62
in vendor/ServerBag.php on line 77
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$_POST,and$_POSTis passed to Request::createRequestFromFactory() in Request.php on line 281$_POST,and$_POSTis passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$requestis passed to Request::__construct()in vendor/Request.php on line 1945
$requestis passed to Request::initialize()in vendor/Request.php on line 222
$requestis passed to ParameterBag::__construct()in vendor/Request.php on line 240
in vendor/ParameterBag.php on line 35
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$_SERVER,and$serveris assigned in Request.php on line 271$_SERVER,and$serveris assignedin vendor/Request.php on line 271
$serveris passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$serveris passed to Request::__construct()in vendor/Request.php on line 1945
$serveris passed to Request::initialize()in vendor/Request.php on line 222
$serveris passed to ParameterBag::__construct()in vendor/Request.php on line 245
in vendor/ParameterBag.php on line 35
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
HTTP_CONTENT_LENGTHfrom$_SERVER,and$serveris assigned in Request.php on line 274HTTP_CONTENT_LENGTHfrom$_SERVER,and$serveris assignedin vendor/Request.php on line 274
$serveris passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$serveris passed to Request::__construct()in vendor/Request.php on line 1945
$serveris passed to Request::initialize()in vendor/Request.php on line 222
$serveris passed to ParameterBag::__construct()in vendor/Request.php on line 245
in vendor/ParameterBag.php on line 35
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
HTTP_CONTENT_TYPEfrom$_SERVER,and$serveris assigned in Request.php on line 277HTTP_CONTENT_TYPEfrom$_SERVER,and$serveris assignedin vendor/Request.php on line 277
$serveris passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$serveris passed to Request::__construct()in vendor/Request.php on line 1945
$serveris passed to Request::initialize()in vendor/Request.php on line 222
$serveris passed to ParameterBag::__construct()in vendor/Request.php on line 245
in vendor/ParameterBag.php on line 35
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$server['HTTP_HOST']seems to return tainted data, and$serveris assigned in Request.php on line 347$server['HTTP_HOST']seems to return tainted data, and$serveris assignedin vendor/Request.php on line 347
$serveris assignedin vendor/Request.php on line 395
$serveris assignedin vendor/Request.php on line 396
$serveris passed to Request::createRequestFromFactory()in vendor/Request.php on line 398
$serveris passed to Request::__construct()in vendor/Request.php on line 1945
$serveris passed to Request::initialize()in vendor/Request.php on line 222
$serveris passed to ParameterBag::__construct()in vendor/Request.php on line 245
in vendor/ParameterBag.php on line 35
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$this->parameters['PHP_AUTH_USER']seems to return tainted data, and$headersis assigned in ServerBag.php on line 43$this->parameters['PHP_AUTH_USER']seems to return tainted data, and$headersis assignedin vendor/ServerBag.php on line 43
$headersis assignedin vendor/ServerBag.php on line 44
$this->server->getHeaders()is passed to HeaderBag::__construct()in vendor/Request.php on line 246
$valuesis assignedin vendor/HeaderBag.php on line 31
$valuesis passed to HeaderBag::set()in vendor/HeaderBag.php on line 32
(array) $valuesis passed through array_values(), and$valuesis assignedin vendor/HeaderBag.php on line 142
in vendor/HeaderBag.php on line 145
in vendor/HeaderBag.php on line 125
$requestUriis assignedin vendor/Request.php on line 1715
$requestUriis passed to ParameterBag::set()in vendor/Request.php on line 1746
in vendor/ParameterBag.php on line 99
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$this->parameters['PHP_AUTH_PW']seems to return tainted data, and$headersis assigned in ServerBag.php on line 44$this->parameters['PHP_AUTH_PW']seems to return tainted data, and$headersis assignedin vendor/ServerBag.php on line 44
$this->server->getHeaders()is passed to HeaderBag::__construct()in vendor/Request.php on line 246
$valuesis assignedin vendor/HeaderBag.php on line 31
$valuesis passed to HeaderBag::set()in vendor/HeaderBag.php on line 32
(array) $valuesis passed through array_values(), and$valuesis assignedin vendor/HeaderBag.php on line 142
in vendor/HeaderBag.php on line 145
in vendor/HeaderBag.php on line 125
$requestUriis assignedin vendor/Request.php on line 1715
$requestUriis passed to ParameterBag::set()in vendor/Request.php on line 1746
in vendor/ParameterBag.php on line 99
in vendor/ParameterBag.php on line 45
$inputis assignedin vendor/Request.php on line 335
$this->input()is passed through array_replace_recursive()in vendor/Request.php on line 323
in src/POData/OperationContext/Web/Illuminate/IncomingIlluminateRequest.php on line 132
in src/POData/UriProcessor/RequestDescription.php on line 282
$stringis assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$stringis passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
Preventing XML Injection Attacks
If you pass user-data to XML parsing functions like
simplexml_load_string(), this can be abused to inject external entities to gain access to the contents of any file in your filesystem, or it can be used to freeze your PHP process with an entity expansion attack.In order to prevent that, make sure to disable external entity loading and disallow custom doc-types:
libxml_disable_entity_loader(true); $dom = new DOMDocument; $dom->loadXML($taintedXml); foreach ($dom->childNodes as $child) { if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { throw new InvalidArgumentException( 'Invalid XML: Detected use of illegal DOCTYPE' ); } } // It is now safe to use $taintedXml $xml = simplexml_load_string($taintedXml);General Strategies to prevent injection
In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) { throw new \InvalidArgumentException('This input is not allowed.'); }For numeric data, we recommend to explicitly cast the data: