golang实现Go Web应用CSRF保护插件库gorilla/csrf的使用

Golang实现Go Web应用CSRF保护插件库gorilla/csrf的使用

gorilla/csrf是一个HTTP中间件库,提供跨站请求伪造(CSRF)保护功能。它包含以下特性:

  • csrf.Protect中间件/处理器,为路由或子路由提供CSRF保护
  • csrf.Token函数,提供令牌传递给响应(HTML表单或JSON响应体)
  • csrf.TemplateField辅助函数,可以传递给html/template模板,用隐藏输入字段替换{{ .csrfField }}模板标签

安装

使用配置好的Go工具链:

go get github.com/gorilla/csrf

示例

HTML表单

这是常见的使用场景:为HTML表单提供CSRF保护,防止恶意POST请求:

package main

import (
    "net/http"

    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/signup", ShowSignupForm)
    // 所有没有有效令牌的POST请求将返回HTTP 403 Forbidden
    r.HandleFunc("/signup/post", SubmitSignupForm).Methods("POST")

    // 通过包装路由添加中间件
    http.ListenAndServe(":8000",
        csrf.Protect([]byte("32-byte-long-auth-key"))(r))
    // 注意:如果在本地通过普通HTTP开发,请传递csrf.Secure(false)
    // (但不要在生产环境中保留此设置)
}

func ShowSignupForm(w http.ResponseWriter, r *http.Request) {
    // signup_form.tmpl只需要一个{{ .csrfField }}模板标签
    // csrf.TemplateField会注入CSRF令牌
    t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{}{
        csrf.TemplateTag: csrf.TemplateField(r),
    })
    // 也可以直接从csrf.Token(r)获取令牌
    // 并在请求头中设置 - w.Header.Set("X-CSRF-Token", token)
    // 这在向客户端或前端JavaScript框架发送JSON时很有用
}

func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
    // 我们可以信任到达这里的请求已经满足CSRF保护要求
}

JavaScript应用

如果你使用React、Ember或Angular等前端JavaScript框架,并提供JSON API,可以使用这种方法:

package main

import (
    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    csrfMiddleware := csrf.Protect([]byte("32-byte-long-auth-key"))

    api := r.PathPrefix("/api").Subrouter()
    api.Use(csrfMiddleware)
    api.HandleFunc("/user/{id}", GetUser).Methods("GET")

    http.ListenAndServe(":8000", r)
}

func GetUser(w http.ResponseWriter, r *http.Request) {
    // 认证请求,从路由参数获取id,从数据库获取用户等
    
    // 获取令牌并在CSRF头中传递
    w.Header().Set("X-CSRF-Token", csrf.Token(r))
    b, err := json.Marshal(user)
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    w.Write(b)
}

设置SameSite

Go 1.11引入了在cookie中设置SameSite属性的选项:

func main() {
    CSRF := csrf.Protect(
      []byte("a-32-byte-long-key-goes-here"),
      // 指示浏览器在跨站请求期间从不发送cookie
      csrf.SameSite(csrf.SameSiteStrictMode),
    )

    r := mux.NewRouter()
    r.HandleFunc("/signup", GetSignupForm)
    r.HandleFunc("/signup/post", PostSignupForm)

    http.ListenAndServe(":8000", CSRF(r))
}

设置选项

如果需要自定义错误处理程序和更改包检查的HTTP头:

func main() {
    CSRF := csrf.Protect(
            []byte("a-32-byte-long-key-goes-here"),
            csrf.RequestHeader("Authenticity-Token"),
            csrf.FieldName("authenticity_token"),
            csrf.ErrorHandler(http.HandlerFunc(serverError(403))),
    )

    r := mux.NewRouter()
    r.HandleFunc("/signup", GetSignupForm)
    r.HandleFunc("/signup/post", PostSignupForm)

    http.ListenAndServe(":8000", CSRF(r))
}

设计说明

  • 生成唯一请求(掩码)令牌作为BREACH攻击的缓解措施
  • “base”(未掩码)令牌存储在会话中
  • 采用"仅白名单"方法,安全的(非变异的)HTTP方法(GET, HEAD, OPTIONS, TRACE)是唯一不强制执行令牌验证的方法
  • 基于Django和Ruby on Rails的成熟方法
  • Cookie基于securecookie库认证,默认也是Secure(仅通过HTTPS发出)和HttpOnly

许可证

BSD许可。详见LICENSE文件。


更多关于golang实现Go Web应用CSRF保护插件库gorilla/csrf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Go Web应用CSRF保护插件库gorilla/csrf的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang Web应用CSRF保护:gorilla/csrf使用指南

CSRF(跨站请求伪造)是Web应用中常见的安全威胁之一,gorilla/csrf是一个流行的Go语言CSRF保护中间件,本文将详细介绍如何使用它来保护你的Web应用。

1. 安装gorilla/csrf

首先安装gorilla/csrf包:

go get github.com/gorilla/csrf

2. 基本使用示例

package main

import (
	"fmt"
	"net/http"
	
	"github.com/gorilla/csrf"
	"github.com/gorilla/mux"
)

func main() {
	r := mux.NewRouter()
	
	// 设置CSRF中间件
	// 32字节长度的认证密钥(实际应用中应从安全配置中获取)
	csrfKey := []byte("32-byte-long-auth-key-123456789012")
	csrfMiddleware := csrf.Protect(
		csrfKey,
		csrf.Secure(false), // 开发环境设为false,生产环境应为true(HTTPS)
	)
	
	// 应用CSRF中间件
	handler := csrfMiddleware(r)
	
	// 注册路由
	r.HandleFunc("/", HomeHandler).Methods("GET")
	r.HandleFunc("/process", ProcessHandler).Methods("POST")
	
	fmt.Println("Server started on :8000")
	http.ListenAndServe(":8000", handler)
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	// 获取CSRF令牌并传递给模板
	token := csrf.Token(r)
	
	// 简单示例,实际应用中应使用模板引擎
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintf(w, `
		<html>
		<body>
			<form action="/process" method="POST">
				<input type="hidden" name="%s" value="%s">
				<input type="text" name="data">
				<input type="submit" value="Submit">
			</form>
		</body>
		</html>
	`, csrf.TemplateField(r), token)
}

func ProcessHandler(w http.ResponseWriter, r *http.Request) {
	// CSRF中间件会自动验证令牌
	// 如果验证失败,会返回403 Forbidden
	
	// 处理表单数据
	data := r.FormValue("data")
	fmt.Fprintf(w, "Received data: %s", data)
}

3. 高级配置选项

gorilla/csrf提供了多种配置选项:

csrfMiddleware := csrf.Protect(
	csrfKey,
	csrf.FieldName("custom_csrf_token"), // 自定义表单字段名
	csrf.CookieName("csrf_cookie"),     // 自定义cookie名
	csrf.SameSite(http.SameSiteStrictMode), // 设置SameSite属性
	csrf.Path("/"),                     // 设置cookie路径
	csrf.MaxAge(3600),                  // 设置cookie有效期(秒)
	csrf.HttpOnly(true),                // 设置HttpOnly
	csrf.ErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 自定义错误处理
		http.Error(w, "Custom CSRF error", http.StatusForbidden)
	})),
)

4. 与模板引擎集成

实际项目中通常会使用模板引擎,以下是使用html/template的示例:

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.New("form").Parse(`
		<html>
		<body>
			<form action="/process" method="POST">
				<input type="hidden" name="{{ .csrfField }}" value="{{ .csrfToken }}">
				<input type="text" name="data">
				<input type="submit" value="Submit">
			</form>
		</body>
		</html>
	`))
	
	data := map[string]interface{}{
		"csrfField": csrf.TemplateField(r),
		"csrfToken": csrf.Token(r),
	}
	
	tmpl.Execute(w, data)
}

5. AJAX请求处理

对于AJAX请求,可以从cookie中获取CSRF令牌:

// 使用JavaScript获取CSRF令牌
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

const csrfToken = getCookie('csrf_cookie'); // 与配置的CookieName一致

然后在AJAX请求中添加CSRF令牌头:

fetch('/api/endpoint', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify({data: 'example'})
})

服务端需要配置接受自定义头:

csrfMiddleware := csrf.Protect(
	csrfKey,
	csrf.RequestHeader("X-CSRF-Token"), // 允许从自定义头获取令牌
)

6. 最佳实践

  1. 生产环境:确保设置csrf.Secure(true)以强制HTTPS
  2. 密钥管理:CSRF密钥应足够随机且保密,不要硬编码在代码中
  3. 令牌有效期:根据应用需求设置合理的MaxAge
  4. SameSite属性:现代浏览器支持SameSite属性,可提供额外保护
  5. 敏感操作:对重要操作(如密码更改、支付等)应强制使用POST请求

gorilla/csrf提供了简单而强大的CSRF保护机制,正确使用可以有效地防止跨站请求伪造攻击,同时保持开发体验的简洁性。

回到顶部