See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Reader\Security; 4 5 use PhpOffice\PhpSpreadsheet\Reader; 6 7 class XmlScanner 8 { 9 /** 10 * String used to identify risky xml elements. 11 * 12 * @var string 13 */ 14 private $pattern; 15 16 private $callback; 17 18 private static $libxmlDisableEntityLoaderValue; 19 20 /** 21 * @var bool 22 */ 23 private static $shutdownRegistered = false; 24 25 public function __construct($pattern = '<!DOCTYPE') 26 { 27 $this->pattern = $pattern; 28 29 $this->disableEntityLoaderCheck(); 30 31 // A fatal error will bypass the destructor, so we register a shutdown here 32 if (!self::$shutdownRegistered) { 33 self::$shutdownRegistered = true; 34 register_shutdown_function([__CLASS__, 'shutdown']); 35 } 36 } 37 38 public static function getInstance(Reader\IReader $reader) 39 { 40 switch (true) { 41 case $reader instanceof Reader\Html: 42 return new self('<!ENTITY'); 43 case $reader instanceof Reader\Xlsx: 44 case $reader instanceof Reader\Xml: 45 case $reader instanceof Reader\Ods: 46 case $reader instanceof Reader\Gnumeric: 47 return new self('<!DOCTYPE'); 48 default: 49 return new self('<!DOCTYPE'); 50 } 51 } 52 53 public static function threadSafeLibxmlDisableEntityLoaderAvailability() 54 { 55 if (PHP_MAJOR_VERSION === 7) { 56 switch (PHP_MINOR_VERSION) { 57 case 2: 58 return PHP_RELEASE_VERSION >= 1; 59 case 1: 60 return PHP_RELEASE_VERSION >= 13; 61 case 0: 62 return PHP_RELEASE_VERSION >= 27; 63 } 64 65 return true; 66 } 67 68 return false; 69 } 70 71 private function disableEntityLoaderCheck(): void 72 { 73 if (\PHP_VERSION_ID < 80000) { 74 $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true); 75 76 if (self::$libxmlDisableEntityLoaderValue === null) { 77 self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue; 78 } 79 } 80 } 81 82 public static function shutdown(): void 83 { 84 if (self::$libxmlDisableEntityLoaderValue !== null && \PHP_VERSION_ID < 80000) { 85 libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue); 86 self::$libxmlDisableEntityLoaderValue = null; 87 } 88 } 89 90 public function __destruct() 91 { 92 self::shutdown(); 93 } 94 95 public function setAdditionalCallback(callable $callback): void 96 { 97 $this->callback = $callback; 98 } 99 100 private function toUtf8($xml) 101 { 102 $pattern = '/encoding="(.*?)"/'; 103 $result = preg_match($pattern, $xml, $matches); 104 $charset = strtoupper($result ? $matches[1] : 'UTF-8'); 105 106 if ($charset !== 'UTF-8') { 107 $xml = mb_convert_encoding($xml, 'UTF-8', $charset); 108 109 $result = preg_match($pattern, $xml, $matches); 110 $charset = strtoupper($result ? $matches[1] : 'UTF-8'); 111 if ($charset !== 'UTF-8') { 112 throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks'); 113 } 114 } 115 116 return $xml; 117 } 118 119 /** 120 * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks. 121 * 122 * @param mixed $xml 123 * 124 * @return string 125 */ 126 public function scan($xml) 127 { 128 $this->disableEntityLoaderCheck(); 129 130 $xml = $this->toUtf8($xml); 131 132 // Don't rely purely on libxml_disable_entity_loader() 133 $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/'; 134 135 if (preg_match($pattern, $xml)) { 136 throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks'); 137 } 138 139 if ($this->callback !== null && is_callable($this->callback)) { 140 $xml = call_user_func($this->callback, $xml); 141 } 142 143 return $xml; 144 } 145 146 /** 147 * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks. 148 * 149 * @param string $filestream 150 * 151 * @return string 152 */ 153 public function scanFile($filestream) 154 { 155 return $this->scan(file_get_contents($filestream)); 156 } 157 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body