CrossOriginProtection 使用指南

Go 1.25 新增 CrossOriginProtection 使用指南

Go 1.25 引入了 CrossOriginProtection,用于保护 Web 应用免受 CSRF 攻击。本文详细介绍其 API 与常见使用方法。

具体的接口定义可以参考CrossOriginProtection


什么是 CrossOriginProtection

CrossOriginProtection 会拒绝不安全的跨源浏览器请求。它的判断依据是:

Sec-Fetch-Site 请求头(2023 年起所有主流浏览器支持)

或 Origin 请求头与 Host 对比

默认允许的安全方法:GET、HEAD、OPTIONS

具体的逻辑可以阅读源码func (*CrossOriginProtection) Check


1. 创建实例

1
cop := http.NewCrossOriginProtection()
  • 零值有效,无需初始化参数
  • 支持并发调用

2. 添加可信来源

1
2
3
4
err := cop.AddTrustedOrigin("https://myapp.com")
if err != nil {
log.Fatal(err)
}
  • 允许来自特定 Origin 的请求
  • Origin 格式:scheme://host[:port]

3. 添加不安全路径绕过

1
cop.AddInsecureBypassPattern("/public/")
  • /public/ 路径将不进行跨源检查
  • 规则与 http.ServeMux 相同
  • 可并发调用

4. 自定义拒绝逻辑

1
2
3
cop.SetDenyHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Forbidden: CSRF protection", http.StatusForbidden)
}))

5. Handler 用法(推荐用于整条路径)

Handler 方法会在调用实际 handler 前自动执行跨源检查,如果不通过则返回 403 或自定义拒绝逻辑。

1
http.Handle("/api/", cop.Handler(http.HandlerFunc(apiHandler)))
  • 优点:简单、自动处理请求拒绝
  • 场景:整条 API 路径都需要统一保护

6. Check 方法用法(手动检查单个请求)

Check 方法用于在业务逻辑中手动判断请求是否被允许。

1
2
3
4
5
6
7
8
9
func apiHandler(w http.ResponseWriter, r *http.Request) {
if err := cop.Check(r); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}

// 请求合法,继续业务处理
w.Write([]byte("API response"))
}
  • 优点:精细控制,可根据条件选择性检查
  • 场景:单个请求、复杂中间件或特殊逻辑处理

7. 直接在 http.ListenAndServe 使用

也可以直接在主 handler 里使用 Check

1
http.ListenAndServe(":8080", cop.Handler(mux))
  • 适合没有复杂路由的简单服务
  • 不需要额外包装 Handler

8. 总结

方法 用途 场景
Handler 自动检查跨源请求,失败返回 403 或自定义 整条 API 路径统一保护
Check 手动检查请求,返回 error 单个请求或复杂业务逻辑控制
AddTrustedOrigin 添加可信 Origin 允许特定来源请求
AddInsecureBypassPattern 添加不安全路径绕过 静态资源或无需检查的路径
SetDenyHandler 自定义拒绝逻辑 替代默认 403 返回

通过 Go 1.25 的 CrossOriginProtection,Web 应用可以更简单地实现 CSRF 防护,同时保留手动检查和自定义拒绝逻辑的灵活性。

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
"net/http"
)

func main() {
cop := http.NewCrossOriginProtection()

// 添加可信任的跨站来源
_ = cop.AddTrustedOrigin("https://trusted.com")

// 添加无需检查的路径
cop.AddInsecureBypassPattern("/public")

mux := http.NewServeMux()

// 1️⃣ 普通业务接口(全局保护会自动检查)
mux.HandleFunc("/update", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Data updated"))
})

// 2️⃣ 演示手动调用 cop.Check()
// 实际是冗余的,因为全局会检查,但可定制失败处理
mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
if err := cop.Check(r); err != nil {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
w.Write([]byte("Login success"))
})

// 3️⃣ 直接在单个路由上使用 cop.Handler 包裹 Handler
// 即使全局没用 cop.Handler,这个路径依然会被保护
mux.Handle("/secure-data", cop.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Secure data accessed"))
})))

// 全局保护(这里会让 /update 和 /login 再次被检查一次)
http.ListenAndServe(":8080", cop.Handler(mux))
}

这样,你可以灵活地选择某些路径使用全局保护,某些路径手动调用 Check 方法。