我想在我的Go Web应用程序中实现CSRF预防。用户不会登录,但他们会填写表格并付款(通过Stripe Checkout)。
发布一些东西会在会话变量(cookie)中设置一个键,这样他们就可以在以后编辑他们发布的内容,而电子邮件中的URL允许他们在cookie过期时回来,并在需要时再次编辑它。
据我所知,我可以使用 https:/code.google.compxsrftoken。 与 "双提交cookie "方法一起实现CSRF预防,具体方法是
针对一个任意的用户ID生成一个CSRF令牌(uuid.V4()) go-uuid),像这样。
if session.Values["id"] == "" {
session.Values["id"] = uuid.NewV4()
}
csrfToken := xsrftoken.Generate(csrfKey, session.Values["id"], "/listing/new/post")
...并将其存储在session中 和 呈现在模板的一个隐藏字段中。
session.Values["csrfToken"] = csrfToken
...
<input type="hidden" id="_csrf" value={{ .csrfToken }}>
当用户提交表单时,我需要得到我生成的ID,确认已提交的ID csrfToken
表单中的信息是否与会话中的信息相匹配,如果相匹配,请用xsrf包来验证它是否过期。
userID := session.Values["id"]
if session.Values["csrfToken"] != r.PostFormValue("csrfToken") {
http.Redirect(w, r, "/listing/new", 400)
}
if !xsrftoken.Valid(session.Values["csrfToken"], csrfKey, userID, "/listing/new/post") {
http.Redirect(w, r, "/listing/new", 400)
}
我的相关问题是:
我应该生成一个 新的 token?还是可以在一个用户会话中重复使用未过期的令牌? 更新: 根据 本回答 我应该在每个会话中只生成一个新的令牌(即同一个用户在同一个表单中获得同一个令牌,直到令牌过期)。
鉴于更新的问题,我如何处理在用户请求表单和提交表单之间创建的token过期的情况?(也许还有10分钟,他们alt+tabab出去了一会儿)重新将他们引导回表单(当然是重新填写!)并生成一个新的session id+csrf token?
有没有别的方法?编码恐怖 表明SO为每一个发送到客户端的HTML表单生成一个唯一的密钥?鉴于xsrf包在生成新的key时需要一个用户ID,我将如何走这条路?
我还忽略了什么?
我是否应该在每次呈现表单时生成一个新的标记?或者说,是否可以在单个用户会话中重复使用未过期的token? 更新:根据这个答案,我应该只在每个会话中生成一个新的令牌(即同一个用户在同一个表单中获得相同的令牌,直到令牌过期)。
经常重新生成令牌和会话ID是个好主意。如果有一个持续的攻击者和一个可行的入口向量,攻击者获得这两个标识只是时间问题。然而,如果两个标识符中至少有一个在攻击者能够破解当前标识符之前重新生成,那么就没有问题。
鉴于更新的问题,如何处理在用户请求表单到提交表单这段时间内,创建的标识符过期的情况?(可能还有10分钟,他们alt+tab掉了一会儿)把他们重新引导回表单(当然是重新填写!)并生成一个新的session id+csrf token?
如果你想给你的客户大量的时间来填写表格,你可以通过AJAX更新cookies和CSRF令牌。
有没有其他的方法来做这件事?Coding Horror表示,SO为每一个发送给客户端的HTML表单生成一个唯一的密钥?既然xsrf包在生成一个新的key时需要一个userID,那么我将如何走这条路?
一个令牌与某个需要认证的操作绑定得越紧密,你的控制就越精细。如果你能唯一地识别你的应用程序中的每个表单,那么我会说这样做。
我为Go创建了一个CSRF保护包,叫做 鼻冲浪. 下面是它如何处理你提到的区域。
nosurf
它负责取消请求,并返回403或调用你的自定义失败处理程序(如果设置)。你不一定要有 if CsrfCheckOk(r) { ... }
或您的代码中的类似内容。就是这样,尽管我不确定这是处理CSRF的最佳方式。由于紧密的集成,一个特定框架的包可能在某些方面处理得更好。