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

如何防御

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

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

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

使用方式

引入插件包

import "github.com/gogf/csrf"

配置接口中间件

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

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

前端对接

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

代码示例

使用默认配置

package main

import (
	"net/http"
	"time"

	"github.com/gogf/csrf"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

// default cfg
func main() {
	s := g.Server()
	s.Group("/api.v2", func(group *ghttp.RouterGroup) {
		group.Middleware(csrf.New())
		group.ALL("/csrf", func(r *ghttp.Request) {
			r.Response.Writeln(r.Method + ": " + r.RequestURI)
		})
	})
	s.SetPort(8199)
	s.Run()
}

使用自定义配置

package main

import (
	"net/http"
	"time"

	"github.com/gogf/csrf"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

// set cfg
func main() {
	s := g.Server()
	s.Group("/api.v2", func(group *ghttp.RouterGroup) {
		group.Middleware(csrf.NewWithCfg(csrf.Config{
			Cookie: &http.Cookie{
				Name: "_csrf",// token name in cookie
				Secure:   true,
				SameSite: http.SameSiteNoneMode,// 自定义samesite    
			},
			ExpireTime:      time.Hour * 24,
			TokenLength:     32,
			TokenRequestKey: "X-Token",// use this key to read token in request param
		}))
		group.ALL("/csrf", func(r *ghttp.Request) {
			r.Response.Writeln(r.Method + ": " + r.RequestURI)
		})
	})
	s.SetPort(8199)
	s.Run()
}

通过请求体验效果

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






Content Menu

  • No labels

11 Comments

  1. router.go
    s.Group("/", func(group *ghttp.RouterGroup) {
    		group.Middleware(csrf.NewWithCfg(csrf.Config{
    			Cookie: &http.Cookie{
    				Name: "_csrf",
    			},
    			ExpireTime:      time.Hour * 24,
    			TokenLength:     32,
    			TokenRequestKey: "X-Token",
    		}))
    		group.ALL("/chat", api.Chat)
    })
    Chat.go
    func (a *chatApi) Index(r *ghttp.Request) {
    	view := r.GetView()
    
    	if r.Session.Contains("chat_name") {
    		view.Assign("tplMain", "chat/include/chat.html")
    	} else {
    		view.Assign("tplMain", "chat/include/main.html")
    	}
    
    	r.Response.WriteTpl("chat/index.html", g.Map{
    		"token": r.Cookie.Get("_csrf"),
    	})
    }


    搭配CSRF上面的範例,我從URL 輸入位置進入form 頁面,第一次我的Token value 都是空的
    <input type="hidden" name="X-Token" value=""/>

    重新刷新之後 才會有cookie的資料  
    <input type="hidden" name="X-Token" value="1I7f3s6o4GwbFyOzSnVqdMfzQrfNX4hc"/>

    不知是否有人碰到?

    1. 你的用法是错误的,r.Cookie.Get是从请求(request)中读取。而中间件是将csrf信息设置到返回(response)中的。至于为什么你第二次请求可以获取,是因为第二次请求把第一次请求返回的cookies带上了。

      "token": r.Cookie.Get("_csrf"),


      先这样解决,增加一个函数读取中间件设置的cookies,这个函数需要在经过了csrf中间件之后调用。

      // GetResponseCookie description
      //
      // createTime: 2021-05-28 11:16:53
      //
      // author: hailaz
      func GetResponseCookie(r *ghttp.Request, key string) string {
      	rsp := http.Response{Header: r.Response.Header()}
      	for _, v := range rsp.Cookies() {
      		if v.Name == key {
      			return v.Value
      		}
      	}
      	return ""
      }
      
      // 使用
      // "token": GetResponseCookie(r, "_csrf"),
      
      
      1. 感謝,我試試看 

  2. 还不不能明白这个防御的原理

    浏览器又不可以自动 做sign验证
    如果没理解错的话,你这个也就只能判断这个 _csrf 是否过期。
    按照例子 24小时后 就会验证不过,那24小时内 还不是不安全。
    既然如此,为什么不去验证 refer 而达到目的

    1. 查了一下资料,refer也是有办法绕过的,token验证是增加伪造难度。至于安全是相对的。有效期可以根据自身业务需求设置。

      CSRF--花式绕过Referer技巧 - 知乎 (zhihu.com)

  3. 越了解越觉得牛逼... 一个人的知识面能这么广..  

    1. 是吧,我也觉得

      1. 后来我发现你TM也牛叉, 文档, 还有很多代码, 都是你更新的. 

  4. 把token放在cookie里没有意义啊?浏览器发送的请求不是自带cookie吗?

    1. cookie里的是用来校验的,用户要提交一个新的csrf表单值或者头。跨站伪造的请求没有csrf表单值和头