Make authentication and captcha more secure

This commit is contained in:
Jan-Lukas Else 2021-02-06 22:53:56 +01:00
parent 79a9ce6126
commit 0c52f90268
2 changed files with 39 additions and 10 deletions

View File

@ -16,22 +16,32 @@ func checkCredentials(username, password string) bool {
return username == appConfig.User.Nick && password == appConfig.User.Password return username == appConfig.User.Nick && password == appConfig.User.Password
} }
func checkUsername(username string) bool {
return username == appConfig.User.Nick
}
func jwtKey() []byte { func jwtKey() []byte {
return []byte(appConfig.Server.JWTSecret) return []byte(appConfig.Server.JWTSecret)
} }
func authMiddleware(next http.Handler) http.Handler { func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. Check JWT // 1. Check BasicAuth
if username, password, ok := r.BasicAuth(); ok && checkCredentials(username, password) {
next.ServeHTTP(w, r)
return
}
// 2. Check JWT
if tokenCookie, err := r.Cookie("token"); err == nil { if tokenCookie, err := r.Cookie("token"); err == nil {
if tkn, err := jwt.Parse(tokenCookie.Value, func(t *jwt.Token) (interface{}, error) { claims := &authClaims{}
if tkn, err := jwt.ParseWithClaims(tokenCookie.Value, claims, func(t *jwt.Token) (interface{}, error) {
return jwtKey(), nil return jwtKey(), nil
}); err == nil && tkn.Valid { }); err == nil && tkn.Valid && claims.TokenType == "login" && checkUsername(claims.Username) {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
} }
// 2. Show login form // 3. Show login form
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
h, _ := json.Marshal(r.Header.Clone()) h, _ := json.Marshal(r.Header.Clone())
b, _ := ioutil.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte b, _ := ioutil.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte
@ -75,7 +85,7 @@ func checkLogin(w http.ResponseWriter, r *http.Request) bool {
} }
// Check credential // Check credential
if checkCredentials(r.FormValue("username"), r.FormValue("password")) { if checkCredentials(r.FormValue("username"), r.FormValue("password")) {
tokenCookie, err := createTokenCookie() tokenCookie, err := createTokenCookie(r.FormValue("username"))
if err != nil { if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return true return true
@ -92,9 +102,19 @@ func checkLogin(w http.ResponseWriter, r *http.Request) bool {
return false return false
} }
func createTokenCookie() (*http.Cookie, error) { type authClaims struct {
*jwt.StandardClaims
TokenType string
Username string
}
func createTokenCookie(username string) (*http.Cookie, error) {
expiration := time.Now().Add(7 * 24 * time.Hour) expiration := time.Now().Add(7 * 24 * time.Hour)
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ExpiresAt: expiration.Unix()}).SignedString(jwtKey()) tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &authClaims{
&jwt.StandardClaims{ExpiresAt: expiration.Unix()},
"login",
username,
}).SignedString(jwtKey())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,10 +19,11 @@ func initCaptcha() {
func captchaMiddleware(next http.Handler) http.Handler { func captchaMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. Check JWT // 1. Check JWT
claims := &captchaClaims{}
if captchaCookie, err := r.Cookie("captcha"); err == nil { if captchaCookie, err := r.Cookie("captcha"); err == nil {
if tkn, err := jwt.Parse(captchaCookie.Value, func(t *jwt.Token) (interface{}, error) { if tkn, err := jwt.ParseWithClaims(captchaCookie.Value, claims, func(t *jwt.Token) (interface{}, error) {
return jwtKey(), nil return jwtKey(), nil
}); err == nil && tkn.Valid { }); err == nil && tkn.Valid && claims.TokenType == "captcha" {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
@ -90,9 +91,17 @@ func checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
return false return false
} }
type captchaClaims struct {
*jwt.StandardClaims
TokenType string
}
func createCaptchaCookie() (*http.Cookie, error) { func createCaptchaCookie() (*http.Cookie, error) {
expiration := time.Now().Add(24 * time.Hour) expiration := time.Now().Add(24 * time.Hour)
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ExpiresAt: expiration.Unix()}).SignedString(jwtKey()) tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &captchaClaims{
&jwt.StandardClaims{ExpiresAt: expiration.Unix()},
"captcha",
}).SignedString(jwtKey())
if err != nil { if err != nil {
return nil, err return nil, err
} }