跨站请求伪造(英语:Cross-Site Request Forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

如何防御

这里我们选择通过token的方式对请求进行校验,通过中间件的方式实现,CSRF跨站点防御插件由社区包提供。

开发者可以通过对接口添加中间件的方式,增加token校验功能。

感兴趣的朋友可以阅读插件源码 https://github.com/gogf/csrf

使用方式

引入插件包

  1. import "github.com/gogf/csrf"

配置接口中间件

csrf插件支持自定义csrf.Config配置,Config中的Cookie.Name是中间件设置到请求返回Cookietoken的名称,ExpireTimetoken超时时间,TokenLengthtoken长度,TokenRequestKey是后续请求需求带上的参数名。

  1. s := g.Server()
  2. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  3. group.Middleware(csrf.NewWithCfg(csrf.Config{
  4. Cookie: &http.Cookie{
  5. Name: "_csrf",// token name in cookie
  6. },
  7. ExpireTime: time.Hour * 24,
  8. TokenLength: 32,
  9. TokenRequestKey: "X-Token",// use this key to read token in request param
  10. }))
  11. group.ALL("/csrf", func(r *ghttp.Request) {
  12. r.Response.Writeln(r.Method + ": " + r.RequestURI)
  13. })
  14. })

前端对接

通过配置后,前端在POST请求前从Cookie中读取_csrf的值(即token),然后请求发出时将tokenX-TokenTokenRequestKey所设置)参数名置入请求中(可以是Header或者Form)即可通过token校验。

代码示例

使用默认配置

  1. package main
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/gogf/csrf"
  6. "github.com/gogf/gf/frame/g"
  7. "github.com/gogf/gf/net/ghttp"
  8. )
  9. // default cfg
  10. func main() {
  11. s := g.Server()
  12. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  13. group.Middleware(csrf.New())
  14. group.ALL("/csrf", func(r *ghttp.Request) {
  15. r.Response.Writeln(r.Method + ": " + r.RequestURI)
  16. })
  17. })
  18. s.SetPort(8199)
  19. s.Run()
  20. }

使用自定义配置

  1. package main
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/gogf/csrf"
  6. "github.com/gogf/gf/frame/g"
  7. "github.com/gogf/gf/net/ghttp"
  8. )
  9. // set cfg
  10. func main() {
  11. s := g.Server()
  12. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  13. group.Middleware(csrf.NewWithCfg(csrf.Config{
  14. Cookie: &http.Cookie{
  15. Name: "_csrf",// token name in cookie
  16. },
  17. ExpireTime: time.Hour * 24,
  18. TokenLength: 32,
  19. TokenRequestKey: "X-Token",// use this key to read token in request param
  20. }))
  21. group.ALL("/csrf", func(r *ghttp.Request) {
  22. r.Response.Writeln(r.Method + ": " + r.RequestURI)
  23. })
  24. })
  25. s.SetPort(8199)
  26. s.Run()
  27. }

通过请求体验效果

http://localhost:8199/api.v2/csrf