前端安全与防护专题
掌握前端安全防护技术,构建安全可靠的前端应用
📚 专题目标
通过本专题学习,你将掌握:
- XSS 攻击防护与内容安全策略
- CSRF 攻击防护与同源策略
- 点击劫持防护与安全头设置
- 认证授权与JWT安全实践
- 数据加密与敏感信息保护
🛡️ XSS 攻击防护
XSS 攻击类型与防护
// xss-protection.ts
export class XSSProtection {
private static instance: XSSProtection
private allowedTags: Set<string> = new Set(['p', 'br', 'strong', 'em', 'span'])
private allowedAttributes: Set<string> = new Set(['class', 'id'])
static getInstance(): XSSProtection {
if (!XSSProtection.instance) {
XSSProtection.instance = new XSSProtection()
}
return XSSProtection.instance
}
// HTML 转义
escapeHtml(text: string): string {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}
// 属性值转义
escapeAttribute(value: string): string {
return value
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>')
}
// URL 转义
escapeUrl(url: string): string {
try {
const urlObj = new URL(url)
// 只允许 http 和 https 协议
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
throw new Error('Invalid protocol')
}
return urlObj.toString()
} catch {
return '#'
}
}
// 清理 HTML 内容
sanitizeHtml(html: string): string {
const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html')
const walker = document.createTreeWalker(
doc.body,
NodeFilter.SHOW_ELEMENT,
null
)
const elementsToRemove: Element[] = []
let node: Node | null
while (node = walker.nextNode()) {
const element = node as Element
// 检查标签是否允许
if (!this.allowedTags.has(element.tagName.toLowerCase())) {
elementsToRemove.push(element)
continue
}
// 检查属性
const attributes = Array.from(element.attributes)
attributes.forEach(attr => {
if (!this.allowedAttributes.has(attr.name.toLowerCase())) {
element.removeAttribute(attr.name)
} else {
// 转义属性值
element.setAttribute(attr.name, this.escapeAttribute(attr.value))
}
})
// 检查事件处理器
const eventHandlers = ['onclick', 'onload', 'onerror', 'onmouseover']
eventHandlers.forEach(handler => {
if (element.hasAttribute(handler)) {
element.removeAttribute(handler)
}
})
}
// 移除不允许的元素
elementsToRemove.forEach(element => {
element.parentNode?.removeChild(element)
})
return doc.body.innerHTML
}
// 验证用户输入
validateInput(input: string, type: 'text' | 'email' | 'url' | 'number'): boolean {
switch (type) {
case 'email':
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)
case 'url':
try {
new URL(input)
return true
} catch {
return false
}
case 'number':
return !isNaN(Number(input)) && isFinite(Number(input))
case 'text':
return input.length > 0 && input.length < 1000
default:
return false
}
}
// 检测潜在的 XSS 攻击
detectXSS(input: string): boolean {
const xssPatterns = [
/<script[^>]*>.*?<\/script>/gi,
/javascript:/gi,
/on\w+\s*=/gi,
/<iframe[^>]*>.*?<\/iframe>/gi,
/<object[^>]*>.*?<\/object>/gi,
/<embed[^>]*>/gi,
/<link[^>]*>/gi,
/<meta[^>]*>/gi,
/<style[^>]*>.*?<\/style>/gi
]
return xssPatterns.some(pattern => pattern.test(input))
}
}
// 在 Vue 组件中使用
export default defineComponent({
setup() {
const xssProtection = XSSProtection.getInstance()
const userInput = ref('')
const sanitizedContent = ref('')
const isSafe = ref(true)
const handleInput = (input: string) => {
userInput.value = input
// 检测 XSS 攻击
if (xssProtection.detectXSS(input)) {
isSafe.value = false
console.warn('Potential XSS attack detected')
return
}
// 清理 HTML 内容
sanitizedContent.value = xssProtection.sanitizeHtml(input)
isSafe.value = true
}
return {
userInput,
sanitizedContent,
isSafe,
handleInput
}
}
})
Content Security Policy (CSP)
// csp-manager.ts
export class CSPManager {
private static instance: CSPManager
private policies: Map<string, string[]> = new Map()
static getInstance(): CSPManager {
if (!CSPManager.instance) {
CSPManager.instance = new CSPManager()
}
return CSPManager.instance
}
// 设置 CSP 策略
setPolicy(directive: string, sources: string[]) {
this.policies.set(directive, sources)
}
// 生成 CSP 头部
generateCSPHeader(): string {
const directives: string[] = []
this.policies.forEach((sources, directive) => {
if (sources.length > 0) {
directives.push(`${directive} ${sources.join(' ')}`)
}
})
return directives.join('; ')
}
// 设置默认安全策略
setDefaultPolicy() {
this.setPolicy('default-src', ["'self'"])
this.setPolicy('script-src', ["'self'", "'unsafe-inline'"])
this.setPolicy('style-src', ["'self'", "'unsafe-inline'"])
this.setPolicy('img-src', ["'self'", 'data:', 'https:'])
this.setPolicy('font-src', ["'self'", 'https:'])
this.setPolicy('connect-src', ["'self'"])
this.setPolicy('media-src', ["'self'"])
this.setPolicy('object-src', ["'none'"])
this.setPolicy('child-src', ["'self'"])
this.setPolicy('frame-ancestors', ["'none'"])
this.setPolicy('form-action', ["'self'"])
this.setPolicy('base-uri', ["'self'"])
}
// 应用 CSP 到页面
applyCSP() {
const cspHeader = this.generateCSPHeader()
// 设置 meta 标签
let metaCSP = document.querySelector('meta[http-equiv="Content-Security-Policy"]')
if (!metaCSP) {
metaCSP = document.createElement('meta')
metaCSP.setAttribute('http-equiv', 'Content-Security-Policy')
document.head.appendChild(metaCSP)
}
metaCSP.setAttribute('content', cspHeader)
// 监听 CSP 违规事件
document.addEventListener('securitypolicyviolation', (event) => {
console.error('CSP Violation:', {
blockedURI: event.blockedURI,
violatedDirective: event.violatedDirective,
originalPolicy: event.originalPolicy
})
})
}
// 动态添加可信源
addTrustedSource(directive: string, source: string) {
const sources = this.policies.get(directive) || []
if (!sources.includes(source)) {
sources.push(source)
this.policies.set(directive, sources)
}
}
// 移除可信源
removeTrustedSource(directive: string, source: string) {
const sources = this.policies.get(directive) || []
const index = sources.indexOf(source)
if (index > -1) {
sources.splice(index, 1)
this.policies.set(directive, sources)
}
}
}
// 使用示例
const cspManager = CSPManager.getInstance()
// 设置默认策略
cspManager.setDefaultPolicy()
// 添加可信的 CDN
cspManager.addTrustedSource('script-src', 'https://cdn.jsdelivr.net')
cspManager.addTrustedSource('style-src', 'https://fonts.googleapis.com')
// 应用 CSP
cspManager.applyCSP()
🔒 CSRF 攻击防护
CSRF Token 管理
// csrf-protection.ts
export class CSRFProtection {
private static instance: CSRFProtection
private token: string | null = null
private tokenName = 'X-CSRF-Token'
static getInstance(): CSRFProtection {
if (!CSRFProtection.instance) {
CSRFProtection.instance = new CSRFProtection()
}
return CSRFProtection.instance
}
// 生成 CSRF Token
generateToken(): string {
const array = new Uint8Array(32)
crypto.getRandomValues(array)
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('')
}
// 获取 CSRF Token
async getToken(): Promise<string> {
if (!this.token) {
this.token = this.generateToken()
// 将 token 存储到 cookie
document.cookie = `${this.tokenName}=${this.token}; SameSite=Strict; Secure; HttpOnly`
}
return this.token
}
// 验证 CSRF Token
validateToken(token: string): boolean {
return this.token === token
}
// 在请求中添加 CSRF Token
addTokenToRequest(headers: HeadersInit = {}): HeadersInit {
if (this.token) {
return {
...headers,
[this.tokenName]: this.token
}
}
return headers
}
// 刷新 CSRF Token
refreshToken(): void {
this.token = this.generateToken()
document.cookie = `${this.tokenName}=${this.token}; SameSite=Strict; Secure; HttpOnly`
}
// 清除 CSRF Token
clearToken(): void {
this.token = null
document.cookie = `${this.tokenName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
}
}
// HTTP 请求拦截器
export class SecureHTTPClient {
private csrfProtection: CSRFProtection
private baseURL: string
constructor(baseURL: string) {
this.csrfProtection = CSRFProtection.getInstance()
this.baseURL = baseURL
}
// 安全的 GET 请求
async get(url: string, options: RequestInit = {}): Promise<Response> {
return this.request(url, {
...options,
method: 'GET'
})
}
// 安全的 POST 请求
async post(url: string, data: any, options: RequestInit = {}): Promise<Response> {
return this.request(url, {
...options,
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
...options.headers
}
})
}
// 安全的 PUT 请求
async put(url: string, data: any, options: RequestInit = {}): Promise<Response> {
return this.request(url, {
...options,
method: 'PUT',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
...options.headers
}
})
}
// 安全的 DELETE 请求
async delete(url: string, options: RequestInit = {}): Promise<Response> {
return this.request(url, {
...options,
method: 'DELETE'
})
}
// 通用请求方法
private async request(url: string, options: RequestInit): Promise<Response> {
const fullUrl = `${this.baseURL}${url}`
// 为修改操作添加 CSRF Token
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(options.method || 'GET')) {
const token = await this.csrfProtection.getToken()
options.headers = this.csrfProtection.addTokenToRequest(options.headers)
}
// 添加安全头
options.headers = {
...options.headers,
'X-Requested-With': 'XMLHttpRequest',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY'
}
const response = await fetch(fullUrl, options)
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return response
}
}
// 使用示例
const httpClient = new SecureHTTPClient('/api')
// 发送安全请求
const response = await httpClient.post('/users', {
name: 'John Doe',
email: 'john@example.com'
})
SameSite Cookie 配置
// cookie-security.ts
export class SecureCookieManager {
private static instance: SecureCookieManager
static getInstance(): SecureCookieManager {
if (!SecureCookieManager.instance) {
SecureCookieManager.instance = new SecureCookieManager()
}
return SecureCookieManager.instance
}
// 设置安全 Cookie
setSecureCookie(
name: string,
value: string,
options: {
expires?: Date
maxAge?: number
domain?: string
path?: string
secure?: boolean
sameSite?: 'Strict' | 'Lax' | 'None'
httpOnly?: boolean
} = {}
): void {
const {
expires,
maxAge,
domain,
path = '/',
secure = true,
sameSite = 'Strict',
httpOnly = true
} = options
let cookieString = `${name}=${encodeURIComponent(value)}`
if (expires) {
cookieString += `; expires=${expires.toUTCString()}`
}
if (maxAge) {
cookieString += `; max-age=${maxAge}`
}
if (domain) {
cookieString += `; domain=${domain}`
}
cookieString += `; path=${path}`
if (secure) {
cookieString += '; secure'
}
cookieString += `; samesite=${sameSite}`
if (httpOnly) {
cookieString += '; httponly'
}
document.cookie = cookieString
}
// 获取 Cookie
getCookie(name: string): string | null {
const cookies = document.cookie.split(';')
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.trim().split('=')
if (cookieName === name) {
return decodeURIComponent(cookieValue)
}
}
return null
}
// 删除 Cookie
deleteCookie(name: string, domain?: string, path: string = '/'): void {
this.setSecureCookie(name, '', {
expires: new Date(0),
domain,
path
})
}
// 设置会话 Cookie
setSessionCookie(name: string, value: string): void {
this.setSecureCookie(name, value, {
secure: true,
sameSite: 'Strict',
httpOnly: true
})
}
// 设置持久化 Cookie
setPersistentCookie(
name: string,
value: string,
days: number = 30
): void {
const expires = new Date()
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
this.setSecureCookie(name, value, {
expires,
secure: true,
sameSite: 'Strict',
httpOnly: true
})
}
}
// 使用示例
const cookieManager = SecureCookieManager.getInstance()
// 设置安全 Cookie
cookieManager.setSecureCookie('sessionId', 'abc123', {
secure: true,
sameSite: 'Strict',
httpOnly: true
})
// 设置持久化 Cookie
cookieManager.setPersistentCookie('userPreferences', JSON.stringify({
theme: 'dark',
language: 'zh-CN'
}), 365)
🔐 认证授权与JWT安全
JWT 安全管理
// jwt-manager.ts
export class JWTManager {
private static instance: JWTManager
private accessToken: string | null = null
private refreshToken: string | null = null
private tokenExpiry: number | null = null
static getInstance(): JWTManager {
if (!JWTManager.instance) {
JWTManager.instance = new JWTManager()
}
return JWTManager.instance
}
// 解析 JWT Token
parseToken(token: string): any {
try {
const parts = token.split('.')
if (parts.length !== 3) {
throw new Error('Invalid JWT format')
}
const payload = JSON.parse(atob(parts[1]))
return payload
} catch (error) {
console.error('Failed to parse JWT:', error)
return null
}
}
// 验证 Token 是否过期
isTokenExpired(token: string): boolean {
const payload = this.parseToken(token)
if (!payload || !payload.exp) {
return true
}
const currentTime = Math.floor(Date.now() / 1000)
return payload.exp < currentTime
}
// 设置访问 Token
setAccessToken(token: string): void {
this.accessToken = token
const payload = this.parseToken(token)
if (payload && payload.exp) {
this.tokenExpiry = payload.exp * 1000
}
// 存储到安全存储
this.storeTokenSecurely('accessToken', token)
}
// 设置刷新 Token
setRefreshToken(token: string): void {
this.refreshToken = token
this.storeTokenSecurely('refreshToken', token)
}
// 获取访问 Token
getAccessToken(): string | null {
if (!this.accessToken) {
this.accessToken = this.getStoredToken('accessToken')
}
return this.accessToken
}
// 获取刷新 Token
getRefreshToken(): string | null {
if (!this.refreshToken) {
this.refreshToken = this.getStoredToken('refreshToken')
}
return this.refreshToken
}
// 刷新访问 Token
async refreshAccessToken(): Promise<boolean> {
const refreshToken = this.getRefreshToken()
if (!refreshToken) {
return false
}
try {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${refreshToken}`
}
})
if (response.ok) {
const data = await response.json()
this.setAccessToken(data.accessToken)
return true
}
} catch (error) {
console.error('Failed to refresh token:', error)
}
return false
}
// 清除所有 Token
clearTokens(): void {
this.accessToken = null
this.refreshToken = null
this.tokenExpiry = null
this.removeStoredToken('accessToken')
this.removeStoredToken('refreshToken')
}
// 安全存储 Token
private storeTokenSecurely(key: string, token: string): void {
try {
// 使用 sessionStorage 存储,页面关闭后自动清除
sessionStorage.setItem(key, token)
} catch (error) {
console.error('Failed to store token:', error)
}
}
// 获取存储的 Token
private getStoredToken(key: string): string | null {
try {
return sessionStorage.getItem(key)
} catch (error) {
console.error('Failed to get stored token:', error)
return null
}
}
// 移除存储的 Token
private removeStoredToken(key: string): void {
try {
sessionStorage.removeItem(key)
} catch (error) {
console.error('Failed to remove stored token:', error)
}
}
// 检查 Token 是否需要刷新
shouldRefreshToken(): boolean {
if (!this.tokenExpiry) {
return false
}
const currentTime = Date.now()
const refreshThreshold = 5 * 60 * 1000 // 5分钟前开始刷新
return this.tokenExpiry - currentTime < refreshThreshold
}
// 自动刷新 Token
startAutoRefresh(): void {
setInterval(async () => {
if (this.shouldRefreshToken()) {
await this.refreshAccessToken()
}
}, 60000) // 每分钟检查一次
}
}
// 认证守卫
export class AuthGuard {
private static instance: AuthGuard
private jwtManager: JWTManager
constructor() {
this.jwtManager = JWTManager.getInstance()
}
static getInstance(): AuthGuard {
if (!AuthGuard.instance) {
AuthGuard.instance = new AuthGuard()
}
return AuthGuard.instance
}
// 检查用户是否已认证
isAuthenticated(): boolean {
const token = this.jwtManager.getAccessToken()
return token !== null && !this.jwtManager.isTokenExpired(token)
}
// 获取用户信息
getUserInfo(): any {
const token = this.jwtManager.getAccessToken()
if (!token) {
return null
}
return this.jwtManager.parseToken(token)
}
// 检查用户权限
hasPermission(permission: string): boolean {
const userInfo = this.getUserInfo()
if (!userInfo || !userInfo.permissions) {
return false
}
return userInfo.permissions.includes(permission)
}
// 检查用户角色
hasRole(role: string): boolean {
const userInfo = this.getUserInfo()
if (!userInfo || !userInfo.roles) {
return false
}
return userInfo.roles.includes(role)
}
// 路由守卫
canActivate(route: any): boolean {
// 检查是否需要认证
if (route.meta?.requiresAuth && !this.isAuthenticated()) {
return false
}
// 检查权限
if (route.meta?.permission && !this.hasPermission(route.meta.permission)) {
return false
}
// 检查角色
if (route.meta?.role && !this.hasRole(route.meta.role)) {
return false
}
return true
}
}
// 在 Vue Router 中使用
const authGuard = AuthGuard.getInstance()
router.beforeEach((to, from, next) => {
if (authGuard.canActivate(to)) {
next()
} else {
next('/login')
}
})
🔐 数据加密与敏感信息保护
客户端数据加密
// data-encryption.ts
export class DataEncryption {
private static instance: DataEncryption
private key: CryptoKey | null = null
static getInstance(): DataEncryption {
if (!DataEncryption.instance) {
DataEncryption.instance = new DataEncryption()
}
return DataEncryption.instance
}
// 生成加密密钥
async generateKey(): Promise<CryptoKey> {
this.key = await crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256
},
true,
['encrypt', 'decrypt']
)
return this.key
}
// 从密码派生密钥
async deriveKeyFromPassword(password: string, salt: Uint8Array): Promise<CryptoKey> {
const keyMaterial = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(password),
'PBKDF2',
false,
['deriveKey']
)
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{
name: 'AES-GCM',
length: 256
},
false,
['encrypt', 'decrypt']
)
}
// 加密数据
async encrypt(data: string, key?: CryptoKey): Promise<{
encrypted: ArrayBuffer
iv: Uint8Array
}> {
const encryptionKey = key || this.key
if (!encryptionKey) {
throw new Error('No encryption key available')
}
const iv = crypto.getRandomValues(new Uint8Array(12))
const encodedData = new TextEncoder().encode(data)
const encrypted = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv
},
encryptionKey,
encodedData
)
return {
encrypted,
iv
}
}
// 解密数据
async decrypt(
encrypted: ArrayBuffer,
iv: Uint8Array,
key?: CryptoKey
): Promise<string> {
const decryptionKey = key || this.key
if (!decryptionKey) {
throw new Error('No decryption key available')
}
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv
},
decryptionKey,
encrypted
)
return new TextDecoder().decode(decrypted)
}
// 加密并编码为字符串
async encryptToString(data: string, key?: CryptoKey): Promise<string> {
const { encrypted, iv } = await this.encrypt(data, key)
const encryptedArray = new Uint8Array(encrypted)
const combined = new Uint8Array(iv.length + encryptedArray.length)
combined.set(iv)
combined.set(encryptedArray, iv.length)
return btoa(String.fromCharCode(...combined))
}
// 从字符串解密
async decryptFromString(encryptedString: string, key?: CryptoKey): Promise<string> {
const combined = new Uint8Array(
atob(encryptedString).split('').map(char => char.charCodeAt(0))
)
const iv = combined.slice(0, 12)
const encrypted = combined.slice(12)
return this.decrypt(encrypted, iv, key)
}
// 哈希数据
async hash(data: string, algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512' = 'SHA-256'): Promise<string> {
const encodedData = new TextEncoder().encode(data)
const hashBuffer = await crypto.subtle.digest(algorithm, encodedData)
const hashArray = new Uint8Array(hashBuffer)
return Array.from(hashArray)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
}
// 敏感数据管理器
export class SensitiveDataManager {
private static instance: SensitiveDataManager
private encryption: DataEncryption
private storageKey = 'encrypted_data'
static getInstance(): SensitiveDataManager {
if (!SensitiveDataManager.instance) {
SensitiveDataManager.instance = new SensitiveDataManager()
}
return SensitiveDataManager.instance
}
constructor() {
this.encryption = DataEncryption.getInstance()
}
// 存储敏感数据
async storeSensitiveData(key: string, data: any, password: string): Promise<void> {
try {
// 生成盐值
const salt = crypto.getRandomValues(new Uint8Array(16))
// 派生密钥
const key = await this.encryption.deriveKeyFromPassword(password, salt)
// 加密数据
const encryptedData = await this.encryption.encryptToString(JSON.stringify(data), key)
// 存储加密数据和盐值
const storageData = {
encrypted: encryptedData,
salt: Array.from(salt)
}
localStorage.setItem(`${this.storageKey}_${key}`, JSON.stringify(storageData))
} catch (error) {
console.error('Failed to store sensitive data:', error)
throw error
}
}
// 获取敏感数据
async getSensitiveData(key: string, password: string): Promise<any> {
try {
const storedData = localStorage.getItem(`${this.storageKey}_${key}`)
if (!storedData) {
return null
}
const { encrypted, salt } = JSON.parse(storedData)
// 派生密钥
const key = await this.encryption.deriveKeyFromPassword(password, new Uint8Array(salt))
// 解密数据
const decryptedData = await this.encryption.decryptFromString(encrypted, key)
return JSON.parse(decryptedData)
} catch (error) {
console.error('Failed to get sensitive data:', error)
return null
}
}
// 删除敏感数据
removeSensitiveData(key: string): void {
localStorage.removeItem(`${this.storageKey}_${key}`)
}
// 清除所有敏感数据
clearAllSensitiveData(): void {
const keys = Object.keys(localStorage)
keys.forEach(key => {
if (key.startsWith(this.storageKey)) {
localStorage.removeItem(key)
}
})
}
}
// 使用示例
const sensitiveDataManager = SensitiveDataManager.getInstance()
// 存储敏感数据
await sensitiveDataManager.storeSensitiveData('userCredentials', {
username: 'john_doe',
password: 'secret_password'
}, 'user_password')
// 获取敏感数据
const credentials = await sensitiveDataManager.getSensitiveData('userCredentials', 'user_password')
console.log(credentials)
🛡️ 安全头设置
安全响应头管理
// security-headers.ts
export class SecurityHeaders {
private static instance: SecurityHeaders
private headers: Map<string, string> = new Map()
static getInstance(): SecurityHeaders {
if (!SecurityHeaders.instance) {
SecurityHeaders.instance = new SecurityHeaders()
}
return SecurityHeaders.instance
}
// 设置安全头
setHeader(name: string, value: string): void {
this.headers.set(name, value)
}
// 获取安全头
getHeader(name: string): string | undefined {
return this.headers.get(name)
}
// 设置默认安全头
setDefaultHeaders(): void {
// 防止点击劫持
this.setHeader('X-Frame-Options', 'DENY')
// 防止 MIME 类型嗅探
this.setHeader('X-Content-Type-Options', 'nosniff')
// 启用 XSS 过滤器
this.setHeader('X-XSS-Protection', '1; mode=block')
// 强制 HTTPS
this.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload')
// 引用者策略
this.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin')
// 权限策略
this.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
// 跨域嵌入器策略
this.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
this.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
this.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
}
// 应用安全头到响应
applyHeaders(response: Response): Response {
const newHeaders = new Headers(response.headers)
this.headers.forEach((value, name) => {
newHeaders.set(name, value)
})
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
}
// 生成安全头字符串
generateHeaderString(): string {
const headerStrings: string[] = []
this.headers.forEach((value, name) => {
headerStrings.push(`${name}: ${value}`)
})
return headerStrings.join('\n')
}
}
// 安全中间件
export class SecurityMiddleware {
private static instance: SecurityMiddleware
private securityHeaders: SecurityHeaders
static getInstance(): SecurityMiddleware {
if (!SecurityMiddleware.instance) {
SecurityMiddleware.instance = new SecurityMiddleware()
}
return SecurityMiddleware.instance
}
constructor() {
this.securityHeaders = SecurityHeaders.getInstance()
this.securityHeaders.setDefaultHeaders()
}
// 处理请求
async handleRequest(request: Request): Promise<Response> {
// 检查请求方法
if (!this.isAllowedMethod(request.method)) {
return new Response('Method Not Allowed', { status: 405 })
}
// 检查请求头
if (!this.isValidRequest(request)) {
return new Response('Bad Request', { status: 400 })
}
// 处理请求
const response = await this.processRequest(request)
// 应用安全头
return this.securityHeaders.applyHeaders(response)
}
// 检查允许的请求方法
private isAllowedMethod(method: string): boolean {
const allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']
return allowedMethods.includes(method)
}
// 验证请求
private isValidRequest(request: Request): boolean {
// 检查 Content-Type
const contentType = request.headers.get('Content-Type')
if (request.method !== 'GET' && request.method !== 'HEAD') {
if (!contentType || !contentType.includes('application/json')) {
return false
}
}
// 检查 User-Agent
const userAgent = request.headers.get('User-Agent')
if (!userAgent || userAgent.length > 500) {
return false
}
return true
}
// 处理请求
private async processRequest(request: Request): Promise<Response> {
// 这里可以添加具体的请求处理逻辑
return new Response('OK', { status: 200 })
}
}
// 使用示例
const securityMiddleware = SecurityMiddleware.getInstance()
// 处理请求
const response = await securityMiddleware.handleRequest(request)
🎯 专题总结
通过本专题学习,你掌握了:
- XSS 攻击防护:HTML转义、内容清理、CSP策略
- CSRF 攻击防护:Token验证、SameSite Cookie、安全请求
- 认证授权安全:JWT管理、权限控制、路由守卫
- 数据加密保护:客户端加密、敏感数据管理、哈希算法
- 安全头设置:响应头配置、安全中间件、防护策略
📝 练习题
- 实现一个完整的XSS防护系统
- 开发一个CSRF防护中间件
- 构建一个安全的认证授权系统
- 创建一个数据加密存储方案
- 实现安全头配置管理