fix anwser token and database issus
This commit is contained in:
@@ -12,18 +12,10 @@ import (
|
||||
)
|
||||
|
||||
// TestCSRFTokenBoundToUserSession 是 P1-1 的回归测试:CSRF token 必须绑定到登录用户的
|
||||
// 会话键(CSRFUserSessionKey = "uid"),而非 flamego/csrf 默认的 "userID"。
|
||||
//
|
||||
// 背景:flamego/csrf 的校验是无状态 HMAC 重算(token = HMAC(secret, id:action:ts),不与
|
||||
// session 内存储的 token 比对)。一旦 SessionKey 与登录时写入的会话键(login.go 中的
|
||||
// Session.Set("uid", ...))不一致,token 内绑定的 id 恒为 "0",任意匿名访客从公开 /login
|
||||
// 页面取到的 token 对所有已登录用户均有效 —— CSRF 退化为全站公开常量,跨站请求伪造可自由发起。
|
||||
//
|
||||
// 本测试用内存会话(无需数据库)重放「会话 B 携带会话 A 的 token 提交」这一跨会话复用攻击:
|
||||
// - 会话 A(已登录 uid=42)携带自己的 token 提交 → 必须通过(200);
|
||||
// - 会话 B(匿名)携带会话 A 的 token 跨会话提交 → 必须被拒绝(400)。
|
||||
//
|
||||
// 若 CSRFUserSessionKey 被误改回 "userID",第二项断言会失败,CI 即报警。
|
||||
// 会话键(CSRFUserSessionKey="uid"),否则 flamego/csrf 的无状态 HMAC 重算会让匿名访客
|
||||
// 取到的 token 对所有已登录用户均有效 —— CSRF 退化为全站公开常量,跨站伪造可自由发起。
|
||||
// 测试用内存会话重放跨会话复用攻击:会话 A(uid=42)自带的 token 提交须 200,会话 B 跨会话
|
||||
// 复用会话 A 的 token 须 400。若 CSRFUserSessionKey 被误改回 "userID",第二项断言即失败报警。
|
||||
func TestCSRFTokenBoundToUserSession(t *testing.T) {
|
||||
const secret = "test-secret-for-csrf-regression"
|
||||
|
||||
@@ -49,14 +41,12 @@ func TestCSRFTokenBoundToUserSession(t *testing.T) {
|
||||
SetHeader: true, // 便于测试从响应头取出 token
|
||||
}))
|
||||
|
||||
// 受保护路由:仅当 CSRF 校验通过才返回 200;失败由 csrf.Error 写 400。
|
||||
// 受保护路由:校验通过才 200;失败由 csrf.Error 写 400。
|
||||
f.Post("/protected", func(ctx flamego.Context, x csrf.CSRF) {
|
||||
x.Validate(ctx)
|
||||
// 校验失败时 x.Validate 已写入 400;此处 Write 在已写 400 时仅追加 body,
|
||||
// 状态保持 400;校验通过时此处隐式返回 200。
|
||||
// 校验失败时 x.Validate 已写 400,此处 Write 仅追加 body,状态保持 400。
|
||||
ctx.ResponseWriter().Write([]byte("ok"))
|
||||
})
|
||||
// 生成 token 的 GET 路由(token 在 GET 时生成,与真实应用一致)。
|
||||
f.Get("/token", func(ctx flamego.Context) {
|
||||
ctx.ResponseWriter().Write([]byte("ok"))
|
||||
})
|
||||
@@ -106,13 +96,11 @@ func TestCSRFTokenBoundToUserSession(t *testing.T) {
|
||||
clientA := newClient()
|
||||
tokenA := getToken(clientA, true)
|
||||
|
||||
// 同一会话携带自己的 token 提交 —— 必须通过(200)。
|
||||
if code := postProtected(clientA, tokenA); code != http.StatusOK {
|
||||
t.Fatalf("会话 A 携带自身 token 提交,期望 200,实际 %d(修复后不应被拒绝)", code)
|
||||
}
|
||||
|
||||
// 会话 B:匿名访客,跨会话复用会话 A 的 token 提交 —— 必须被拒绝(400)。
|
||||
// 这是 P1-1 的核心回归点:token 绑定了 uid=42,会话 B 的 id 为 "0",HMAC 重算不匹配。
|
||||
// 核心回归点:token 绑定了 uid=42,会话 B 的 id 为 "0",HMAC 重算不匹配,须被拒绝(400)。
|
||||
clientB := newClient()
|
||||
if code := postProtected(clientB, tokenA); code != http.StatusBadRequest {
|
||||
t.Fatalf("会话 B 跨会话复用会话 A 的 token,期望 400,实际 %d(CSRF 绑定失效!)", code)
|
||||
|
||||
Reference in New Issue
Block a user