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
}
贡献指南
- 找到一个困扰你的问题/开一个新问题
- 讨论
- 分支、提交、测试
- 提出拉取请求/将提交附加到问题上
更多关于golang实现CSRF保护的安全中间件插件nosurf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于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")
}
最佳实践
- HTTPS:始终在生产环境中使用 HTTPS,并设置
Secure: true
的 cookie 选项 - SameSite Cookie:设置 SameSite 属性为 Strict 或 Lax
- 敏感操作:对敏感操作(如密码更改、支付等)使用 POST 请求
- 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 攻击。