mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-05 06:58:15 +00:00
Ignore Settings::libXmlLoaderOptions
Having addressed several security advisories, one evident *theoretical* problem remains. This is an attempt to future-proof our code against similar vulnerabilities. It all begins with our implementation of libXmlLoaderOptions, which uses as a default LIBXML_DTDLOAD. This unfortunate choice opens us to XXE problems, many recently solved. I do not believe that there is a legitimate use case for allowing this, and will therefore ignore and deprecate that option. Although this might seem to be a breaking change, it is not. The setting is used only after the Xml has been subject to a security scan, and the security scan throws an exception if it detects the use of `<!DOCTYPE` within the Xml. Therefore, the setting will be effective only on Xml which does not contain that tag, and will consequently have no effect on most Xml. The only exception would be Xml which has been crafted to avoid detection by the security scanner in a manner which has not been disclosed to us. Although we hope that we've now blocked all such avenues, this provides additional protection just in case. With this change in place, we could relax certain restrictions, e.g. the use of EBCDIC or even UTF-7. For now, these will remain in place. I will need to be convinced that there is a legitimate use case for easing the restrictions before doing so. We might even consider the elimination of the Security Scanner altogether. However, it does allow for early detection, and, in any case, provides a method to correct Xml which most Xml readers would fail but which Excel accepts. My plan is to merge this within the next few days, and tag a new release immediately after. It will also be backported to all active branches.
This commit is contained in:
@@ -298,7 +298,6 @@ versions of Microsoft Excel.
|
||||
**Excel 2003 XML limitations** Please note that Excel 2003 XML format
|
||||
has some limits regarding to styling cells and handling large
|
||||
spreadsheets via PHP.
|
||||
Also, only files using charset UTF-8 or ISO-8859-* are supported.
|
||||
|
||||
### \PhpOffice\PhpSpreadsheet\Reader\Xml
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ use PhpOffice\PhpSpreadsheet\Reader\Gnumeric\Styles;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
@@ -104,7 +103,7 @@ class Gnumeric extends BaseReader
|
||||
|
||||
$xml = new XMLReader();
|
||||
$contents = $this->gzfileGetContents($filename);
|
||||
$xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
|
||||
$xml->xml($contents);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
$worksheetNames = [];
|
||||
@@ -133,7 +132,7 @@ class Gnumeric extends BaseReader
|
||||
|
||||
$xml = new XMLReader();
|
||||
$contents = $this->gzfileGetContents($filename);
|
||||
$xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
|
||||
$xml->xml($contents);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
$worksheetInfo = [];
|
||||
@@ -248,7 +247,7 @@ class Gnumeric extends BaseReader
|
||||
|
||||
/** @var XmlScanner */
|
||||
$securityScanner = $this->securityScanner;
|
||||
$xml2 = simplexml_load_string($securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
|
||||
$xml2 = simplexml_load_string($securityScanner->scan($gFileData));
|
||||
$xml = self::testSimpleXml($xml2);
|
||||
|
||||
$gnmXML = $xml->children(self::NAMESPACE_GNM);
|
||||
|
||||
@@ -34,7 +34,7 @@ class Html extends BaseReader
|
||||
|
||||
private const STARTS_WITH_BOM = '/^(?:\xfe\xff|\xff\xfe|\xEF\xBB\xBF)/';
|
||||
|
||||
private const DECLARES_CHARSET = '/ charset=/i';
|
||||
private const DECLARES_CHARSET = '/\\bcharset=/i';
|
||||
|
||||
/**
|
||||
* Input encoding.
|
||||
|
||||
@@ -17,7 +17,6 @@ use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
@@ -58,9 +57,12 @@ class Ods extends BaseReader
|
||||
$mimeType = $zip->getFromName($stat['name']);
|
||||
} elseif ($zip->statName('META-INF/manifest.xml')) {
|
||||
$xml = simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($zip->getFromName('META-INF/manifest.xml')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan(
|
||||
$zip->getFromName(
|
||||
'META-INF/manifest.xml'
|
||||
)
|
||||
)
|
||||
);
|
||||
if ($xml !== false) {
|
||||
$namespacesContent = $xml->getNamespaces(true);
|
||||
@@ -98,9 +100,10 @@ class Ods extends BaseReader
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml(
|
||||
$this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
|
||||
null,
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scanFile(
|
||||
'zip://' . realpath($filename) . '#' . self::INITIAL_FILE
|
||||
)
|
||||
);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
@@ -145,9 +148,10 @@ class Ods extends BaseReader
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml(
|
||||
$this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
|
||||
null,
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scanFile(
|
||||
'zip://' . realpath($filename) . '#' . self::INITIAL_FILE
|
||||
)
|
||||
);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
@@ -254,9 +258,8 @@ class Ods extends BaseReader
|
||||
// Meta
|
||||
|
||||
$xml = @simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($zip->getFromName('meta.xml')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($zip->getFromName('meta.xml'))
|
||||
);
|
||||
if ($xml === false) {
|
||||
throw new Exception('Unable to read data from {$pFilename}');
|
||||
@@ -270,8 +273,8 @@ class Ods extends BaseReader
|
||||
|
||||
$dom = new DOMDocument('1.01', 'UTF-8');
|
||||
$dom->loadXML(
|
||||
$this->getSecurityScannerOrThrow()->scan($zip->getFromName('styles.xml')),
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($zip->getFromName('styles.xml'))
|
||||
);
|
||||
|
||||
$pageSettings = new PageSettings($dom);
|
||||
@@ -280,8 +283,8 @@ class Ods extends BaseReader
|
||||
|
||||
$dom = new DOMDocument('1.01', 'UTF-8');
|
||||
$dom->loadXML(
|
||||
$this->getSecurityScannerOrThrow()->scan($zip->getFromName(self::INITIAL_FILE)),
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($zip->getFromName(self::INITIAL_FILE))
|
||||
);
|
||||
|
||||
$officeNs = (string) $dom->lookupNamespaceUri('office');
|
||||
@@ -690,8 +693,8 @@ class Ods extends BaseReader
|
||||
{
|
||||
$dom = new DOMDocument('1.01', 'UTF-8');
|
||||
$dom->loadXML(
|
||||
$this->getSecurityScannerOrThrow()->scan($zip->getFromName('settings.xml')),
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($zip->getFromName('settings.xml'))
|
||||
);
|
||||
//$xlinkNs = $dom->lookupNamespaceUri('xlink');
|
||||
$configNs = (string) $dom->lookupNamespaceUri('config');
|
||||
|
||||
@@ -27,7 +27,6 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Theme;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\WorkbookView;
|
||||
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
@@ -123,7 +122,7 @@ class Xlsx extends BaseReader
|
||||
$rels = @simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($contents),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions(),
|
||||
0,
|
||||
$ns
|
||||
);
|
||||
|
||||
@@ -138,7 +137,7 @@ class Xlsx extends BaseReader
|
||||
$rels = simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($contents),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions(),
|
||||
0,
|
||||
($ns === '' ? $ns : '')
|
||||
);
|
||||
|
||||
@@ -245,11 +244,13 @@ class Xlsx extends BaseReader
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml(
|
||||
$this->getSecurityScannerOrThrow()->scan(
|
||||
$this->getFromZipArchive($this->zip, $fileWorksheetPath)
|
||||
),
|
||||
null,
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan(
|
||||
$this->getFromZipArchive(
|
||||
$this->zip,
|
||||
$fileWorksheetPath
|
||||
)
|
||||
)
|
||||
);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
@@ -2001,9 +2002,8 @@ class Xlsx extends BaseReader
|
||||
if ($dataRels) {
|
||||
// exists and not empty if the ribbon have some pictures (other than internal MSO)
|
||||
$UIRels = simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($dataRels),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($dataRels)
|
||||
);
|
||||
if (false !== $UIRels) {
|
||||
// we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Properties
|
||||
@@ -23,9 +22,7 @@ class Properties
|
||||
{
|
||||
// okay to omit namespace because everything will be processed by xpath
|
||||
$obj = simplexml_load_string(
|
||||
$this->securityScanner->scan($propertyData),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->securityScanner->scan($propertyData)
|
||||
);
|
||||
|
||||
return $obj === false ? null : $obj;
|
||||
|
||||
@@ -15,7 +15,6 @@ use PhpOffice\PhpSpreadsheet\Reader\Xml\PageSettings;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xml\Properties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xml\Style;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
@@ -132,9 +131,8 @@ class Xml extends BaseReader
|
||||
}
|
||||
if ($continue) {
|
||||
$xml = @simplexml_load_string(
|
||||
$this->getSecurityScannerOrThrow()->scan($data),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
$this->getSecurityScannerOrThrow()
|
||||
->scan($data)
|
||||
);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
|
||||
@@ -94,6 +94,8 @@ class Settings
|
||||
* Set default options for libxml loader.
|
||||
*
|
||||
* @param ?int $options Default options for libxml loader
|
||||
*
|
||||
* @deprecated 3.5.0 no longer needed
|
||||
*/
|
||||
public static function setLibXmlLoaderOptions(?int $options): int
|
||||
{
|
||||
@@ -110,14 +112,12 @@ class Settings
|
||||
* Defaults to LIBXML_DTDLOAD | LIBXML_DTDATTR when not set explicitly.
|
||||
*
|
||||
* @return int Default options for libxml loader
|
||||
*
|
||||
* @deprecated 3.5.0 no longer needed
|
||||
*/
|
||||
public static function getLibXmlLoaderOptions(): int
|
||||
{
|
||||
if (self::$libXmlLoaderOptions === null) {
|
||||
return self::setLibXmlLoaderOptions(null);
|
||||
}
|
||||
|
||||
return self::$libXmlLoaderOptions;
|
||||
return self::$libXmlLoaderOptions ?? (defined('LIBXML_DTDLOAD') ? (LIBXML_DTDLOAD | LIBXML_DTDATTR) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@ class HtmlCharsetTest extends TestCase
|
||||
['charset.UTF-16.lebom.html', 'À1'],
|
||||
['charset.gb18030.html', '电视机'],
|
||||
['charset.unknown.html', 'exception'],
|
||||
['xhtml4.entity.xhtml', 'exception'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; CHARSET=ISO-8859-1">
|
||||
<meta http-equiv="Content-Type" content="text/html;CHARSET=ISO-8859-1">
|
||||
<title>ISO-8859-1 Html4 Doctype and Meta</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
|
||||
<!ENTITY test "It worked">
|
||||
]>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml;charset=utf-8" />
|
||||
<title>HTML Entities</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><td>&test;</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user