CSRF攻击防御原理
字数 1681 2025-08-18 11:39:15
CSRF攻击防御原理详解
0x01 前言
CSRF(Cross-Site Request Forgery,跨站请求伪造)是现代浏览器工作机制导致的一种常见WEB攻击形态。本文将从攻击原理和服务器端防御解决方案两方面进行详细阐述。
CSRF是现代WEB程序面临的共性问题,许多流行WEB框架都在框架层面解决了这一问题。本文将介绍基于时间与签名的防护手段,并提供具体代码实现(使用Lua语言)。
0x02 CSRF攻击原理
CSRF概念
CSRF跨站点请求伪造是一种危害巨大的攻击方式,攻击者盗用用户身份,以用户名义发送恶意请求。对服务器而言这些请求完全合法,但实际完成了攻击者期望的操作,如:
- 以用户名义发送邮件/消息
- 盗取账号
- 添加系统管理员
- 购买商品
- 虚拟货币转账等
攻击场景
- Web A:存在CSRF漏洞的网站
- Web B:攻击者构建的恶意网站
- User C:Web A的合法用户
攻击过程
- 用户C打开浏览器,访问受信任网站A,输入用户名密码登录
- 用户信息验证通过后,网站A产生Cookie信息返回给浏览器,用户登录成功
- 用户未退出网站A前,在同一浏览器中打开新标签页访问网站B
- 网站B返回攻击性代码,并发出访问网站A的请求
- 浏览器接收攻击代码后,在用户不知情下携带Cookie信息向网站A发出请求
- 网站A认为请求来自用户C,以C的权限处理请求,导致恶意代码执行
0x03 CSRF防御原理
防御核心思想
CSRF防护重点是对"用户凭证"进行校验,判断请求是否合法。由于"用户凭证"存储在Cookie中,防护机制主要处理Cookie数据,需要:
- 加入签名校验
- 实现数据生命周期(过期)管理
Token机制设计
Lapis框架(基于Moonscript的WEB框架)采用基于时间戳和签名验证的CSRF防护设计:
- 创建Token处理机制
- Token数据结构与时间、加密签名直接相关
- 目的:为"身份凭证"添加时间生存周期管理和签名校验管理
0x04 签名与时间戳防护处理流程
Token生成
Token构成
Token由三部分组成:
- 消息(msg)
- 随机字符串
- 过期时间戳
- 分隔符(separator):使用"."分隔msg和signature
- 签名(signature):对"msg消息"用特定算法加密后的串
结构表示:
token = base64(msg)..base64(sha256("秘锁", msg))
加密方法
- 使用sha256散列算法
- 进行BASE64格式转换
- 隐含过期时间设定
Token验证
验证步骤
-
Token解包
- 以"."为分隔符,分为msg部分和signature部分
-
比对签名
- 对msg部分base64解码
- 对解码后的msg明文进行相同加密处理
- 比较加密结果与客户端传来的signature部分
- 一致则token有效
-
判断时间过期
- 取出msg中的timestamp字段
- 与当前系统时间比较
- 过期时间小于当前时间则token过期
0x05 流程实现(Lua代码)
Token生成函数
local gen_token = function(key, expires)
-- 设置过期时间戳(默认当前时间+8小时)
if expires == nil then
expires = os.time() + 60 + 60 * 8
end
-- 对msg部分进行base64编码
local msg = encode_base64(
json.encode({
key = key,
expires = expires
})
)
-- 进行sha256哈希
local signature = encode_base64(hmac_sha256('testkey', msg))
-- 拼接成完整token
return msg.."."..signature
end
Token验证函数
local val_token = function(key, token)
-- 输入判空
if not (token) then
return nil, 'missing csrf token'
end
-- 拆分msg和signature
local msg, sig = token:match("^(.*)%.(.*)$")
if not (msg) then
return nil, "malformed csrf token"
end
sig = encoding.decode_base64(sig)
-- 签名验证
if not (sig == hmac_sha256('testkey', msg)) then
return nil, "invalid csrf token(bad sig)"
end
-- key验证
msg = json.decode(decode_base64(msg))
if not (msg.key == key) then
return nil, "invalid csrf token (bad key)"
end
-- 过期时间验证
if not (not msg.expires or msg.expires > os.time()) then
return nil, "csrf token expired"
end
return true
end
0x06 核心安全算法库
实现Token机制需要以下库支持:
1. 加密库
LuaCrypto(推荐)
- 安装:
luarocks install luacrypto - 是OpenSSL库的前端lua调用
- 依赖OpenSSL,支持sha256加密
- 示例代码:
local crypto = require("crypto")
local hmac = require("crypto.hmac")
local ret = hmac.digest("sha256", "abcdefg", "hmackey")
print(ret)
其他选项
- SecureHashAlgorithm/SecureHashAlgorithmBW:纯lua实现,但依赖lua5.2/5.3
- Lcrypt:C语言实现,依赖libTomCrypt和libTomMath
2. Base64库
推荐使用:https://github.com/toastdriven/lua-base64
0x07 总结
- 现代WEB开发必须重视输入数据的有效性验证
- 渗透攻击方式不断演变,防御手段也需要相应更新
- 安全人员需要从攻防双重角度理解问题
- CSRF防御核心:
- Token机制
- 时间戳管理
- 签名验证
- 任何程序都可能存在漏洞,安全是持续的过程
通过这种基于时间和签名的防护机制,可以有效降低CSRF攻击的成功率,为WEB应用提供更安全的防护。