Use of constant state
value in OAuth 2.0 URL¶
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
Click to see the query in the CodeQL repository
OAuth 2.0 clients must implement CSRF protection for the redirection URI, which is typically accomplished by including a “state” value that binds the request to the user’s authenticated state. The Go OAuth 2.0 library allows you to specify a “state” value which is then included in the auth code URL. That state is then provided back by the remote authentication server in the redirect callback, from where it must be validated. Failure to do so makes the client susceptible to an CSRF attack.
Recommendation¶
Always include a unique, non-guessable state
value (provided to the call to AuthCodeURL
function) that is also bound to the user’s authenticated state with each authentication request, and then validated in the redirect callback.
Example¶
The first example shows you the use of a constant state (bad).
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)
// ...
}
The second example shows a better implementation idea.
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
}
References¶
Common Weakness Enumeration: CWE-352.