安全与最佳实践
Web 安全是前端开发中至关重要的部分。不安全的 HTML 代码可能导致跨站脚本攻击(XSS)、点击劫持、数据泄露等严重安全问题。本文档将介绍 HTML 开发中的安全最佳实践,包括安全响应头、XSS 防护、表单安全和外链安全等内容。
HTTP 安全响应头是服务器发送给浏览器的安全指令,用于防止各种类型的攻击。这些头部应该在服务器端配置,但了解它们对于前端开发者同样重要。
1.1 Content-Security-Policy (CSP)
Content-Security-Policy(内容安全策略)是最重要的安全头部之一,用于防止 XSS 攻击。
基本语法
1
| Content-Security-Policy: directive1 value1; directive2 value2;
|
常用指令
| 指令 |
说明 |
示例 |
default-src |
所有资源类型的默认策略 |
default-src 'self' |
script-src |
控制脚本来源 |
script-src 'self' 'unsafe-inline' |
style-src |
控制样式表来源 |
style-src 'self' https://fonts.googleapis.com |
img-src |
控制图片来源 |
img-src 'self' data: https: |
font-src |
控制字体来源 |
font-src 'self' https://fonts.gstatic.com |
connect-src |
控制 AJAX/WebSocket 连接 |
connect-src 'self' https://api.example.com |
frame-src |
控制 iframe 来源 |
frame-src 'self' https://trusted-site.com |
object-src |
控制 object/embed/applet |
object-src 'none' |
media-src |
控制 audio/video 来源 |
media-src 'self' |
form-action |
控制表单提交目标 |
form-action 'self' |
base-uri |
控制 <base> 标签的 href |
base-uri 'self' |
frame-ancestors |
控制页面是否可被嵌入 iframe |
frame-ancestors 'none' |
report-uri |
违规报告 URL |
report-uri /csp-report |
常用值
| 值 |
说明 |
'self' |
允许同源资源 |
'unsafe-inline' |
允许内联脚本/样式(不安全) |
'unsafe-eval' |
允许 eval() 等动态代码执行(不安全) |
'none' |
禁止所有 |
https: |
允许所有 HTTPS 来源 |
data: |
允许 data: URL |
*.example.com |
允许特定域名 |
CSP 示例
严格的 CSP(推荐):
1 2 3 4 5 6 7 8 9 10 11
| Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self';
|
在 HTML meta 标签中使用:
1 2 3 4 5 6 7
| <meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; ">
|
注意:meta 标签的 CSP 有一些限制,推荐在服务器端设置。
渐进式实施 CSP
如果现有网站大量使用内联脚本,可以逐步实施:
第一步:报告模式(只报告,不阻止)
1 2 3 4
| Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report;
|
第二步:启用 CSP(真正阻止违规)
1 2 3 4
| Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri /csp-report;
|
第三步:逐步收紧(移除 unsafe-inline)
1 2 3 4
| Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-随机字符串'; report-uri /csp-report;
|
Nonce 和 Hash
如果必须使用内联脚本,可以使用 nonce 或 hash:
使用 Nonce:
1
| Content-Security-Policy: script-src 'self' 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa';
|
1 2 3 4
| <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"> console.log('安全的内联脚本'); </script>
|
使用 Hash:
1
| Content-Security-Policy: script-src 'self' 'sha256-abc123...';
|
浏览器会计算脚本的 hash 值,只有匹配的脚本才会执行。
1.2 X-Frame-Options
防止页面被嵌入到 iframe 中(点击劫持防护)。
可选值:
DENY:完全禁止被嵌入
SAMEORIGIN:只允许同源页面嵌入
ALLOW-FROM uri:允许指定 URI 嵌入(已废弃)
推荐配置:
或者使用更现代的 CSP 方式:
1
| Content-Security-Policy: frame-ancestors 'none'
|
注意:如果使用了 CSP 的 frame-ancestors,X-Frame-Options 会被忽略。
1.3 X-Content-Type-Options
防止浏览器 MIME 类型嗅探。
1
| X-Content-Type-Options: nosniff
|
作用:强制浏览器使用服务器指定的 Content-Type,防止将文本文件当作脚本执行。
推荐配置:
1
| X-Content-Type-Options: nosniff
|
1.4 Referrer-Policy
控制浏览器发送 Referer 头的信息。
1
| Referrer-Policy: strict-origin-when-cross-origin
|
可选值:
| 值 |
说明 |
no-referrer |
不发送 Referer |
no-referrer-when-downgrade |
降级(HTTPS→HTTP)时不发送(默认) |
origin |
只发送源(协议+域名) |
origin-when-cross-origin |
跨域时只发送源 |
same-origin |
同源时发送完整 URL |
strict-origin |
只发送源,降级时不发送 |
strict-origin-when-cross-origin |
同源发送完整,跨域发送源,降级时不发送(推荐) |
unsafe-url |
始终发送完整 URL(不安全) |
推荐配置:
1
| Referrer-Policy: strict-origin-when-cross-origin
|
在 HTML 中设置:
1
| <meta name="referrer" content="strict-origin-when-cross-origin">
|
1.5 Permissions-Policy(原 Feature-Policy)
控制浏览器功能的使用权限。
1
| Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self)
|
常用指令:
| 功能 |
说明 |
geolocation |
地理位置 |
microphone |
麦克风 |
camera |
摄像头 |
payment |
支付 API |
usb |
USB 设备 |
fullscreen |
全屏 |
autoplay |
自动播放 |
示例:
1 2 3 4 5
| Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self "https://payment.example.com")
|
在 HTML 中设置:
1 2
| <meta http-equiv="Permissions-Policy" content="geolocation=(), microphone=(), camera=()">
|
1.6 Strict-Transport-Security (HSTS)
强制使用 HTTPS 连接。
1
| Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
参数说明:
max-age:有效期(秒)
includeSubDomains:包含子域名
preload:加入浏览器 HSTS 预加载列表
推荐配置:
1
| Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
1.7 完整的安全响应头配置示例
Nginx 配置示例:
1 2 3 4 5 6
| add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self';" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(self)" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
Apache 配置示例:
1 2 3 4
| Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
2. XSS(跨站脚本攻击)防护
XSS 是最常见的 Web 安全漏洞之一,攻击者通过在网页中注入恶意脚本,窃取用户信息或执行恶意操作。
2.1 XSS 攻击类型
反射型 XSS
恶意脚本通过 URL 参数等方式反射到页面中。
攻击示例:
1 2 3 4 5
| <div>欢迎, <?php echo $_GET['name']; ?></div>
http://example.com/?name=<script>alert(document.cookie)</script>
|
存储型 XSS
恶意脚本被存储到数据库中,每次访问时都会执行。
攻击示例:
1 2 3 4 5
| <script>alert('XSS')</script>
<div><?php echo $comment; ?></div>
|
DOM 型 XSS
通过修改 DOM 结构导致脚本执行。
攻击示例:
1 2 3 4 5
| document.getElementById('output').innerHTML = location.hash.substring(1);
http:
|
2.2 XSS 防护措施
1. 输入验证和过滤
服务器端验证:
1 2 3 4 5 6 7 8
| $input = strip_tags($userInput);
$input = filter_var($userInput, FILTER_SANITIZE_STRING);
$input = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
|
客户端验证(仅作辅助,不能依赖):
1 2 3 4 5 6 7 8
| function sanitizeInput(input) { const div = document.createElement('div'); div.textContent = input; return div.innerHTML; }
const safeInput = sanitizeInput(userInput);
|
2. 输出编码
HTML 实体编码:
1 2
| <script>alert('XSS')</script>
|
JavaScript 中的编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| element.textContent = userInput;
function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); }
|
URL 编码:
1 2
| const safeUrl = encodeURIComponent(userInput);
|
3. 使用安全的 API
避免使用危险的 DOM API:
1 2 3 4 5 6 7 8 9
| element.innerHTML = userInput; document.write(userInput); eval(userInput); setTimeout(userInput, 100);
element.textContent = userInput; element.setAttribute('data-value', userInput);
|
4. Content Security Policy (CSP)
如前所述,CSP 是最有效的 XSS 防护措施之一。
1
| Content-Security-Policy: script-src 'self'; object-src 'none';
|
5. HttpOnly Cookie
防止 JavaScript 访问敏感 Cookie。
1
| Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Strict
|
2.3 XSS 防护最佳实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"> <title>安全的页面</title> </head> <body> <div id="output"></div> <script> function displayUserInput(input) { const output = document.getElementById('output'); output.textContent = input; } function displayHtml(htmlString) { const output = document.getElementById('output'); const temp = document.createElement('div'); temp.textContent = htmlString; output.appendChild(temp); } function getUrlParam(name) { const params = new URLSearchParams(window.location.search); return params.get(name); } </script> </body> </html>
|
3. 表单安全
表单是用户输入的主要入口,也是最容易受到攻击的地方。
3.1 输入验证
HTML5 验证属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <form> <input type="text" name="username" required> <input type="email" name="email" required> <input type="tel" name="phone" pattern="[0-9]{10,11}"> <input type="text" name="title" maxlength="100" minlength="5"> <input type="number" name="age" min="18" max="100"> <input type="file" accept="image/*" max="5242880"> <button type="submit">提交</button> </form>
|
客户端验证(JavaScript)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <form id="myForm"> <input type="text" name="username" id="username" required> <input type="email" name="email" id="email" required> <button type="submit">提交</button> </form>
<script> document.getElementById('myForm').addEventListener('submit', function(e) { e.preventDefault(); const username = document.getElementById('username').value; const email = document.getElementById('email').value; if (!/^[a-zA-Z0-9_]{3,20}$/.test(username)) { alert('用户名必须是3-20个字母、数字或下划线'); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { alert('请输入有效的邮箱地址'); return; } this.submit(); }); </script>
|
重要提醒:客户端验证只能提升用户体验,不能作为唯一的安全措施,服务器端验证是必需的。
3.2 CSRF(跨站请求伪造)防护
CSRF Token
在表单中添加 CSRF token,服务器验证 token 的有效性。
1 2 3 4 5 6 7
| <form method="POST" action="/submit"> <input type="hidden" name="csrf_token" value="随机生成的token"> <input type="text" name="data"> <button type="submit">提交</button> </form>
|
JavaScript 获取 token:
1 2 3 4 5 6 7 8 9 10 11 12
| const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': token }, body: JSON.stringify({ data: 'value' }) });
|
SameSite Cookie
1
| Set-Cookie: sessionid=abc123; SameSite=Strict; Secure; HttpOnly
|
SameSite 值:
Strict:完全禁止跨站发送 Cookie
Lax:GET 请求允许跨站,POST 禁止(推荐)
None:允许跨站(需要 Secure)
3.3 密码安全
密码输入字段
1 2 3 4
| <input type="password" name="password" minlength="8" required autocomplete="new-password">
|
密码强度验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function validatePassword(password) { const minLength = 8; const hasUpperCase = /[A-Z]/.test(password); const hasLowerCase = /[a-z]/.test(password); const hasNumbers = /\d/.test(password); const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password); if (password.length < minLength) { return { valid: false, message: '密码至少需要8个字符' }; } if (!hasUpperCase || !hasLowerCase) { return { valid: false, message: '密码必须包含大小写字母' }; } if (!hasNumbers) { return { valid: false, message: '密码必须包含数字' }; } if (!hasSpecialChar) { return { valid: false, message: '密码必须包含特殊字符' }; } return { valid: true }; }
|
密码确认
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <input type="password" name="password" id="password" required> <input type="password" name="password_confirm" id="password_confirm" required>
<script> document.getElementById('password_confirm').addEventListener('input', function() { const password = document.getElementById('password').value; const confirm = this.value; if (password !== confirm) { this.setCustomValidity('密码不匹配'); } else { this.setCustomValidity(''); } }); </script>
|
3.4 文件上传安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <form enctype="multipart/form-data"> <input type="file" name="upload" accept=".jpg,.jpeg,.png,.gif" required> <small>最大文件大小:5MB</small> <button type="submit">上传</button> </form>
<script> document.querySelector('input[type="file"]').addEventListener('change', function(e) { const file = e.target.files[0]; if (!file) return; if (file.size > 5 * 1024 * 1024) { alert('文件大小不能超过 5MB'); this.value = ''; return; } const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if (!allowedTypes.includes(file.type)) { alert('只允许上传 JPG、PNG 或 GIF 图片'); this.value = ''; return; } }); </script>
|
服务器端验证(必需):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const multer = require('multer'); const path = require('path');
const upload = multer({ limits: { fileSize: 5 * 1024 * 1024 }, fileFilter: (req, file, cb) => { const allowedTypes = /jpeg|jpg|png|gif/; const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase()); const mimetype = allowedTypes.test(file.mimetype); if (mimetype && extname) { return cb(null, true); } else { cb(new Error('只允许上传图片文件')); } } });
|
3.5 表单提交安全
防止重复提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <form id="myForm"> <input type="text" name="data" required> <button type="submit" id="submitBtn">提交</button> </form>
<script> let isSubmitting = false; document.getElementById('myForm').addEventListener('submit', function(e) { if (isSubmitting) { e.preventDefault(); return false; } isSubmitting = true; const submitBtn = document.getElementById('submitBtn'); submitBtn.disabled = true; submitBtn.textContent = '提交中...'; }); </script>
|
安全的表单提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const form = document.getElementById('myForm');
form.addEventListener('submit', async function(e) { e.preventDefault(); const formData = new FormData(this); const csrfToken = document.querySelector('meta[name="csrf-token"]').content; try { const response = await fetch('/api/submit', { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: formData }); if (!response.ok) { throw new Error('提交失败'); } const result = await response.json(); alert('提交成功'); } catch (error) { console.error('错误:', error); alert('提交失败,请稍后重试'); } });
|
4. 外链安全
外部链接可能指向恶意网站或导致安全问题,需要谨慎处理。
4.1 链接的 rel 属性
rel=”noopener”
防止新打开的页面访问 window.opener,防止潜在的安全风险。
1 2 3 4 5
| <a href="https://external-site.com" target="_blank" rel="noopener">外部链接</a>
<a href="https://external-site.com" target="_blank">外部链接</a>
|
rel=”noreferrer”
在 noopener 的基础上,还阻止发送 Referer 头。
1
| <a href="https://external-site.com" target="_blank" rel="noreferrer">外部链接</a>
|
rel=”noopener noreferrer”(推荐)
同时使用两个属性,提供最好的安全保护。
1 2 3
| <a href="https://external-site.com" target="_blank" rel="noopener noreferrer"> 外部链接 </a>
|
为什么需要 noopener:
恶意网站可能通过以下方式利用 window.opener:
1 2 3 4 5
| if (window.opener) { window.opener.location = 'http://phishing-site.com'; }
|
4.2 验证外部链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function isExternalLink(url) { try { const linkUrl = new URL(url, window.location.origin); return linkUrl.origin !== window.location.origin; } catch { return false; } }
function createSafeLink(href, text) { const link = document.createElement('a'); link.href = href; link.textContent = text; if (isExternalLink(href)) { link.target = '_blank'; link.rel = 'noopener noreferrer'; } return link; }
document.querySelectorAll('a[href^="http"]').forEach(link => { if (isExternalLink(link.href)) { if (!link.rel.includes('noopener')) { link.rel = (link.rel ? link.rel + ' ' : '') + 'noopener noreferrer'; } } });
|
4.3 白名单验证
对于用户生成的内容中的链接,应该验证其安全性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const allowedDomains = [ 'example.com', 'trusted-site.com', 'another-trusted.com' ];
function isAllowedDomain(url) { try { const urlObj = new URL(url); return allowedDomains.includes(urlObj.hostname); } catch { return false; } }
function sanitizeLink(href) { if (!isAllowedDomain(href)) { return '/warning?url=' + encodeURIComponent(href); } return href; }
|
4.4 危险的 URL 协议
避免使用危险的 URL 协议。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const dangerousProtocols = ['javascript:', 'data:', 'vbscript:', 'file:'];
function isDangerousUrl(url) { const protocol = url.split(':')[0].toLowerCase(); return dangerousProtocols.includes(protocol); }
function createSafeLink(href, text) { if (isDangerousUrl(href)) { console.warn('危险的 URL:', href); return null; } const link = document.createElement('a'); link.href = href; link.textContent = text; return link; }
|
4.5 链接安全检查清单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <a href="/page.html">内部链接</a> <a href="#section">锚点链接</a>
<a href="https://example.com" target="_blank" rel="noopener noreferrer" title="在新窗口打开"> 外部链接 </a>
<a href="https://example.com" target="_blank">外部链接</a>
<a href="javascript:alert('XSS')">危险链接</a> <a href="data:text/html,<script>alert('XSS')</script>">危险链接</a>
|
5. 其他安全最佳实践
5.1 HTTPS 使用
所有网站都应该使用 HTTPS。
1 2
| <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
|
或者在服务器配置:
1
| Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
5.2 敏感信息处理
不要在 HTML 中暴露敏感信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div data-api-key="sk_live_abc123xyz">...</div> <script> const apiKey = 'sk_live_abc123xyz'; </script>
<script> fetch('/api/get-config') .then(res => res.json()) .then(config => { }); </script>
|
5.3 防止信息泄露
避免在错误信息中泄露敏感信息:
1 2 3 4 5 6 7 8 9
| <div class="error"> 数据库连接失败:用户 root,密码 123456 </div>
<div class="error"> 系统错误,请稍后重试 </div>
|
5.4 安全的 JSON-LD
在使用结构化数据时,注意安全:
1 2 3 4 5 6 7 8 9
| <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Person", "name": "张三", } </script>
|
5.5 安全测试工具
推荐使用以下工具检查网站安全性:
6. 完整的安全配置示例
6.1 HTML 页面安全配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self' 'nonce-随机字符串'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; object-src 'none'; "> <meta http-equiv="X-Frame-Options" content="DENY"> <meta http-equiv="X-Content-Type-Options" content="nosniff"> <meta name="referrer" content="strict-origin-when-cross-origin"> <meta name="csrf-token" content="服务器生成的token"> <title>安全示例页面</title> </head> <body> <form id="safeForm" method="POST"> <input type="hidden" name="csrf_token" id="csrf_token"> <input type="text" name="username" required minlength="3" maxlength="20"> <input type="email" name="email" required> <input type="password" name="password" required minlength="8"> <button type="submit">提交</button> </form> <a href="https://example.com" target="_blank" rel="noopener noreferrer"> 安全的外部链接 </a> <script nonce="随机字符串"> document.getElementById('csrf_token').value = document.querySelector('meta[name="csrf-token"]').content; document.getElementById('safeForm').addEventListener('submit', async function(e) { e.preventDefault(); const formData = new FormData(this); const csrfToken = document.querySelector('meta[name="csrf-token"]').content; try { const response = await fetch('/api/submit', { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: formData }); if (!response.ok) throw new Error('提交失败'); alert('提交成功'); } catch (error) { console.error('错误:', error); alert('提交失败'); } }); document.querySelectorAll('a[href^="http"]').forEach(link => { if (new URL(link.href).origin !== window.location.origin) { link.rel = (link.rel ? link.rel + ' ' : '') + 'noopener noreferrer'; } }); </script> </body> </html>
|
6.2 服务器配置示例(Nginx)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self';" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(self)" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; location / { root /var/www/html; index index.html; } }
|
总结
本文档详细介绍了 HTML 开发中的安全最佳实践:
- 安全响应头:CSP、X-Frame-Options、HSTS 等
- XSS 防护:输入验证、输出编码、CSP 策略
- 表单安全:输入验证、CSRF 防护、密码安全、文件上传
- 外链安全:rel=”noopener noreferrer”、链接验证
- 其他实践:HTTPS、敏感信息处理、安全测试
关键要点:
- 永远不要信任用户输入:所有输入都要验证和过滤
- 使用 HTTPS:保护数据传输安全
- 实施 CSP:最有效的 XSS 防护措施
- **外部链接使用 rel=”noopener noreferrer”**:防止恶意网站利用
- 服务器端验证:客户端验证只是辅助,不能作为唯一安全措施
- 保持更新:定期更新依赖和安全配置
- 安全测试:使用工具定期检查网站安全性
安全是一个持续的过程,需要在前端、后端、服务器配置等多个层面协同工作。掌握这些最佳实践可以帮助你构建更安全的 Web 应用。