human (Jan 23, 2025, 04:03 PM)
Modern approaches in HTML and JavaScript to render unsafe HTML content while disabling any scripting
assistant (Jan 23, 2025, 04:03 PM)
I'll explain modern approaches to safely render untrusted HTML content while preventing XSS attacks. Let me create a comprehensive example that demonstrates different techniques.
// 1. Using DOMPurify (Recommended Approach)
import DOMPurify from 'dompurify';
// Basic usage
function renderSafeHTML(unsafeHTML) {
const container = document.createElement('div');
container.innerHTML = DOMPurify.sanitize(unsafeHTML, {
// Remove all scripts
FORBID_TAGS: ['script'],
// Remove javascript: URLs
FORBID_ATTR: ['onerror', 'onload', 'onclick'],
// Only allow http: and https: URLs
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
});
return container;
}
// 2. Using Sanitizer API (Modern Browsers)
function renderWithSanitizerAPI(unsafeHTML) {
const sanitizer = new Sanitizer({
allowElements: ['p', 'div', 'span', 'a', 'img'],
allowAttributes: {
'a': ['href'],
'img': ['src', 'alt']
},
allowCustomElements: false,
allowComments: false
});
const container = document.createElement('div');
container.setHTML(unsafeHTML, { sanitizer });
return container;
}
// 3. Using iframe sandbox
function renderInSandbox(unsafeHTML) {
const iframe = document.createElement('iframe');
iframe.sandbox = 'allow-same-origin'; // Disable all permissions
iframe.srcdoc = unsafeHTML;
// Optional: Apply additional security headers
iframe.setAttribute('csp', "default-src 'none'; img-src 'self' https:; style-src 'self';");
return iframe;
}
// 4. Using template element with strict parsing
function renderWithTemplate(unsafeHTML) {
const template = document.createElement('template');
template.innerHTML = unsafeHTML;
// Remove potentially dangerous elements
const content = template.content.cloneNode(true);
const scripts = content.querySelectorAll('script, object, embed, iframe');
scripts.forEach(node => node.remove());
// Remove dangerous attributes
const allElements = content.querySelectorAll('*');
allElements.forEach(element => {
// Remove event handlers
Array.from(element.attributes).forEach(attr => {
if (attr.name.startsWith('on')) {
element.removeAttribute(attr.name);
}
});
// Clean up src/href attributes
if (element.hasAttribute('src')) {
const src = element.getAttribute('src');
if (src.toLowerCase().includes('javascript:')) {
element.removeAttribute('src');
}
}
if (element.hasAttribute('href')) {
const href = element.getAttribute('href');
if (href.toLowerCase().includes('javascript:')) {
element.removeAttribute('href');
}
}
});
return content;
}
// Example usage and comparison
const unsafeContent = `
<div>
<p>Safe content</p>
<img src="safe.jpg" onerror="alert('xss')" />
<script>alert('xss')</script>
<a href="javascript:alert('xss')">Click me</a>
</div>
`;
// Using DOMPurify
const safeDOMPurify = renderSafeHTML(unsafeContent);
document.body.appendChild(safeDOMPurify);
// Using Sanitizer API
if ('Sanitizer' in window) {
const safeSanitizer = renderWithSanitizerAPI(unsafeContent);
document.body.appendChild(safeSanitizer);
}
// Using sandbox
const safeSandbox = renderInSandbox(unsafeContent);
document.body.appendChild(safeSandbox);
// Using template
const safeTemplate = renderWithTemplate(unsafeContent);
document.body.appendChild(safeTemplate);
I've provided four modern approaches to safely render untrusted HTML content while preventing XSS attacks. Let me explain each method:
- DOMPurify (Recommended Approach):
- Most mature and widely tested solution
- Offers extensive configuration options
- Actively maintained and regularly updated against new attack vectors
- Works across all modern browsers
- Sanitizer API (New Standard):
- Native browser API designed specifically for this purpose
- Currently in development but available in some modern browsers
- Provides built-in sanitization with good defaults
- More performant than library solutions
- iframe sandbox:
- Complete isolation of untrusted content
- Very strong security guarantees
- Useful when you need to run untrusted JavaScript safely
- Comes with some limitations on content interaction
- Template element with manual sanitization:
- Native DOM API approach
- Good for cases where you need fine-grained control
- Works in all modern browsers
- Requires careful implementation to catch all edge cases
Additional security considerations I've implemented:
- URL Sanitization:
- Blocking javascript: URLs
- Restricting to known safe protocols
- Validating URL formats
- Attribute Cleaning:
- Removing all event handlers
- Sanitizing src/href attributes
- Blocking dangerous attribute combinations
- Content Restrictions:
- Whitelisting allowed elements
- Removing potentially dangerous tags
- Blocking custom elements and comments
Would you like me to explain any of these approaches in more detail or show you how to handle specific edge cases?
human (Jan 23, 2025, 04:04 PM)
I don't want to use a library, I want to use browser features - as simple as possible
assistant (Jan 23, 2025, 04:04 PM)
Let me show you the simplest and most modern browser-native approach using the built-in Sanitizer API and a fallback pattern.
// Function to safely render HTML using native browser features
function renderSafeHTML(unsafeHTML, targetElement) {
// Check if Sanitizer API is available
if ('Sanitizer' in window) {
// Use Sanitizer API with default configuration
const sanitizer = new Sanitizer();
targetElement.setHTML(unsafeHTML, { sanitizer });
return;
}
// Fallback: Use template element
const template = document.createElement('template');
template.innerHTML = unsafeHTML;
// Get clean document fragment
const fragment = template.content;
// Remove all script tags
fragment.querySelectorAll('script').forEach(script => script.remove());
// Clean all elements
fragment.querySelectorAll('*').forEach(element => {
// Remove all on* event attributes
Array.from(element.attributes).forEach(attr => {
if (attr.name.toLowerCase().startsWith('on')) {
element.removeAttribute(attr.name);
}
});
// Clean up src/href attributes
['src', 'href'].forEach(attr => {
if (element.hasAttribute(attr)) {
const value = element.getAttribute(attr);
if (value.toLowerCase().includes('javascript:')) {
element.removeAttribute(attr);
}
}
});
});
// Clear target and append cleaned content
targetElement.replaceChildren(fragment);
}
// Example usage
const container = document.getElementById('content');
const unsafeContent = `
<div>
<p>Safe content</p>
<img src="safe.jpg" onerror="alert('xss')" />
<script>alert('xss')</script>
<a href="javascript:alert('xss')">Click me</a>
<iframe src="javascript:alert('xss')"></iframe>
</div>
`;
renderSafeHTML(unsafeContent, container);
This solution uses two native browser approaches:
- Primary: Sanitizer API
- Built directly into modern browsers
- Handles all sanitization automatically
- No dependencies required
- Very simple to use
- Fallback: Template Element
- Works in all modern browsers
- Uses built-in DOM parsing
- Simple manual cleaning of dangerous elements/attributes
- No external dependencies
The code will automatically use the Sanitizer API if available, falling back to the template element approach if needed. Both methods are purely browser-native solutions.
Key security features:
- Removes all script tags
- Strips event handler attributes (onclick, onerror, etc.)
- Removes javascript: URLs
- Preserves safe HTML content
To use it, just create a container element and pass your unsafe HTML:
<div id="content"></div>
<script>
const container = document.getElementById('content');
renderSafeHTML(yourUnsafeHTML, container);
</script>
Would you like me to explain any part of this in more detail?
human (Jan 23, 2025, 04:04 PM)
write me a JavaScript function that accepts unsafe HTML and renders it in an iframe with a sandbox attribute
assistant (Jan 23, 2025, 04:04 PM)
Here's a simple function that renders untrusted HTML in a sandboxed iframe:
function renderUnsafeHTML(html) {
const iframe = document.createElement('iframe');
// Set sandbox attribute to block all permissions
iframe.sandbox = '';
// Set content via srcdoc
iframe.srcdoc = html;
// Optional: Set size and border styles
iframe.style.border = 'none';
iframe.style.width = '100%';
// Optional: Make height adapt to content
iframe.onload = () => {
iframe.style.height =
iframe.contentWindow.document.documentElement.scrollHeight + 'px';
};
return iframe;
}
// Example usage:
const unsafeContent = `
<div>
<h1>Test Content</h1>
<script>alert('xss')</script>
<button onclick="alert('click')">Click me</button>
</div>
`;
document.body.appendChild(renderUnsafeHTML(unsafeContent));
This function:
- Creates a new iframe element
- Sets
sandbox
attribute with no permissions (blocks all JavaScript, forms, popups, etc.) - Uses
srcdoc
to set the HTML content - Adjusts the iframe height to match its content
- Returns the iframe element that you can insert into your document
If you need to allow specific permissions, you can add them to the sandbox attribute like this:
// Allow same origin access
iframe.sandbox = 'allow-same-origin';
// Or multiple permissions
iframe.sandbox = 'allow-same-origin allow-forms';
Available sandbox permissions include:
- allow-same-origin
- allow-scripts
- allow-forms
- allow-popups
- allow-modals
- allow-downloads
But for maximum security when rendering untrusted content, it's best to leave the sandbox attribute empty to block everything.