From 12f8338977a32f53d9979d7831f5c80619e8509a Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 22 Jan 2019 15:20:44 +0100 Subject: [PATCH] stores hashed state code in cookie --- pkg/api/login_oauth.go | 39 ++++++++++++++++++++++++++++----- pkg/services/auth/auth_token.go | 2 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 6013df8ea02..36c9c12905f 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -3,9 +3,11 @@ package api import ( "context" "crypto/rand" + "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/base64" + "encoding/hex" "fmt" "io/ioutil" "net/http" @@ -18,12 +20,14 @@ import ( "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/metrics" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/social" ) -var oauthLogger = log.New("oauth") +var ( + oauthLogger = log.New("oauth") + OauthStateCookieName = "oauth_state" +) func GenStateString() string { rnd := make([]byte, 32) @@ -55,7 +59,9 @@ func (hs *HTTPServer) OAuthLogin(ctx *m.ReqContext) { code := ctx.Query("code") if code == "" { state := GenStateString() - ctx.Session.Set(session.SESS_KEY_OAUTH_STATE, state) + hashedState := hashStatecode(state, setting.OAuthService.OAuthInfos[name].ClientSecret) + hs.writeOauthStateCookie(ctx, hashedState, 60) + if setting.OAuthService.OAuthInfos[name].HostedDomain == "" { ctx.Redirect(connect.AuthCodeURL(state, oauth2.AccessTypeOnline)) } else { @@ -64,13 +70,18 @@ func (hs *HTTPServer) OAuthLogin(ctx *m.ReqContext) { return } - savedState, ok := ctx.Session.Get(session.SESS_KEY_OAUTH_STATE).(string) - if !ok { + savedState := ctx.GetCookie(OauthStateCookieName) + + // delete cookie + ctx.Resp.Header().Del("Set-Cookie") + hs.writeOauthStateCookie(ctx, "", -1) + + if savedState == "" { ctx.Handle(500, "login.OAuthLogin(missing saved state)", nil) return } - queryState := ctx.Query("state") + queryState := hashStatecode(ctx.Query("state"), setting.OAuthService.OAuthInfos[name].ClientSecret) if savedState != queryState { ctx.Handle(500, "login.OAuthLogin(state mismatch)", nil) return @@ -191,6 +202,22 @@ func (hs *HTTPServer) OAuthLogin(ctx *m.ReqContext) { ctx.Redirect(setting.AppSubUrl + "/") } +func (hs *HTTPServer) writeOauthStateCookie(ctx *m.ReqContext, value string, maxAge int) { + http.SetCookie(ctx.Resp, &http.Cookie{ + Name: OauthStateCookieName, + MaxAge: maxAge, + Value: value, + HttpOnly: true, + Path: setting.AppSubUrl + "/", + Secure: hs.Cfg.LoginCookieSecure, + }) +} + +func hashStatecode(code, seed string) string { + hashBytes := sha256.Sum256([]byte(code + setting.SecretKey + seed)) + return hex.EncodeToString(hashBytes[:]) +} + func redirectWithError(ctx *m.ReqContext, err error, v ...interface{}) { ctx.Logger.Error(err.Error(), v...) ctx.Session.Set("loginError", err.Error()) diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index b2d9ab09200..c04389ab557 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -86,7 +86,7 @@ func (s *UserAuthTokenServiceImpl) InitContextWithToken(ctx *models.ReqContext, func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, value string, maxAge int) { if setting.Env == setting.DEV { - ctx.Logger.Info("new token", "unhashed token", value, "cookieName", s.Cfg.LoginCookieName, "secure", s.Cfg.LoginCookieSecure) + ctx.Logger.Info("new token", "unhashed token", value) } ctx.Resp.Header().Del("Set-Cookie")