golang实现CSRF保护的安全中间件插件nosurf的使用

golang实现CSRF保护的安全中间件插件nosurf的使用

nosurf 是一个用于Go语言的HTTP包,可以帮助你防止跨站请求伪造(CSRF)攻击。它作为中间件工作,因此基本上与任何Go HTTP应用程序兼容。

为什么需要nosurf?

尽管CSRF是一个突出的漏洞,但Go的web相关包基础设施主要由微框架组成,这些框架既不实现CSRF检查,也不应该实现。

nosurf 通过提供一个CSRFHandler来解决这个问题,该处理器包装你的http.Handler并在每个非安全(非GET/HEAD/OPTIONS/TRACE)方法上检查CSRF攻击。

nosurf 需要Go 1.1或更高版本。

特性

  • 支持任何http.Handler(框架、你自己的处理器等),并且本身就像一个处理器一样工作
  • 允许通过精确URL、glob或正则表达式免除特定端点的CSRF检查
  • 允许指定你自己的失败处理器
  • 使用掩码令牌来缓解BREACH攻击
  • 除了Go标准库外没有其他依赖

示例

下面是一个完整的示例demo,展示如何使用nosurf实现CSRF保护:

package main

import (
	"fmt"
	"github.com/justinas/nosurf"
	"html/template"
	"net/http"
)

var templateString string = `
<!doctype html>
<html>
<body>
{{ if .name }}
<p>Your name: {{ .name }}</p>
{{ end }}
<form action="/" method="POST">
<input type="text" name="name">

<!-- Try removing this or changing its value
     and see what happens -->
<input type="hidden" name="csrf_token" value="{{ .token }}">
<input type="submit" value="Send">
</form>
</body>
</html>
`
var templ = template.Must(template.New("t1").Parse(templateString))

func myFunc(w http.ResponseWriter, r *http.Request) {
	context := make(map[string]string)
	context["token"] = nosurf.Token(r)
	if r.Method == "POST" {
		context["name"] = r.FormValue("name")
	}
	
	templ.Execute(w, context)
}

func main() {
	myHandler := http.HandlerFunc(myFunc)
	fmt.Println("Listening on http://127.0.0.1:8000/")
	http.ListenAndServe(":8000", nosurf.New(myHandler))
}

手动令牌验证

在某些情况下,CSRF令牌可能通过非标准方式发送,例如请求体是一个JSON编码的消息,其中一个字段是令牌。

在这种情况下,应该使用以下豁免方法之一将处理程序(path)从自动验证中排除:

func (h *CSRFHandler) ExemptFunc(fn func(r *http.Request) bool)
func (h *CSRFHandler) ExemptGlob(pattern string)
func (h *CSRFHandler) ExemptGlobs(patterns ...string)
func (h *CSRFHandler) ExemptPath(path string)
func (h *CSRFHandler) ExemptPaths(paths ...string)
func (h *CSRFHandler) ExemptRegexp(re interface{})
func (h *CSRFHandler) ExemptRegexps(res ...interface{})

之后,必须通过从cookie中获取令牌并提供通过主体发送的令牌来手动验证令牌:VerifyToken(tkn, tkn2 string) bool

示例:

func HandleJson(w http.ResponseWriter, r *http.Request) {
	d := struct{
		X,Y int
		Tkn string
	}{}
	json.Unmarshal(ioutil.ReadAll(r.Body), &d)
	if !nosurf.VerifyToken(nosurf.Token(r), d.Tkn) {
		http.Errorf(w, "CSRF token incorrect", http.StatusBadRequest)
		return
	}
	// do smth cool
}

贡献指南

  1. 找到一个困扰你的问题/开一个新问题
  2. 讨论
  3. 分支、提交、测试
  4. 提出拉取请求/将提交附加到问题上

更多关于golang实现CSRF保护的安全中间件插件nosurf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现CSRF保护的安全中间件插件nosurf的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 实现 CSRF 保护的 nosurf 中间件使用指南

CSRF(跨站请求伪造)是一种常见的网络攻击方式,nosurf 是一个轻量级的 Go 语言 CSRF 保护中间件,可以轻松集成到你的 web 应用中。

nosurf 基本用法

首先安装 nosurf 包:

go get github.com/justinas/nosurf

基本示例

package main

import (
	"fmt"
	"html/template"
	"net/http"
	
	"github.com/justinas/nosurf"
)

var templateString = `
<!doctype html>
<html>
<body>
{{ if .name }}
<p>Your name: {{ .name }}</p>
{{ end }}
<form action="/" method="POST">
<input type="text" name="name">
<input type="hidden" name="csrf_token" value="{{ .token }}">
<button type="submit">Send</button>
</form>
</body>
</html>
`

var templ = template.Must(template.New("t1").Parse(templateString))

func main() {
	// 创建基本路由器
	mux := http.NewServeMux()
	
	// 添加路由
	mux.HandleFunc("/", Home)
	
	// 使用 nosurf 中间件包装路由器
	// 注意: nosurf.New 返回一个 http.Handler
	csrfHandler := nosurf.New(mux)
	
	// 启动服务器
	fmt.Println("Listening on http://127.0.0.1:8000/")
	http.ListenAndServe(":8000", csrfHandler)
}

func Home(w http.ResponseWriter, r *http.Request) {
	context := make(map[string]interface{})
	context["token"] = nosurf.Token(r)
	
	if r.Method == "POST" {
		context["name"] = r.FormValue("name")
	}
	
	templ.Execute(w, context)
}

高级配置选项

nosurf 提供了多种配置选项:

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", Home)
	
	csrfHandler := nosurf.New(mux)
	
	// 配置选项
	csrfHandler.SetBaseCookie(http.Cookie{
		Path:     "/",
		Domain:   "yourdomain.com",
		Secure:   true,  // 仅HTTPS
		HttpOnly: true,  // 防止JavaScript访问
		SameSite: http.SameSiteStrictMode,
	})
	
	// 设置自定义失败处理
	csrfHandler.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "CSRF token验证失败", http.StatusBadRequest)
	}))
	
	// 排除特定路径
	csrfHandler.ExemptPath("/api/public")
	
	http.ListenAndServe(":8000", csrfHandler)
}

与框架集成

与 Gorilla Mux 集成

package main

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

func main() {
	r := mux.NewRouter()
	
	// 受保护的路由
	r.HandleFunc("/protected", ProtectedHandler).Methods("GET", "POST")
	
	// 不受保护的路由
	r.HandleFunc("/unprotected", UnprotectedHandler)
	
	// 使用 nosurf 中间件
	csrfHandler := nosurf.New(r)
	
	// 排除特定路由
	csrfHandler.ExemptPath("/unprotected")
	
	http.ListenAndServe(":8000", csrfHandler)
}

func ProtectedHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		w.Write([]byte("POST请求成功处理"))
	} else {
		w.Write([]byte("GET请求成功处理,Token: " + nosurf.Token(r)))
	}
}

func UnprotectedHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("这个路由不受CSRF保护"))
}

与 Gin 框架集成

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/justinas/nosurf"
	"net/http"
)

func main() {
	r := gin.Default()
	
	// 创建 nosurf 中间件
	csrfMiddleware := func() gin.HandlerFunc {
		handler := nosurf.New(nil)
		return func(c *gin.Context) {
			handler.ServeHTTP(c.Writer, c.Request)
		}
	}()
	
	// 应用中间件
	r.Use(csrfMiddleware)
	
	// 路由
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"csrfToken": nosurf.Token(c.Request),
		})
	})
	
	r.POST("/submit", func(c *gin.Context) {
		c.String(http.StatusOK, "表单提交成功")
	})
	
	r.Run(":8000")
}

最佳实践

  1. HTTPS:始终在生产环境中使用 HTTPS,并设置 Secure: true 的 cookie 选项
  2. SameSite Cookie:设置 SameSite 属性为 Strict 或 Lax
  3. 敏感操作:对敏感操作(如密码更改、支付等)使用 POST 请求
  4. AJAX 请求:对于 AJAX 请求,需要在请求头中添加 CSRF 令牌:
// 前端JavaScript示例
fetch('/api/sensitive', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': document.querySelector('input[name="csrf_token"]').value
  },
  body: JSON.stringify({data: 'value'})
});

然后在 Go 中配置 nosurf 检查请求头:

csrfHandler := nosurf.New(mux)
csrfHandler.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    http.Error(w, "CSRF token无效", http.StatusBadRequest)
}))

nosurf 是一个简单而强大的 CSRF 保护解决方案,通过合理配置可以有效地保护你的 web 应用免受 CSRF 攻击。

回到顶部