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_xml
can 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$authorizationHeader
is assigned in ServerBag.php on line 62$this->parameters['HTTP_AUTHORIZATION']
seems to return tainted data, and$authorizationHeader
is assignedin vendor/ServerBag.php on line 62
in vendor/ServerBag.php on line 77
in vendor/ParameterBag.php on line 45
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$_POST,
and$_POST
is passed to Request::createRequestFromFactory() in Request.php on line 281$_POST,
and$_POST
is passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$request
is passed to Request::__construct()in vendor/Request.php on line 1945
$request
is passed to Request::initialize()in vendor/Request.php on line 222
$request
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$_SERVER,
and$server
is assigned in Request.php on line 271$_SERVER,
and$server
is assignedin vendor/Request.php on line 271
$server
is passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$server
is passed to Request::__construct()in vendor/Request.php on line 1945
$server
is passed to Request::initialize()in vendor/Request.php on line 222
$server
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
HTTP_CONTENT_LENGTH
from$_SERVER,
and$server
is assigned in Request.php on line 274HTTP_CONTENT_LENGTH
from$_SERVER,
and$server
is assignedin vendor/Request.php on line 274
$server
is passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$server
is passed to Request::__construct()in vendor/Request.php on line 1945
$server
is passed to Request::initialize()in vendor/Request.php on line 222
$server
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
HTTP_CONTENT_TYPE
from$_SERVER,
and$server
is assigned in Request.php on line 277HTTP_CONTENT_TYPE
from$_SERVER,
and$server
is assignedin vendor/Request.php on line 277
$server
is passed to Request::createRequestFromFactory()in vendor/Request.php on line 281
$server
is passed to Request::__construct()in vendor/Request.php on line 1945
$server
is passed to Request::initialize()in vendor/Request.php on line 222
$server
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$server['HTTP_HOST']
seems to return tainted data, and$server
is assigned in Request.php on line 347$server['HTTP_HOST']
seems to return tainted data, and$server
is assignedin vendor/Request.php on line 347
$server
is assignedin vendor/Request.php on line 395
$server
is assignedin vendor/Request.php on line 396
$server
is passed to Request::createRequestFromFactory()in vendor/Request.php on line 398
$server
is passed to Request::__construct()in vendor/Request.php on line 1945
$server
is passed to Request::initialize()in vendor/Request.php on line 222
$server
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$this->parameters['PHP_AUTH_USER']
seems to return tainted data, and$headers
is assigned in ServerBag.php on line 43$this->parameters['PHP_AUTH_USER']
seems to return tainted data, and$headers
is assignedin vendor/ServerBag.php on line 43
$headers
is assignedin vendor/ServerBag.php on line 44
$this->server->getHeaders()
is passed to HeaderBag::__construct()in vendor/Request.php on line 246
$values
is assignedin vendor/HeaderBag.php on line 31
$values
is passed to HeaderBag::set()in vendor/HeaderBag.php on line 32
(array) $values
is passed through array_values(), and$values
is assignedin vendor/HeaderBag.php on line 142
in vendor/HeaderBag.php on line 145
in vendor/HeaderBag.php on line 125
$requestUri
is assignedin vendor/Request.php on line 1715
$requestUri
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is passed to XML2Array::createArray()in src/POData/UriProcessor/RequestDescription.php on line 302
$this->parameters['PHP_AUTH_PW']
seems to return tainted data, and$headers
is assigned in ServerBag.php on line 44$this->parameters['PHP_AUTH_PW']
seems to return tainted data, and$headers
is assignedin vendor/ServerBag.php on line 44
$this->server->getHeaders()
is passed to HeaderBag::__construct()in vendor/Request.php on line 246
$values
is assignedin vendor/HeaderBag.php on line 31
$values
is passed to HeaderBag::set()in vendor/HeaderBag.php on line 32
(array) $values
is passed through array_values(), and$values
is assignedin vendor/HeaderBag.php on line 142
in vendor/HeaderBag.php on line 145
in vendor/HeaderBag.php on line 125
$requestUri
is assignedin vendor/Request.php on line 1715
$requestUri
is 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
$input
is 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
$string
is assignedin src/POData/UriProcessor/RequestDescription.php on line 297
$string
is 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:
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:
For numeric data, we recommend to explicitly cast the data: