-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Executive Summary
A Cross-Site Scripting (XSS) vulnerability has been identified in the html2pdf.js library. The vulnerability exists due to unsanitized user input being directly assigned to the innerHTML property. This allows attackers to execute arbitrary JavaScript code in the context of the application, potentially leading to session hijacking, data theft, and unauthorized actions.
Vulnerability Details
Affected Component
File: src/worker.js
Line: 71
Link: https://github.com/eKoopmans/html2pdf.js/blob/main/src/worker.js#L71
Function: Worker.prototype.from()
Vulnerable Code
case 'string': return this.set({ src: createElement('div', {innerHTML: src}) });Root Cause
When html2pdf() is called with a string parameter, the library directly assigns the user-controlled input to the innerHTML property of a newly created div element without any sanitization. This occurs in the from() method of the Worker class.
Attack Vector
- Attacker provides malicious HTML string containing JavaScript payload
- html2pdf.js assigns this string to
innerHTML(line 71, worker.js) - Element is appended to the DOM (line 125, worker.js)
- Browser executes JavaScript when the element is added to the DOM
- XSS payload runs in the victim's browser context
Partial Mitigation
The library includes a partial mitigation in src/utils.js (lines 20-23) that removes <script> tags:
if (opt.innerHTML) {
el.innerHTML = opt.innerHTML;
var scripts = el.getElementsByTagName('script');
for (var i = scripts.length; i-- > 0; null) {
scripts[i].parentNode.removeChild(scripts[i]);
}
}However, this mitigation is insufficient as it only removes <script> tags but does not protect against:
- Event handlers (
onerror,onclick,onload, etc.) - SVG with embedded scripts
<iframe>with JavaScript URLs- Other HTML injection vectors
Proof of Concept
PoC 1: Basic XSS (Alert)
Payload:
<img src=x onerror="alert(document.cookie)">Exploitation Code:
const maliciousHTML = '<img src=x onerror="alert(document.cookie)">';
html2pdf()
.from(maliciousHTML)
.set({
margin: 1,
filename: 'xss-test.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
})
.save();Result: An alert dialog appears when the PDF generation process begins, confirming XSS execution.
Technical Analysis
Code Flow
-
Input Reception (
src/index.js:20)return worker.from(src).save();
-
String Processing (
src/worker.js:71)case 'string': return this.set({ src: createElement('div', {innerHTML: src}) });
-
DOM Injection (
src/worker.js:125)document.body.appendChild(this.prop.overlay);
-
XSS Execution
- When
innerHTMLis set, browser parses HTML - Event handlers (e.g.,
onerror) are registered - When element is appended to DOM, events fire
- JavaScript executes in page context
- When
Why Current Mitigation Fails
The script tag removal in src/utils.js is insufficient because:
- Event Handlers Bypass: Event handlers like
onerror,onclick,onloadare not script tags - SVG Vectors: SVG elements can contain embedded JavaScript
- Iframe Vectors: Iframes with JavaScript URLs can execute code
- Timing: Script removal happens after
innerHTMLassignment, but event handlers are already registered
Browser Behavior
Modern browsers execute JavaScript when:
innerHTMLis set with HTML containing event handlers- Elements with event handlers are appended to the DOM
- This occurs before html2canvas processes the element
Remediation
Always Sanitize User Input:
const DOMPurify = require('dompurify');
const sanitizedHTML = DOMPurify.sanitize(userInput);
html2pdf().from(sanitizedHTML).save();