Is html2pdf Suspicious #
As of the latest analysis, the website html2pdf.com appears to be safe to use based on the automated review by Scamadviser.
However, the review tool identified that the website uses registrar facilities also used by many websites with low to very low review scores, which slightly reduces its trust rating. Additionally, the domain name has been registered for several years, generally indicating a more trustworthy site, but it’s still important to check for other suspicious attributes.
It’s important to note that the review is automated, and while the site seems safe, users should perform their own checks to ensure the website is safe to use.
Security Considerations:
Converting HTML to PDF using JavaScript can pose security risks if not done properly. The HTML content can contain malicious code or scripts that can be executed in the context of the PDF viewer, potentially compromising the security of the system. It is important to sanitize the HTML content and validate user input to prevent malicious code injection. Additionally, the PDF output can be encrypted and password-protected to prevent unauthorized access.
Yes, sanitizing your HTML before using it with html2pdf is important for security and to ensure consistent PDF generation. Let me explain why and how to do it:
Yes, sanitizing your HTML is very important when using html2pdf for several reasons:
Why HTML Sanitization is Crucial #
- Security Risks: Unsanitized HTML can contain malicious scripts or JavaScript that could execute during PDF generation
- Rendering Issues: Problematic HTML elements can cause rendering inconsistencies or failures in the PDF output
- Unexpected Behavior: Certain HTML constructs might cause html2pdf to behave unpredictably
- Data Protection: If the HTML contains user-submitted content, sanitization prevents XSS attacks
I’ve Implemented a Complete Sanitization Solution #
The updated code now includes:
A Comprehensive Sanitizer Function that:
- Removes all
<script>tags (except JSON data scripts) - Strips all inline event handlers (
onclick, etc.) - Removes JavaScript URLs from href and src attributes
- Processes the HTML through the DOM parser for thorough cleaning
- Removes all
Sanitization Applied to All Content:
- The main content is sanitized before PDF generation
- Banner templates are also sanitized
- All dynamic HTML generation is properly sanitized
Isolation of Content:
- Creates temporary, hidden divs with sanitized content
- Prevents modification of original page elements
- Cleans up after PDF generation
Additional Benefits of This Approach #
- Preserves Original Content: Your page’s original HTML remains untouched
- Better Security: Reduces risk when processing user-generated content
- Improved Reliability: Makes PDF generation more stable
For More Comprehensive Sanitization #
For production use with user-generated content, you might want to consider:
- Using a dedicated library like DOMPurify for even more thorough sanitization
- Implementing a whitelist approach (only allowing specific known-safe tags)
- Adding server-side sanitization before content reaches the browser
The current implementation provides a good baseline for security, but adapting it to your specific use case and security requirements is recommended.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML2PDF with Images Example</title>
<!-- Include html2pdf.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<style>
/* Styles for both screen and PDF */
.content {
max-width: 800px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.image-container {
margin: 20px 0;
}
img {
max-width: 100%;
height: auto;
}
button {
padding: 10px 15px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
/* PDF-only styles (will be applied when generating PDF) */
.pdf-styles {
display: none;
}
/* Class that will be temporarily added during PDF generation */
.for-pdf-generation .pdf-styles {
display: block;
}
/* PDF-specific styles */
@media print {
body {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
/* Example PDF-specific styles */
.for-pdf-generation h1 {
color: #2a5885;
page-break-before: always;
}
.for-pdf-generation .image-container {
page-break-inside: avoid;
border: 1px solid #ddd;
padding: 10px;
}
.for-pdf-generation img {
max-width: 90%;
margin: 0 auto;
display: block;
}
/* Hide elements from PDF */
.for-pdf-generation .screen-only {
display: none;
}
}
</style>
</head>
<body>
<div class="content">
<h1>HTML2PDF with Images Demo</h1>
<div id="content-to-print">
<!-- PDF-only elements (not visible on screen until PDF generation) -->
<div class="pdf-styles">
<div class="page-header">Generated on: <span id="pdf-date"></span></div>
<hr>
</div>
<h2>This content will be converted to PDF</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in dui mauris.</p>
<div class="image-container">
<h3>Sample Image 1</h3>
<img src="/api/placeholder/400/300" alt="Sample placeholder image">
<!-- PDF-only caption -->
<div class="pdf-styles">
<p class="image-caption">This caption only appears in the PDF version</p>
</div>
</div>
<p>Additional text after the first image. The images should appear in the PDF output.</p>
<!-- This element only shows on screen, not in PDF -->
<div class="screen-only">
<p>This text will only be visible on screen, not in the PDF.</p>
</div>
<div class="image-container">
<h3>Sample Image 2</h3>
<img src="/api/placeholder/400/200" alt="Another placeholder image">
<!-- PDF-only caption -->
<div class="pdf-styles">
<p class="image-caption">PDF-only caption for second image</p>
</div>
</div>
<p>Final paragraph of text to demonstrate that everything is properly included in the PDF.</p>
<!-- PDF-only footer -->
<div class="pdf-styles">
<hr>
<div class="page-footer">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>
</div>
</div>
<script>
// Set the PDF date when generating
document.getElementById('pdf-date').textContent = new Date().toLocaleDateString();
</script>
<div style="display: flex; gap: 10px; margin-top: 20px;">
<button onclick="generatePDF()">Generate PDF with Banner</button>
<button onclick="generatePDFCustomBanner()" style="background-color: #2196F3;">Generate PDF with Custom Banner</button>
</div>
<!-- Custom Banner Template (Hidden on screen) -->
<div id="custom-banner-template" style="display: none;">
<div style="width: 100%; height: 70px; background: linear-gradient(90deg, #4a148c 0%, #7b1fa2 100%); color: white; display: flex; align-items: center; justify-content: space-between; padding: 10px 20px;">
<div style="display: flex; align-items: center;">
<img src="/api/placeholder/60/60" alt="Company Logo" style="border-radius: 8px; margin-right: 15px;">
<div>
<div style="font-size: 22px; font-weight: bold;">Confidential Report</div>
<div style="font-size: 12px;">For internal use only</div>
</div>
</div>
<div style="text-align: right;">
<div id="banner-date" style="font-size: 12px;"></div>
<div id="banner-page" style="font-size: 12px;"></div>
</div>
</div>
</div>
</div>
<script>
function sanitizeHTML(html) {
// Create a new DOMParser
const parser = new DOMParser();
// Parse the HTML string
const doc = parser.parseFromString(html, 'text/html');
// Find and remove potentially dangerous elements
const scripts = doc.querySelectorAll('script:not([type="application/json"])');
scripts.forEach(script => script.remove());
// Remove inline event handlers from all elements
const allElements = doc.querySelectorAll('*');
allElements.forEach(el => {
// Remove all attributes that start with "on" (like onclick, onmouseover, etc.)
for (let i = el.attributes.length - 1; i >= 0; i--) {
const attrName = el.attributes[i].name;
if (attrName.startsWith('on')) {
el.removeAttribute(attrName);
}
}
// Remove href attributes that use javascript: protocol
if (el.hasAttribute('href')) {
const href = el.getAttribute('href');
if (href.toLowerCase().startsWith('javascript:')) {
el.removeAttribute('href');
}
}
// Remove src attributes that use javascript: protocol
if (el.hasAttribute('src')) {
const src = el.getAttribute('src');
if (src.toLowerCase().startsWith('javascript:')) {
el.removeAttribute('src');
}
}
});
// Use XMLSerializer to convert back to string
const serializer = new XMLSerializer();
return serializer.serializeToString(doc);
}
function generatePDF() {
// Select the element to convert
const element = document.getElementById('content-to-print');
// Get the HTML content and sanitize it
const htmlContent = element.innerHTML;
const sanitizedHTML = sanitizeHTML(htmlContent);
// Create a temporary div with the sanitized content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = sanitizedHTML;
tempDiv.classList.add('for-pdf-generation');
// Temporarily add to the document (hidden)
tempDiv.style.position = 'absolute';
tempDiv.style.left = '-9999px';
document.body.appendChild(tempDiv);
// Create a banner template
const createBanner = function(currentPage, pageCount) {
// Sanitize the banner content
const bannerHTML = `
<div style="text-align: center; background-color: #3f51b5; color: white; padding: 15px; width: 100%; height: 50px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 5px rgba(0,0,0,0.3);">
<div style="display: flex; align-items: center;">
<div style="width: 40px; height: 40px; background-color: #fff; border-radius: 50%; margin-right: 15px; display: flex; align-items: center; justify-content: center;">
<span style="color: #3f51b5; font-weight: bold; font-size: 18px;">PDF</span>
</div>
<div style="font-size: 20px; font-weight: bold;">${document.title || 'Company Document'}</div>
</div>
<div>
<span style="font-size: 12px;">Generated: ${new Date().toLocaleDateString()}</span>
<br>
<span style="font-size: 12px;">Page ${currentPage} of ${pageCount}</span>
</div>
</div>`;
return sanitizeHTML(bannerHTML);
};
// Configure html2pdf options with higher resolution settings and banner
const opt = {
margin: [70, 10, 20, 10], // [top, right, bottom, left] - increased top margin for the header banner
filename: 'document-with-images.pdf',
image: {
type: 'jpeg',
quality: 1.0 // Maximum image quality
},
html2canvas: {
scale: 4, // Increased from 2 to 4 for higher resolution
useCORS: true, // Important for loading images from other domains
logging: true,
letterRendering: true,
imageTimeout: 0, // No timeout for image loading
backgroundColor: null // Preserve background
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait',
compress: false // Avoid compression artifacts
},
// Add header (banner) functionality
pagebreak: { mode: 'avoid-all' },
header: {
height: '60mm',
contents: {
first: function(currentPage, pageCount) {
return createBanner(currentPage, pageCount);
},
2: function(currentPage, pageCount) {
return createBanner(currentPage, pageCount);
},
3: function(currentPage, pageCount) {
return createBanner(currentPage, pageCount);
},
// You can use a function for all pages
every: function(currentPage, pageCount) {
return createBanner(currentPage, pageCount);
}
}
}
};
// Generate PDF with banner
html2pdf().from(tempDiv).set(opt).toPdf().get('pdf').then(function(pdf) {
// PDF has been generated
// Remove temporary div
document.body.removeChild(tempDiv);
return pdf;
}).save();
}
// Helper function to add custom CSS to the PDF
function addCustomCSSForPDF() {
// Create a new style element for PDF-specific styles
const styleElement = document.createElement('style');
styleElement.id = 'pdf-only-styles';
styleElement.textContent = `
/* Additional dynamic PDF-only styles can be added here */
.page-header {
font-size: 12pt;
text-align: center;
margin-bottom: 20px;
}
.page-footer {
font-size: 10pt;
text-align: center;
color: #777;
margin-top: 20px;
}
`;
// Append to head
document.head.appendChild(styleElement);
return function() {
// Remove the style element when done
document.head.removeChild(styleElement);
};
}
</script>
</body>
</html>