Files
Atay-Makhzan/modules/context/xsrf.go
T

100 lines
3.2 KiB
Go
Raw Normal View History

2016-11-03 23:16:01 +01:00
// Copyright 2012 Google Inc. All Rights Reserved.
// Copyright 2014 The Macaron Authors
2021-01-26 23:36:53 +08:00
// Copyright 2020 The Gitea Authors
2016-11-03 23:16:01 +01:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
2016-11-03 23:16:01 +01:00
2021-01-26 23:36:53 +08:00
package context
2016-11-03 23:16:01 +01:00
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"crypto/subtle"
"encoding/base64"
"fmt"
"strconv"
"strings"
"time"
)
// CsrfTokenTimeout represents the duration that XSRF tokens are valid.
2016-11-03 23:16:01 +01:00
// It is exported so clients may set cookie timeouts that match generated tokens.
const CsrfTokenTimeout = 24 * time.Hour
2016-11-03 23:16:01 +01:00
// CsrfTokenRegenerationInterval is the interval between token generations, old tokens are still valid before CsrfTokenTimeout
var CsrfTokenRegenerationInterval = 10 * time.Minute
2016-11-03 23:16:01 +01:00
var csrfTokenSep = []byte(":")
// GenerateCsrfToken returns a URL-safe secure XSRF token that expires in CsrfTokenTimeout hours.
2016-11-03 23:16:01 +01:00
// key is a secret key for your application.
// userID is a unique identifier for the user.
// actionID is the action the user is taking (e.g. POSTing to a particular path).
func GenerateCsrfToken(key, userID, actionID string, now time.Time) string {
nowUnixNano := now.UnixNano()
nowUnixNanoStr := strconv.FormatInt(nowUnixNano, 10)
2016-11-03 23:16:01 +01:00
h := hmac.New(sha1.New, []byte(key))
h.Write([]byte(strings.ReplaceAll(userID, ":", "_")))
h.Write(csrfTokenSep)
h.Write([]byte(strings.ReplaceAll(actionID, ":", "_")))
h.Write(csrfTokenSep)
h.Write([]byte(nowUnixNanoStr))
tok := fmt.Sprintf("%s:%s", h.Sum(nil), nowUnixNanoStr)
2019-07-12 06:57:31 -07:00
return base64.RawURLEncoding.EncodeToString([]byte(tok))
2016-11-03 23:16:01 +01:00
}
func ParseCsrfToken(token string) (issueTime time.Time, ok bool) {
2019-07-12 06:57:31 -07:00
data, err := base64.RawURLEncoding.DecodeString(token)
2016-11-03 23:16:01 +01:00
if err != nil {
return time.Time{}, false
2016-11-03 23:16:01 +01:00
}
pos := bytes.LastIndex(data, csrfTokenSep)
if pos == -1 {
return time.Time{}, false
2016-11-03 23:16:01 +01:00
}
nanos, err := strconv.ParseInt(string(data[pos+1:]), 10, 64)
2016-11-03 23:16:01 +01:00
if err != nil {
return time.Time{}, false
}
return time.Unix(0, nanos), true
}
// ValidCsrfToken returns true if token is a valid and unexpired token returned by Generate.
func ValidCsrfToken(token, key, userID, actionID string, now time.Time) bool {
issueTime, ok := ParseCsrfToken(token)
if !ok {
2016-11-03 23:16:01 +01:00
return false
}
// Check that the token is not expired.
if now.Sub(issueTime) >= CsrfTokenTimeout {
2016-11-03 23:16:01 +01:00
return false
}
// Check that the token is not from the future.
// Allow 1-minute grace period in case the token is being verified on a
2016-11-03 23:16:01 +01:00
// machine whose clock is behind the machine that issued the token.
if issueTime.After(now.Add(1 * time.Minute)) {
return false
}
expected := GenerateCsrfToken(key, userID, actionID, issueTime)
2016-11-03 23:16:01 +01:00
// Check that the token matches the expected value.
// Use constant time comparison to avoid timing attacks.
return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1
}