From 1d519f1b0876f1c48cb9e08390064ea11f795be3 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 4 Sep 2023 13:29:40 +0300 Subject: [PATCH] cluster-bootstrap: make IsValidBootstrapToken() be in constant-time The function uses BootstrapTokenRegexp.MatchString(token) which is not a recommended practice. Instead, break down the token into its components: ID, secret. The ID is public thus we can use Regexp matching for it. The secret needs constant time comparison. Iterate over every character and make sure it fits the 0-9a-z range and that it has a length of 16. --- .../cluster-bootstrap/token/util/helpers.go | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/cluster-bootstrap/token/util/helpers.go b/staging/src/k8s.io/cluster-bootstrap/token/util/helpers.go index 70abe8f1cb3..31379c5d686 100644 --- a/staging/src/k8s.io/cluster-bootstrap/token/util/helpers.go +++ b/staging/src/k8s.io/cluster-bootstrap/token/util/helpers.go @@ -84,10 +84,36 @@ func TokenFromIDAndSecret(id, secret string) string { return fmt.Sprintf("%s.%s", id, secret) } -// IsValidBootstrapToken returns whether the given string is valid as a Bootstrap Token and -// in other words satisfies the BootstrapTokenRegexp +// IsValidBootstrapToken returns whether the given string is valid as a Bootstrap Token. +// Avoid using BootstrapTokenRegexp.MatchString(token) and instead perform constant-time +// comparisons on the secret. func IsValidBootstrapToken(token string) bool { - return BootstrapTokenRegexp.MatchString(token) + // Must be exactly two strings separated by "." + t := strings.Split(token, ".") + if len(t) != 2 { + return false + } + + // Validate the ID: t[0] + // Using a Regexp for it is safe because the ID is public already + if !BootstrapTokenIDRegexp.MatchString(t[0]) { + return false + } + + // Validate the secret with constant-time: t[1] + secret := t[1] + if len(secret) != api.BootstrapTokenSecretBytes { // Must be an exact size + return false + } + for i := range secret { + c := int(secret[i]) + notDigit := (c < 48 || c > 57) // Character is not in the 0-9 range + notLetter := (c < 97 || c > 122) // Character is not in the a-z range + if notDigit && notLetter { + return false + } + } + return true } // IsValidBootstrapTokenID returns whether the given string is valid as a Bootstrap Token ID and