Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | /** * 安全中间件 * 包含:XSS 防护、SQL 注入防护、敏感数据脱敏 */ // XSS 防护 export function xssProtection() { return async (ctx: any, next: any) => { // 过滤请求体中的 XSS if (ctx.request.body && typeof ctx.request.body === 'object') { sanitizeObject(ctx.request.body); } // 过滤查询参数中的 XSS if (ctx.request.query && typeof ctx.request.query === 'object') { sanitizeObject(ctx.request.query); } // 设置安全响应头 ctx.set('X-XSS-Protection', '1; mode=block'); ctx.set('X-Content-Type-Options', 'nosniff'); ctx.set('X-Frame-Options', 'DENY'); ctx.set('Content-Security-Policy', "default-src 'self'"); await next(); }; } /** * 递归清理对象中的 XSS */ function sanitizeObject(obj: any): void { for (const key in obj) { if (typeof obj[key] === 'string') { obj[key] = sanitizeString(obj[key]); } else if (typeof obj[key] === 'object' && obj[key] !== null) { sanitizeObject(obj[key]); } } } /** * 清理字符串中的 XSS 攻击代码 */ function sanitizeString(str: string): string { return str .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/\//g, '/') .replace(/javascript:/gi, '') .replace(/on\w+=/gi, '') .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') .replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi, '') .replace(/<object[^>]*>[\s\S]*?<\/object>/gi, '') .replace(/<embed[^>]*>[\s\S]*?<\/embed>/gi, ''); } // SQL 注入防护 export function sqlInjectionProtection() { return async (ctx: any, next: any) => { // 检查请求参数 const params = { ...ctx.request.query, ...(ctx.request.body || {}), }; for (const key in params) { if (typeof params[key] === 'string') { if (detectSQLInjection(params[key])) { ctx.status = 400; ctx.body = { code: 400, message: '请求包含非法字符', }; return; } } } await next(); }; } /** * 检测 SQL 注入 */ function detectSQLInjection(str: string): boolean { const sqlPatterns = [ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)\b)/i, /(--|;|\/\*|\*\/|@@|@)/, /(\b(OR|AND)\b\s+\d+=\d+)/i, /(\bUNION\b\s+\bSELECT\b)/i, /(\bWAITFOR\b\s+\bDELAY\b)/i, /(\bBENCHMARK\b)/i, /(SLEEP\()/i, /('|\")\s*(OR|AND)\s*('|\")/, ]; return sqlPatterns.some(pattern => pattern.test(str)); } // 敏感数据脱敏 export function sensitiveDataMasking() { return async (ctx: any, next: any) => { await next(); // 脱敏响应中的敏感数据 if (ctx.body && typeof ctx.body === 'object') { maskSensitiveData(ctx.body); } }; } /** * 脱敏敏感数据 */ function maskSensitiveData(obj: any): void { const sensitiveKeys = ['password', 'token', 'secret', 'key', 'cookie', 'authorization']; for (const key in obj) { if (sensitiveKeys.includes(key.toLowerCase())) { if (typeof obj[key] === 'string' && obj[key].length > 8) { obj[key] = obj[key].substring(0, 4) + '****' + obj[key].substring(obj[key].length - 4); } else { obj[key] = '****'; } } else if (typeof obj[key] === 'object' && obj[key] !== null) { maskSensitiveData(obj[key]); } } } // 密码加密工具 export function maskPassword(password: string): string { if (!password || password.length < 8) return '****'; return password.substring(0, 2) + '****' + password.substring(password.length - 2); } // 手机号脱敏 export function maskPhone(phone: string): string { if (!phone || phone.length !== 11) return phone; return phone.substring(0, 3) + '****' + phone.substring(7); } // 邮箱脱敏 export function maskEmail(email: string): string { if (!email || !email.includes('@')) return email; const [name, domain] = email.split('@'); if (name.length <= 2) return `**@${domain}`; return name.substring(0, 2) + '****' + '@' + domain; } |