diff --git a/src/api-umbrella/utils/random_token.lua b/src/api-umbrella/utils/random_token.lua index 94622a9d5..4d92ff283 100644 --- a/src/api-umbrella/utils/random_token.lua +++ b/src/api-umbrella/utils/random_token.lua @@ -1,24 +1,42 @@ -local alpha_numeric = { - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" -} +local resty_random = require "resty.random" -local alpha_numeric_size = #alpha_numeric - -if ngx then - math.randomseed(ngx.time()) -else - math.randomseed(os.time()) -end +local encode_base64 = ngx.encode_base64 +local gsub = ngx.re.gsub +local random_bytes = resty_random.bytes return function(length) - local token = {} - for i = 1, length do - token[i] = alpha_numeric[math.random(alpha_numeric_size)] + local token = "" + -- Loop until we've generated a valid token. The basic process: + -- + -- 1. Generate secure random bytes. + -- 2. Convert random bytes to base64. + -- 3. Strip out special characters from base64 result, so we're left with + -- just alphanumerics. + -- + -- It should be extraordinarily rare that this needs to loop, but since we + -- strip out some of the special characters from the resulting base64 string, + -- this loops in case we strip more than expected. + while string.len(token) < length do + -- Attempt to generate cryptographically secure random bytes. We + -- purposefully generate more bytes than we need, since we'll be stripping + -- some of the base64 characters out. + local num_bytes = length + 10 + local strong_random = random_bytes(num_bytes, true) + if not strong_random then + ngx.log(ngx.WARN, "Could not generate cryptographically secure random data. Falling back to non-secure random data.") + strong_random = random_bytes(num_bytes, false) + end + + -- Encode with base64. + token = token .. encode_base64(strong_random) + + -- Strip +, /, and = out of the base64 result, since we just want a-z, A-Z, + -- and 0-9 in our tokens. + token = gsub(token, "[+/=]", "", "jo") + + -- Take just the number of characters requested. + token = string.sub(token, 1, length) end - return table.concat(token) + return token end