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
更多关于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. 最佳实践
- 生产环境:确保设置
csrf.Secure(true)
以强制HTTPS - 密钥管理:CSRF密钥应足够随机且保密,不要硬编码在代码中
- 令牌有效期:根据应用需求设置合理的MaxAge
- SameSite属性:现代浏览器支持SameSite属性,可提供额外保护
- 敏感操作:对重要操作(如密码更改、支付等)应强制使用POST请求
gorilla/csrf提供了简单而强大的CSRF保护机制,正确使用可以有效地防止跨站请求伪造攻击,同时保持开发体验的简洁性。