扫一扫分享
domPurify 是一个专业的html安全清理工具,专门用来防御跨站脚本(XSS)攻击。它就像一个严格的安检员,能够识别并移除HTML中的危险内容,只留下安全的代码。
超快速度:利用浏览器原生能力,性能优异
全面防护:支持 HTML、MathML 和 SVG 清理
广泛兼容:支持所有现代浏览器
高度可配置:可以根据需求定制清理规则
通过 npm/yarn 安装:
npm install dompurify
# 或
yarn add dompurifyCDN 引入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script>// 在表单提交时净化用户输入
import DOMPurify from 'dompurify';
function handleLogin() {
const username = DOMPurify.sanitize(document.getElementById('username').value);
const password = DOMPurify.sanitize(document.getElementById('password').value);
// 现在可以安全地使用这些值了
login(username, password);
}
// 净化用户输入的HTML内容
function displayUserContent(userInput) {
const cleanHTML = DOMPurify.sanitize(userInput);
document.getElementById('content').innerHTML = cleanHTML;
}看看 DOMPurify 如何清理危险代码:
// 危险的 HTML 输入
const dangerousInputs = [
'<img src=x onerror=alert("XSS")>',
'<svg><g/onload=alert("SVG攻击")></svg>',
'<p>正常内容<iframe src="javascript:alert(1)">恶意部分</p>',
'<a href="javascript:alert(\'点击劫持\')">点击我</a>'
];
// 净化后的安全结果
dangerousInputs.forEach(input => {
const clean = DOMPurify.sanitize(input);
console.log('输入:', input);
console.log('输出:', clean);
console.log('---');
});
/* 输出结果:
输入: <img src=x onerror=alert("XSS")>
输出: <img src="x">
输入: <svg><g/onload=alert("SVG攻击")></svg>
输出: <svg><g></g></svg>
输入: <p>正常内容<iframe src="javascript:alert(1)">恶意部分</p>
输出: <p>正常内容</p>
输入: <a href="javascript:alert('点击劫持')">点击我</a>
输出: <a>点击我</a>
*/// 博客评论系统配置 - 只允许基本的格式标签
const blogCommentConfig = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a', 'code'],
ALLOWED_ATTR: ['href', 'title'],
FORBID_TAGS: ['script', 'style', 'img', 'iframe'],
FORBID_ATTR: ['style', 'class', 'onclick', 'onmouseover']
};
// 用户个人简介配置 - 允许更多样式
const userProfileConfig = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a', 'img', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'src', 'alt', 'title'],
ALLOWED_URI_REGEXP: /^(https?|ftp):/i
};
function sanitizeComment(userInput) {
return DOMPurify.sanitize(userInput, blogCommentConfig);
}
function sanitizeProfile(userInput) {
return DOMPurify.sanitize(userInput, userProfileConfig);
}// CKEditor 或 TinyMCE 内容净化
const richTextConfig = {
ALLOWED_TAGS: [
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'strong', 'em', 'u', 's', 'blockquote', 'code',
'pre', 'ul', 'ol', 'li', 'a', 'img', 'br', 'hr'
],
ALLOWED_ATTR: [
'href', 'title', 'src', 'alt', 'class',
'>, '>// 允许自定义数据属性
],
ALLOWED_URI_REGEXP: /^(https?|ftp|data):/i,
FORBID_ATTR: ['style', 'onclick']
};
class RichTextProcessor {
constructor() {
this.config = richTextConfig;
}
// 处理编辑器内容
processContent(htmlContent) {
return DOMPurify.sanitize(htmlContent, this.config);
}
// 批量处理多个内容
processMultiple(contents) {
return contents.map(content =>
DOMPurify.sanitize(content, this.config)
);
}
}
// 使用示例
const processor = new RichTextProcessor();
const userArticle = editor.getContent(); // 从富文本编辑器获取
const safeArticle = processor.processContent(userArticle);// 移除所有外部图片,只允许内嵌base64图片
DOMPurify.addHook('beforeSanitizeAttributes', (node) => {
if (node.tagName === 'IMG') {
const src = node.getAttribute('src');
if (src && !src.startsWith('data:')) {
node.removeAttribute('src');
}
}
});
// 为所有外部链接添加 nofollow 和 target="_blank"
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
if (node.tagName === 'A') {
const href = node.getAttribute('href');
if (href && href.startsWith('http')) {
node.setAttribute('rel', 'nofollow noopener');
node.setAttribute('target', '_blank');
}
}
});class CommentSystem {
constructor() {
this.config = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a', 'code'],
ALLOWED_ATTR: ['href'],
ALLOWED_URI_REGEXP: /^https?:\/\/(www\.)?(example\.com|trusted-site\.org)/i,
FORBID_TAGS: ['script', 'style', 'img']
};
}
addComment(userInput, userName) {
// 净化用户输入
const cleanContent = DOMPurify.sanitize(userInput, this.config);
// 创建安全的评论元素
const commentElement = this.createCommentElement(cleanContent, userName);
// 添加到页面
document.getElementById('comments').appendChild(commentElement);
}
createCommentElement(content, userName) {
const div = document.createElement('div');
div.className = 'comment';
div.innerHTML = `
<div>
<strong>${DOMPurify.sanitize(userName)}</strong>
</div>
<div>${content}</div>
`;
return div;
}
}class NotificationSystem {
constructor() {
this.config = {
ALLOWED_TAGS: ['strong', 'em', 'a'],
ALLOWED_ATTR: ['href'],
ALLOWED_URI_REGEXP: /^https?:\/\//i
};
}
showNotification(message, type = 'info') {
const cleanMessage = DOMPurify.sanitize(message, this.config);
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = cleanMessage;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 5000);
}
}
// 使用示例
const notifier = new NotificationSystem();
notifier.showNotification('欢迎 <strong>新用户</strong>!点击<a href="/welcome">这里</a>查看指南。');class DynamicContentLoader {
constructor() {
this.config = {
ALLOWED_TAGS: ['div', 'p', 'span', 'strong', 'em', 'a', 'img', 'br'],
ALLOWED_ATTR: ['class', 'href', 'src', 'alt', 'title'],
ALLOWED_URI_REGEXP: /^(https?|data):/i
};
}
async loadUserGeneratedContent(userId) {
try {
const response = await fetch(`/api/user-content/${userId}`);
const data = await response.json();
// 净化用户生成的内容
const cleanContent = DOMPurify.sanitize(data.content, this.config);
// 安全地显示内容
this.renderContent(cleanContent);
} catch (error) {
console.error('加载内容失败:', error);
}
}
renderContent(cleanHTML) {
const container = document.getElementById('user-content');
container.innerHTML = cleanHTML;
}
}class SecurityManager {
constructor() {
this.domPurifyConfig = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a'],
ALLOWED_ATTR: ['href'],
ALLOWED_URI_REGEXP: /^https?:\/\//i
};
}
// 多层验证
validateAndSanitize(input, maxLength = 1000) {
// 第一层:长度限制
if (input.length > maxLength) {
throw new Error('输入内容过长');
}
// 第二层:基础字符检查
if (/[<>]/.test(input) && !/<[^>]*>/.test(input)) {
throw new Error('包含危险字符');
}
// 第三层:DOMPurify 净化
return DOMPurify.sanitize(input, this.domPurifyConfig);
}
}class SafeDOMPurify {
constructor(config = {}) {
this.defaultConfig = {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
ALLOWED_ATTR: [],
...config
};
}
safeSanitize(html, customConfig = {}) {
try {
const config = { ...this.defaultConfig, ...customConfig };
const result = DOMPurify.sanitize(html, config);
// 记录净化统计(生产环境中发送到监控系统)
this.logSanitization(html, result);
return result;
} catch (error) {
console.error('DOMPurify 处理失败:', error);
// 返回空字符串作为安全降级
return '';
}
}
logSanitization(original, sanitized) {
const stats = {
originalLength: original.length,
sanitizedLength: sanitized.length,
reduction: ((original.length - sanitized.length) / original.length * 100).toFixed(2),
timestamp: new Date().toISOString()
};
console.log('净化统计:', stats);
}
}// 批量净化多个内容
function batchSanitize(contents, config) {
return contents.map(content =>
DOMPurify.sanitize(content, config)
);
}
// 使用 Worker 处理大量内容
class DOMPurifyWorker {
constructor() {
this.worker = new Worker('dompurify-worker.js');
}
sanitizeInWorker(html, config) {
return new Promise((resolve) => {
this.worker.postMessage({ html, config });
this.worker.onmessage = (e) => resolve(e.data);
});
}
}class OptimizedSanitizer {
constructor() {
this.configCache = new Map();
}
getConfig(profile) {
if (!this.configCache.has(profile)) {
const config = this.createConfig(profile);
this.configCache.set(profile, config);
}
return this.configCache.get(profile);
}
createConfig(profile) {
const profiles = {
'comment': { ALLOWED_TAGS: ['p', 'br', 'strong', 'em'] },
'article': { ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'strong', 'em', 'a', 'img'] },
'minimal': { ALLOWED_TAGS: [] }
};
return profiles[profile] || profiles.minimal;
}
}DOMPurify 是前端安全的重要防线,通过合理配置和使用,可以有效地防御 XSS 攻击。记住这些关键点:
始终净化用户输入:不要信任任何来自用户的内容
按需配置:根据具体场景设置合适的允许标签和属性
多层防御:结合其他安全措施,形成完整的防护体系
监控和测试:定期检查净化效果,确保安全策略有效
通过正确使用 DOMPurify,你可以大大提升网站的安全性,保护用户数据不受攻击。
仅供个人学习参考/导航指引使用,具体请以第三方网站说明为准,本站不提供任何专业建议。如果地址失效或描述有误,请联系站长反馈~感谢您的理解与支持!
手机预览