在 OAuth 2.0 URL 中使用常量 state
值¶
ID: go/constant-oauth2-state
Kind: path-problem
Security severity: 8.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-352
Query suites:
- go-code-scanning.qls
- go-security-extended.qls
- go-security-and-quality.qls
OAuth 2.0 客户端必须对重定向 URI 实现 CSRF 保护,这通常是通过包含将请求绑定到用户的身份验证状态的“状态”值来完成的。Go OAuth 2.0 库允许您指定一个“状态”值,该值随后将包含在授权代码 URL 中。然后,远程身份验证服务器会在重定向回调中返回该状态,必须在该回调中对其进行验证。如果未这样做,则客户端容易受到 CSRF 攻击。
建议¶
始终包含一个唯一的、不可猜测的 state
值(提供给对 AuthCodeURL
函数的调用),该值也绑定到每个身份验证请求中用户的身份验证状态,然后在重定向回调中进行验证。
示例¶
第一个示例展示了如何使用常量状态(错误)。
package main
import (
"golang.org/x/oauth2"
)
func main() {}
var stateStringVar = "state"
func badWithStringLiteralState() {
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"SCOPE1", "SCOPE2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth",
TokenURL: "https://provider.com/o/oauth2/token",
},
}
url := conf.AuthCodeURL(stateStringVar)
// ...
}
第二个示例展示了一种更好的实现方法。
package main
import (
"crypto/rand"
"encoding/base64"
"net/http"
"golang.org/x/oauth2"
)
func betterWithVariableStateReturned(w http.ResponseWriter) {
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"SCOPE1", "SCOPE2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth",
TokenURL: "https://provider.com/o/oauth2/token",
},
}
state := generateStateOauthCookie(w)
url := conf.AuthCodeURL(state)
_ = url
// ...
}
func generateStateOauthCookie(w http.ResponseWriter) string {
b := make([]byte, 128)
rand.Read(b)
// TODO: save the state string to cookies or HTML storage,
// and bind it to the authenticated status of the user.
state := base64.URLEncoding.EncodeToString(b)
return state
}
参考资料¶
IETF:OAuth 2.0 授权框架
IETF:OAuth 2.0 安全最佳实践
常见弱点枚举:CWE-352。