mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-05 16:02:48 +00:00
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
package formserver
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// The render token is the anti-scraping gate: the JSON API and submit
|
|
// endpoints only answer requests carrying a token minted when the page shell
|
|
// was served, so a script cannot harvest form definitions or spray submits
|
|
// without loading the page first. The key derives from INTERNAL_API_TOKEN,
|
|
// so replicas agree without any new configuration.
|
|
|
|
const renderTokenTTL = 12 * time.Hour
|
|
|
|
func renderKey(internalToken string) []byte {
|
|
sum := sha256.Sum256([]byte(internalToken + ":forms-render"))
|
|
return sum[:]
|
|
}
|
|
|
|
func renderMAC(key []byte, publicID string, expiry int64) []byte {
|
|
mac := hmac.New(sha256.New, key)
|
|
mac.Write([]byte(publicID + "." + strconv.FormatInt(expiry, 10)))
|
|
return mac.Sum(nil)
|
|
}
|
|
|
|
// mintRenderToken returns base64url(expiryUnix_be8 || HMAC(publicID.expiry)).
|
|
func mintRenderToken(key []byte, publicID string, now time.Time) string {
|
|
expiry := now.Add(renderTokenTTL).Unix()
|
|
buf := make([]byte, 8, 8+sha256.Size)
|
|
binary.BigEndian.PutUint64(buf, uint64(expiry))
|
|
buf = append(buf, renderMAC(key, publicID, expiry)...)
|
|
return base64.RawURLEncoding.EncodeToString(buf)
|
|
}
|
|
|
|
func verifyRenderToken(key []byte, publicID, token string, now time.Time) bool {
|
|
raw, err := base64.RawURLEncoding.DecodeString(token)
|
|
if err != nil || len(raw) != 8+sha256.Size {
|
|
return false
|
|
}
|
|
expiry := int64(binary.BigEndian.Uint64(raw[:8]))
|
|
if now.Unix() > expiry {
|
|
return false
|
|
}
|
|
return hmac.Equal(raw[8:], renderMAC(key, publicID, expiry))
|
|
}
|