introduce
This article describes how to pass rk-boot Implement the CSRF verification logic of the server.
What is CSRF?
Cross Site Request Forgery (English: Cross Site Request Forgery), also known as one click attack or session riding, usually abbreviated as CSRF or XSRF, is an attack method to coerce users to perform unintentional operations on the currently logged in Web application.
Compared with cross site scripting (XSS), XSS uses the user's trust in the specified website, and CSRF uses the website's trust in the user's web browser.
What's the defense?
There are several popular defense methods. We use examples to realize the defense of [add verification Token].
1: Token synchronization mode 2: Check the Referer field 3: Add verification Token
Please visit the following address for a complete tutorial:
install
go get github.com/rookie-ninja/rk-boot/gf
Quick start
1. Create boot yaml
boot.yaml file will tell rk boot how to start gogf/gf Service.
In the following YAML file, we declare one thing:
- Turn on the CSRF interceptor and use the default parameters. The interceptor will check the value of X-CSRF-Token in the request Header to determine whether the Token is correct.
--- gf: - name: greeter # Required port: 8080 # Required enabled: true # Required interceptors: csrf: enabled: true # Optional, default: false
2. Create main go
We are gogf/gf Add two restful APIs to the.
- GET /v1/hello: returns the CSRF Token generated by the server
- POST /v1/hello: verify CSRF Token
// Copyright (c) 2021 rookie-ninja // // Use of this source code is governed by an Apache-style // license that can be found in the LICENSE file. package main import ( "context" "github.com/gogf/gf/v2/net/ghttp" "github.com/rookie-ninja/rk-boot" "github.com/rookie-ninja/rk-boot/gf" "net/http" ) func main() { // Create a new boot instance. boot := rkboot.NewBoot() // Register handler entry := rkbootgf.GetGfEntry("greeter") entry.Server.BindHandler("/v1/hello", hello) // Bootstrap boot.Bootstrap(context.TODO()) boot.WaitForShutdownSig(context.TODO()) } func hello(ctx *ghttp.Request) { ctx.Response.WriteHeader(http.StatusOK) ctx.Response.WriteJson(map[string]string{ "message": "hello!", }) }
3. Folder structure
. ├── boot.yaml ├── go.mod ├── go.sum └── main.go 0 directories, 4 files
4. Start main go
$ go run main.go 2022-02-07T15:02:03.187+0800 INFO boot/gf_entry.go:600 Bootstrap gfEntry {"eventId": "8238e90e-5cd0-4da7-9f9b-7bb9b1946978", "entryName": "greeter", "entryType": "GoFrame"} ------------------------------------------------------------------------ endTime=2022-02-07T15:02:03.188021+08:00 startTime=2022-02-07T15:02:03.187943+08:00 elapsedNano=78089 timezone=CST ids={"eventId":"8238e90e-5cd0-4da7-9f9b-7bb9b1946978"} app={"appName":"rk","appVersion":"","entryName":"greeter","entryType":"GoFrame"} env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"} payloads={"gfPort":8080} counters={} pairs={} timing={} remoteAddr=localhost operation=Bootstrap resCode=OK eventStatus=Ended EOE
5. Verification
- Send a GET request to / v1/hello and we will GET the CSRF Token.
$ curl -X GET -vs localhost:8080/v1/hello ... > Cookie: _csrf=my-test-csrf-token > X-CSRF-Token:my-test-csrf-token > < HTTP/1.1 200 OK < Content-Type: application/json < Server: GoFrame HTTP Server < Set-Cookie: _csrf=my-test-csrf-token; Expires=Tue, 08 Feb 2022 07:02:45 GMT < Trace-Id: 104af099fb6ed11600722376ab2d8a82 < Vary: Cookie < Date: Mon, 07 Feb 2022 07:02:45 GMT < Content-Length: 20 < * Connection #0 to host localhost left intact {"message":"hello!"}
- Send a POST request to / v1/hello and provide a legal CSRF Token.
$ curl -X POST -v --cookie "_csrf=my-test-csrf-token" -H "X-CSRF-Token:my-test-csrf-token" localhost:8080/v1/hello ... > Cookie: _csrf=my-test-csrf-token > X-CSRF-Token:my-test-csrf-token > < HTTP/1.1 200 OK < Content-Type: application/json < Server: GoFrame HTTP Server < Set-Cookie: _csrf=my-test-csrf-token; Expires=Tue, 08 Feb 2022 07:03:31 GMT < Trace-Id: 90ded13e066fd1160172237663ed8fbb < Vary: Cookie < Date: Mon, 07 Feb 2022 07:03:31 GMT < Content-Length: 20 < * Connection #0 to host localhost left intact {"message":"hello!"}
- Send a POST request to / v1/hello and provide an illegal CSRF Token.
$ curl -X POST -v -H "X-CSRF-Token:my-test-csrf-token" localhost:8080/v1/hello ... > X-CSRF-Token:my-test-csrf-token > < HTTP/1.1 403 Forbidden < Server: GoFrame HTTP Server < Trace-Id: c88c53630b6fd116027223761a59ee69 < Date: Mon, 07 Feb 2022 07:03:53 GMT < Content-Length: 91 < Content-Type: text/plain; charset=utf-8 < * Connection #0 to host localhost left intact {"error":{"code":403,"status":"Forbidden","message":"invalid csrf token","details":[null]}}*
CSRF interceptor options
Rk boot provides several CSRF interceptor options. Unless there are special needs, the override option is not recommended.
option | describe | type | Default value |
---|---|---|---|
gf.interceptors.csrf.enabled | Start CSRF interceptor | boolean | false |
gf.interceptors.csrf.tokenLength | Token length | int | 32 |
gf.interceptors.csrf.tokenLookup | Please refer to the following introduction for where to get the Token | string | "header:X-CSRF-Token" |
gf.interceptors.csrf.cookieName | Cookie name | string | _csrf |
gf.interceptors.csrf.cookieDomain | Cookie domain | string | "" |
gf.interceptors.csrf.cookiePath | Cookie path | string | "" |
gf.interceptors.csrf.cookieMaxAge | Cookie MaxAge (seconds) | int | 86400 (24 hours) |
gf.interceptors.csrf.cookieHttpOnly | Cookie HTTP Only option | bool | false |
gf.interceptors.csrf.cookieSameSite | Cookie SameSite option, which supports lax, strict, none and default | string | "lax" |
gf.interceptors.csrf.ignorePrefix | Ignore Restful API Path for CSRF validation | []string | [] |
tokenLookup format
At present, the following three methods are supported. The interceptor will use one of the following methods to find a Token in the request.
- Get from HTTP Header
- Get from HTTP Form
- Get from HTTP Query
// Optional. Default value "header:X-CSRF-Token". // Possible values: // - "header:<name>" // - "form:<name>" // - "query:<name>" // Optional. Default value "header:X-CSRF-Token".