- 发布于
前端安全防护实践:XSS、CSRF防护与内容安全策略
- 作者

- 姓名
- 全能波
- GitHub
- @weicracker
前端安全防护实践:XSS、CSRF防护与内容安全策略
前端安全是Web应用的重要基石,本文将分享前端安全防护的实战经验,涵盖常见攻击手段的防护策略和最佳实践。
XSS攻击防护
XSS攻击类型与防护
// XSS防护工具类
class XSSProtection {
constructor() {
this.htmlEntities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
};
}
// HTML实体编码
escapeHtml(str) {
if (typeof str !== 'string') return str;
return str.replace(/[&<>"'`=\/]/g, (match) => {
return this.htmlEntities[match];
});
}
// 属性值编码
escapeAttribute(str) {
if (typeof str !== 'string') return str;
return str.replace(/[&<>"']/g, (match) => {
return this.htmlEntities[match];
});
}
// JavaScript字符串编码
escapeJS(str) {
if (typeof str !== 'string') return str;
return str.replace(/[\\'"<>&\r\n\t]/g, (match) => {
const escapeMap = {
'\\': '\\\\',
"'": "\\'",
'"': '\\"',
'<': '\\u003c',
'>': '\\u003e',
'&': '\\u0026',
'\r': '\\r',
'\n': '\\n',
'\t': '\\t'
};
return escapeMap[match];
});
}
// URL编码
escapeURL(str) {
if (typeof str !== 'string') return str;
return encodeURIComponent(str);
}
// CSS编码
escapeCSS(str) {
if (typeof str !== 'string') return str;
return str.replace(/[<>"'&\\\r\n]/g, (match) => {
return '\\' + match.charCodeAt(0).toString(16) + ' ';
});
}
// 安全的innerHTML替代方案
safeSetHTML(element, html) {
// 创建临时元素进行清理
const temp = document.createElement('div');
temp.innerHTML = this.escapeHtml(html);
// 移除所有脚本标签
const scripts = temp.querySelectorAll('script');
scripts.forEach(script => script.remove());
// 移除危险属性
const dangerousAttrs = ['onclick', 'onload', 'onerror', 'onmouseover'];
const allElements = temp.querySelectorAll('*');
allElements.forEach(el => {
dangerousAttrs.forEach(attr => {
if (el.hasAttribute(attr)) {
el.removeAttribute(attr);
}
});
// 检查href和src属性
if (el.hasAttribute('href')) {
const href = el.getAttribute('href');
if (href.startsWith('javascript:')) {
el.removeAttribute('href');
}
}
if (el.hasAttribute('src')) {
const src = el.getAttribute('src');
if (src.startsWith('javascript:')) {
el.removeAttribute('src');
}
}
});
element.innerHTML = temp.innerHTML;
}
// 验证URL安全性
isValidURL(url) {
try {
const urlObj = new URL(url);
const allowedProtocols = ['http:', 'https:', 'mailto:', 'tel:'];
if (!allowedProtocols.includes(urlObj.protocol)) {
return false;
}
// 检查是否为恶意域名
const maliciousDomains = ['evil.com', 'malware.net'];
if (maliciousDomains.includes(urlObj.hostname)) {
return false;
}
return true;
} catch (error) {
return false;
}
}
// 安全的动态脚本加载
loadScript(src, options = {}) {
return new Promise((resolve, reject) => {
if (!this.isValidURL(src)) {
reject(new Error('Invalid script URL'));
return;
}
const script = document.createElement('script');
script.src = src;
script.async = options.async !== false;
script.defer = options.defer || false;
// 添加完整性检查
if (options.integrity) {
script.integrity = options.integrity;
script.crossOrigin = 'anonymous';
}
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
}
}
// 全局XSS防护实例
const xssProtection = new XSSProtection();
// React组件中的安全实践
import React from 'react';
import DOMPurify from 'dompurify';
function SafeComponent({ userContent, userUrl, userName }) {
// 安全显示用户内容
const sanitizedContent = DOMPurify.sanitize(userContent, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u'],
ALLOWED_ATTR: []
});
// 安全显示用户名
const safeName = xssProtection.escapeHtml(userName);
// 安全处理用户URL
const safeUrl = xssProtection.isValidURL(userUrl) ? userUrl : '#';
return (
<div>
<h3>{safeName}</h3>
<div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />
<a href={safeUrl} target="_blank" rel="noopener noreferrer">
访问链接
</a>
</div>
);
}
输入验证与过滤
// 输入验证工具类
class InputValidator {
constructor() {
this.patterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phone: /^[\d\-\+\(\)\s]+$/,
alphanumeric: /^[a-zA-Z0-9]+$/,
safeString: /^[a-zA-Z0-9\s\-_.,!?]+$/,
};
}
// 验证邮箱
validateEmail(email) {
if (!email || typeof email !== 'string') return false;
// 长度检查
if (email.length > 254) return false;
// 格式检查
if (!this.patterns.email.test(email)) return false;
// 域名白名单检查
const allowedDomains = ['gmail.com', 'outlook.com', 'company.com'];
const domain = email.split('@')[1];
if (allowedDomains.length > 0 && !allowedDomains.includes(domain)) {
return false;
}
return true;
}
// 验证用户名
validateUsername(username) {
if (!username || typeof username !== 'string') return false;
// 长度检查
if (username.length < 3 || username.length > 20) return false;
// 字符检查
if (!this.patterns.alphanumeric.test(username)) return false;
// 保留词检查
const reservedWords = ['admin', 'root', 'system', 'null', 'undefined'];
if (reservedWords.includes(username.toLowerCase())) return false;
return true;
}
// 验证密码强度
validatePassword(password) {
if (!password || typeof password !== 'string') {
return { valid: false, message: '密码不能为空' };
}
if (password.length < 8) {
return { valid: false, message: '密码长度至少8位' };
}
if (password.length > 128) {
return { valid: false, message: '密码长度不能超过128位' };
}
const hasLower = /[a-z]/.test(password);
const hasUpper = /[A-Z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
const strength = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;
if (strength < 3) {
return {
valid: false,
message: '密码必须包含大写字母、小写字母、数字和特殊字符中的至少3种'
};
}
// 检查常见弱密码
const weakPasswords = ['password', '123456', 'qwerty', 'admin'];
if (weakPasswords.includes(password.toLowerCase())) {
return { valid: false, message: '密码过于简单' };
}
return { valid: true, strength };
}
// 通用输入清理
sanitizeInput(input, type = 'text') {
if (typeof input !== 'string') return '';
// 移除控制字符
input = input.replace(/[\x00-\x1F\x7F]/g, '');
// 限制长度
const maxLengths = {
text: 1000,
name: 50,
email: 254,
url: 2048,
comment: 5000
};
const maxLength = maxLengths[type] || maxLengths.text;
if (input.length > maxLength) {
input = input.substring(0, maxLength);
}
// 根据类型进行特定清理
switch (type) {
case 'name':
input = input.replace(/[^a-zA-Z\s\-']/g, '');
break;
case 'alphanumeric':
input = input.replace(/[^a-zA-Z0-9]/g, '');
break;
case 'numeric':
input = input.replace(/[^0-9]/g, '');
break;
case 'url':
// URL特殊处理
try {
new URL(input);
} catch {
input = '';
}
break;
}
return input.trim();
}
// 文件上传验证
validateFile(file, options = {}) {
const {
maxSize = 5 * 1024 * 1024, // 5MB
allowedTypes = ['image/jpeg', 'image/png', 'image/gif'],
allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
} = options;
// 文件大小检查
if (file.size > maxSize) {
return { valid: false, message: '文件大小超出限制' };
}
// 文件类型检查
if (!allowedTypes.includes(file.type)) {
return { valid: false, message: '不支持的文件类型' };
}
// 文件扩展名检查
const extension = '.' + file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(extension)) {
return { valid: false, message: '不支持的文件扩展名' };
}
// 文件名安全检查
const safeName = file.name.replace(/[^a-zA-Z0-9.-]/g, '_');
if (safeName !== file.name) {
return {
valid: true,
message: '文件名包含特殊字符,已自动处理',
safeName
};
}
return { valid: true };
}
}
// 全局输入验证实例
const inputValidator = new InputValidator();
CSRF攻击防护
CSRF Token实现
// CSRF防护工具类
class CSRFProtection {
constructor() {
this.tokenName = 'csrf_token';
this.headerName = 'X-CSRF-Token';
this.cookieName = 'csrf_cookie';
}
// 生成CSRF Token
generateToken() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
// 设置CSRF Token
setToken() {
const token = this.generateToken();
// 存储到sessionStorage
sessionStorage.setItem(this.tokenName, token);
// 设置到meta标签
let metaTag = document.querySelector(`meta[name="${this.tokenName}"]`);
if (!metaTag) {
metaTag = document.createElement('meta');
metaTag.name = this.tokenName;
document.head.appendChild(metaTag);
}
metaTag.content = token;
// 设置到cookie(HttpOnly由服务端设置)
document.cookie = `${this.cookieName}=${token}; path=/; secure; samesite=strict`;
return token;
}
// 获取CSRF Token
getToken() {
// 优先从sessionStorage获取
let token = sessionStorage.getItem(this.tokenName);
if (!token) {
// 从meta标签获取
const metaTag = document.querySelector(`meta[name="${this.tokenName}"]`);
token = metaTag ? metaTag.content : null;
}
if (!token) {
// 生成新token
token = this.setToken();
}
return token;
}
// 验证CSRF Token
validateToken(token) {
const storedToken = this.getToken();
return token && storedToken && token === storedToken;
}
// 为表单添加CSRF Token
addTokenToForm(form) {
const token = this.getToken();
let tokenInput = form.querySelector(`input[name="${this.tokenName}"]`);
if (!tokenInput) {
tokenInput = document.createElement('input');
tokenInput.type = 'hidden';
tokenInput.name = this.tokenName;
form.appendChild(tokenInput);
}
tokenInput.value = token;
}
// 为AJAX请求添加CSRF Token
addTokenToRequest(config = {}) {
const token = this.getToken();
// 添加到headers
config.headers = config.headers || {};
config.headers[this.headerName] = token;
// 如果是POST请求,也添加到body
if (config.method === 'POST' && config.body instanceof FormData) {
config.body.append(this.tokenName, token);
}
return config;
}
// 拦截所有fetch请求
interceptFetch() {
const originalFetch = window.fetch;
const self = this;
window.fetch = function(url, options = {}) {
// 只对同源请求添加CSRF Token
if (self.isSameOrigin(url)) {
options = self.addTokenToRequest(options);
}
return originalFetch.call(this, url, options);
};
}
// 拦截所有XMLHttpRequest
interceptXHR() {
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
const self = this;
XMLHttpRequest.prototype.open = function(method, url, ...args) {
this._method = method;
this._url = url;
return originalOpen.call(this, method, url, ...args);
};
XMLHttpRequest.prototype.send = function(data) {
if (self.isSameOrigin(this._url)) {
const token = self.getToken();
this.setRequestHeader(self.headerName, token);
// 如果是FormData,添加token字段
if (data instanceof FormData) {
data.append(self.tokenName, token);
}
}
return originalSend.call(this, data);
};
}
// 检查是否为同源请求
isSameOrigin(url) {
try {
const urlObj = new URL(url, window.location.origin);
return urlObj.origin === window.location.origin;
} catch {
return true; // 相对URL视为同源
}
}
// 初始化CSRF防护
init() {
this.setToken();
this.interceptFetch();
this.interceptXHR();
this.addFormListeners();
}
// 为所有表单添加监听器
addFormListeners() {
document.addEventListener('submit', (event) => {
const form = event.target;
if (form.tagName === 'FORM') {
this.addTokenToForm(form);
}
});
}
}
// 全局CSRF防护实例
const csrfProtection = new CSRFProtection();
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', () => {
csrfProtection.init();
});
// React Hook for CSRF protection
import { useEffect, useState } from 'react';
function useCSRFToken() {
const [token, setToken] = useState('');
useEffect(() => {
const csrfToken = csrfProtection.getToken();
setToken(csrfToken);
}, []);
const refreshToken = () => {
const newToken = csrfProtection.setToken();
setToken(newToken);
};
return { token, refreshToken };
}
// 使用示例
function SecureForm() {
const { token } = useCSRFToken();
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
formData.append('csrf_token', token);
try {
const response = await fetch('/api/secure-endpoint', {
method: 'POST',
body: formData,
headers: {
'X-CSRF-Token': token
}
});
if (response.ok) {
console.log('请求成功');
}
} catch (error) {
console.error('请求失败:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="hidden" name="csrf_token" value={token} />
<input type="text" name="data" required />
<button type="submit">提交</button>
</form>
);
}
内容安全策略(CSP)
CSP配置与实现
// CSP策略管理器
class CSPManager {
constructor() {
this.policies = {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'"],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'", 'https://fonts.gstatic.com'],
'connect-src': ["'self'"],
'media-src': ["'self'"],
'object-src': ["'none'"],
'child-src': ["'self'"],
'frame-ancestors': ["'none'"],
'form-action': ["'self'"],
'base-uri': ["'self'"],
'manifest-src': ["'self'"]
};
this.nonces = new Map();
this.hashes = new Set();
}
// 生成nonce
generateNonce() {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
return btoa(String.fromCharCode.apply(null, array));
}
// 添加nonce到策略
addNonce(directive, nonce) {
if (!this.policies[directive]) {
this.policies[directive] = [];
}
const nonceValue = `'nonce-${nonce}'`;
if (!this.policies[directive].includes(nonceValue)) {
this.policies[directive].push(nonceValue);
}
this.nonces.set(directive, nonce);
}
// 计算脚本哈希
calculateHash(script, algorithm = 'sha256') {
return crypto.subtle.digest(algorithm.toUpperCase(), new TextEncoder().encode(script))
.then(hashBuffer => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashBase64 = btoa(String.fromCharCode.apply(null, hashArray));
return `'${algorithm}-${hashBase64}'`;
});
}
// 添加脚本哈希
async addScriptHash(script) {
const hash = await this.calculateHash(script);
this.hashes.add(hash);
if (!this.policies['script-src'].includes(hash)) {
this.policies['script-src'].push(hash);
}
return hash;
}
// 添加样式哈希
async addStyleHash(style) {
const hash = await this.calculateHash(style);
this.hashes.add(hash);
if (!this.policies['style-src'].includes(hash)) {
this.policies['style-src'].push(hash);
}
return hash;
}
// 添加域名到策略
addDomain(directive, domain) {
if (!this.policies[directive]) {
this.policies[directive] = [];
}
if (!this.policies[directive].includes(domain)) {
this.policies[directive].push(domain);
}
}
// 生成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头部
setCSPHeader() {
const csp = 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.content = csp;
return csp;
}
// 处理CSP违规报告
handleViolation(event) {
const violation = {
documentURI: event.documentURI,
referrer: event.referrer,
blockedURI: event.blockedURI,
violatedDirective: event.violatedDirective,
originalPolicy: event.originalPolicy,
sourceFile: event.sourceFile,
lineNumber: event.lineNumber,
columnNumber: event.columnNumber,
timestamp: Date.now()
};
// 发送违规报告到服务器
this.reportViolation(violation);
// 本地日志记录
console.warn('CSP Violation:', violation);
}
// 发送违规报告
async reportViolation(violation) {
try {
await fetch('/api/csp-report', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(violation)
});
} catch (error) {
console.error('Failed to report CSP violation:', error);
}
}
// 初始化CSP监听
init() {
// 监听CSP违规事件
document.addEventListener('securitypolicyviolation', (event) => {
this.handleViolation(event);
});
// 设置报告端点
this.policies['report-uri'] = ['/api/csp-report'];
this.policies['report-to'] = ['csp-endpoint'];
// 生成并设置CSP
this.setCSPHeader();
}
// 安全加载外部脚本
async loadExternalScript(src, options = {}) {
const nonce = this.generateNonce();
this.addNonce('script-src', nonce);
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.nonce = nonce;
if (options.integrity) {
script.integrity = options.integrity;
script.crossOrigin = 'anonymous';
}
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
}
// 安全执行内联脚本
async executeInlineScript(scriptContent) {
const hash = await this.addScriptHash(scriptContent);
const script = document.createElement('script');
script.textContent = scriptContent;
document.head.appendChild(script);
return hash;
}
}
// 全局CSP管理器
const cspManager = new CSPManager();
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', () => {
cspManager.init();
});
// 生产环境CSP配置示例
const productionCSP = {
'default-src': ["'self'"],
'script-src': [
"'self'",
'https://cdn.jsdelivr.net',
'https://unpkg.com',
"'sha256-xyz123...'", // 内联脚本哈希
],
'style-src': [
"'self'",
'https://fonts.googleapis.com',
"'unsafe-inline'" // 仅在必要时使用
],
'img-src': [
"'self'",
'data:',
'https:',
'https://images.unsplash.com'
],
'font-src': [
"'self'",
'https://fonts.gstatic.com'
],
'connect-src': [
"'self'",
'https://api.example.com',
'wss://websocket.example.com'
],
'media-src': ["'self'"],
'object-src': ["'none'"],
'child-src': ["'none'"],
'frame-ancestors': ["'none'"],
'form-action': ["'self'"],
'base-uri': ["'self'"],
'upgrade-insecure-requests': [],
'block-all-mixed-content': []
};
安全开发最佳实践
综合安全防护
// 综合安全防护类
class SecurityManager {
constructor() {
this.xssProtection = new XSSProtection();
this.csrfProtection = new CSRFProtection();
this.cspManager = new CSPManager();
this.inputValidator = new InputValidator();
this.securityHeaders = {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()'
};
}
// 初始化所有安全防护
init() {
this.csrfProtection.init();
this.cspManager.init();
this.setupSecureDefaults();
this.monitorSecurity();
}
// 设置安全默认值
setupSecureDefaults() {
// 禁用右键菜单(可选)
if (this.shouldDisableContextMenu()) {
document.addEventListener('contextmenu', (e) => e.preventDefault());
}
// 禁用开发者工具(生产环境)
if (process.env.NODE_ENV === 'production') {
this.disableDevTools();
}
// 设置安全的默认表单属性
this.setupSecureForms();
}
// 禁用开发者工具
disableDevTools() {
// 检测开发者工具
let devtools = { open: false, orientation: null };
setInterval(() => {
if (window.outerHeight - window.innerHeight > 200 ||
window.outerWidth - window.innerWidth > 200) {
if (!devtools.open) {
devtools.open = true;
console.warn('开发者工具已被检测到');
// 可以选择重定向或显示警告
// window.location.href = '/security-warning';
}
} else {
devtools.open = false;
}
}, 500);
}
// 设置安全表单
setupSecureForms() {
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
// 添加CSRF保护
this.csrfProtection.addTokenToForm(form);
// 设置安全属性
if (!form.hasAttribute('novalidate')) {
form.setAttribute('novalidate', 'true'); // 使用自定义验证
}
// 添加输入验证
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
this.addInputValidation(input);
});
});
});
}
// 添加输入验证
addInputValidation(input) {
input.addEventListener('input', (event) => {
const value = event.target.value;
const type = event.target.type || 'text';
// 实时清理输入
const sanitized = this.inputValidator.sanitizeInput(value, type);
if (sanitized !== value) {
event.target.value = sanitized;
}
});
input.addEventListener('blur', (event) => {
const value = event.target.value;
const type = event.target.type || 'text';
// 验证输入
let isValid = true;
let message = '';
switch (type) {
case 'email':
isValid = this.inputValidator.validateEmail(value);
message = '请输入有效的邮箱地址';
break;
case 'password':
const result = this.inputValidator.validatePassword(value);
isValid = result.valid;
message = result.message;
break;
default:
isValid = value.length > 0;
message = '此字段不能为空';
}
// 显示验证结果
this.showValidationResult(input, isValid, message);
});
}
// 显示验证结果
showValidationResult(input, isValid, message) {
// 移除之前的错误信息
const existingError = input.parentNode.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
if (!isValid) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
errorDiv.style.color = 'red';
errorDiv.style.fontSize = '12px';
input.parentNode.appendChild(errorDiv);
input.style.borderColor = 'red';
} else {
input.style.borderColor = '';
}
}
// 安全监控
monitorSecurity() {
// 监控异常活动
this.monitorAbnormalActivity();
// 监控网络请求
this.monitorNetworkRequests();
// 定期安全检查
setInterval(() => {
this.performSecurityCheck();
}, 60000); // 每分钟检查一次
}
// 监控异常活动
monitorAbnormalActivity() {
let clickCount = 0;
let keyCount = 0;
document.addEventListener('click', () => {
clickCount++;
if (clickCount > 100) { // 1分钟内超过100次点击
console.warn('检测到异常点击活动');
clickCount = 0;
}
});
document.addEventListener('keydown', () => {
keyCount++;
if (keyCount > 500) { // 1分钟内超过500次按键
console.warn('检测到异常键盘活动');
keyCount = 0;
}
});
// 重置计数器
setInterval(() => {
clickCount = 0;
keyCount = 0;
}, 60000);
}
// 监控网络请求
monitorNetworkRequests() {
const originalFetch = window.fetch;
window.fetch = async function(url, options = {}) {
// 记录请求
console.log('Network request:', url, options);
// 检查可疑请求
if (typeof url === 'string' && url.includes('eval(')) {
console.error('检测到可疑请求:', url);
throw new Error('Suspicious request blocked');
}
return originalFetch.call(this, url, options);
};
}
// 执行安全检查
performSecurityCheck() {
// 检查DOM是否被篡改
this.checkDOMIntegrity();
// 检查全局变量
this.checkGlobalVariables();
// 检查CSP违规
this.checkCSPViolations();
}
// 检查DOM完整性
checkDOMIntegrity() {
const scripts = document.querySelectorAll('script');
scripts.forEach(script => {
if (script.src && !script.src.startsWith(window.location.origin)) {
console.warn('检测到外部脚本:', script.src);
}
});
}
// 检查全局变量
checkGlobalVariables() {
const dangerousFunctions = ['eval', 'Function', 'setTimeout', 'setInterval'];
dangerousFunctions.forEach(funcName => {
if (window[funcName] && window[funcName].toString().includes('native code')) {
// 原生函数,正常
} else {
console.warn(`全局函数 ${funcName} 可能被篡改`);
}
});
}
// 检查CSP违规
checkCSPViolations() {
// 这里可以检查是否有CSP违规记录
// 实际实现需要与服务端配合
}
// 判断是否应该禁用右键菜单
shouldDisableContextMenu() {
// 根据业务需求决定
return process.env.NODE_ENV === 'production' &&
!window.location.hostname.includes('localhost');
}
}
// 全局安全管理器
const securityManager = new SecurityManager();
// 页面加载时初始化安全防护
document.addEventListener('DOMContentLoaded', () => {
securityManager.init();
});
// 导出安全工具供其他模块使用
window.Security = {
xss: securityManager.xssProtection,
csrf: securityManager.csrfProtection,
csp: securityManager.cspManager,
validator: securityManager.inputValidator
};
总结
前端安全防护的核心要点:
- XSS防护:输入验证、输出编码、内容过滤
- CSRF防护:Token验证、同源检查、安全头设置
- CSP策略:内容安全策略配置、违规监控
- 输入验证:客户端和服务端双重验证
- 安全监控:异常活动检测、安全事件记录
前端安全是一个系统工程,需要从多个维度进行防护,并且要与后端安全措施相配合,形成完整的安全防护体系。