설명
OAuth2 state 값은 인증 요청과 콜백을 연결해 CSRF와 세션 혼동을 막는 값입니다. 고정 문자열이나 재사용 가능한 상수를 사용하면 공격자가 예측 가능한 콜백을 만들 수 있습니다.
잠재적 영향
- 인증 응답 위조 또는 로그인 CSRF가 가능해질 수 있습니다.
- 사용자가 공격자가 의도한 계정이나 세션에 연결될 수 있습니다.
해결 방법
- 요청마다 예측 불가능한
state값을 생성하세요. - 생성한 값을 서버 세션 또는 서명된 저장소에 바인딩하고 콜백에서 검증하세요. 만료 시간을 정하고, 성공한 검증과 사용 처리를 원자적으로 수행해 재사용을 막으세요.
예시
세션 바인딩과 빈 값 거부를 보여 주는 발췌입니다. newRandomState는 암호학적으로 안전한 난수로 비어 있지 않은 값을 생성하거나 오류를 반환하고, Session.Get은 문자열을 반환한다는 전제입니다. 만료 검사와 일회성 사용 처리는 생략되어 있으므로 실제 로그인 수락 전에 구현하세요.
변경 전
go
const oauthState = "fixed-state"
func login(cfg *oauth2.Config) string {
return cfg.AuthCodeURL(oauthState)
}
func callback(r *http.Request) bool {
return r.URL.Query().Get("state") == oauthState
}
변경 후
go
func login(cfg *oauth2.Config, session Session) (string, error) {
state, err := newRandomState()
if err != nil {
return "", err
}
session.Set("oauth_state", state)
return cfg.AuthCodeURL(state), nil
}
func callback(r *http.Request, session Session) bool {
expected := session.Get("oauth_state")
return expected != "" && r.URL.Query().Get("state") == expected
}
설명:
- 변경 전: 고정된
state값을 인증 요청과 콜백 검증에 재사용하므로 공격자가 예측 가능한 콜백을 만들 수 있습니다. - 변경 후: 요청마다 예측 불가능한
state를 생성해 사용자 세션에 저장하고, 콜백에서는 저장된 값이 비어 있지 않은지 확인한 뒤 요청 값과 비교합니다. 이 비교만으로 만료나 일회성 사용이 보장되지는 않습니다.