Skip to content

Commit

Permalink
Fix the possibility of the seed process generating duplicate API keys.
Browse files Browse the repository at this point in the history
Lua's math.randomseed doesn't really work effectively when just using
the current time as the seed value, since duplicate processes starting
up at the same time can lead to duplicate random values. I think this
was primarily affecting the test and CI environment, where the seeding
might happen multiple times in rapid succession.

This fixes it by using cryptographically secure random data to generate
the random tokens, which is better anyway (this was taken from some work
on the postgres branch, where we had already made this switch in
bdaafa5 as part of more widespread
usage of this random_token method, besides just for the seeding
process).
  • Loading branch information
GUI committed May 9, 2018
1 parent f88a2c0 commit a725342
Showing 1 changed file with 36 additions and 18 deletions.
54 changes: 36 additions & 18 deletions src/api-umbrella/utils/random_token.lua
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit a725342

Please sign in to comment.