前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈 KONG key-auth 插件 token 的生成

浅谈 KONG key-auth 插件 token 的生成

作者头像
poslua
发布2019-08-19 14:27:06
1.3K0
发布2019-08-19 14:27:06
举报
文章被收录于专栏:posluaposlua

最近我在 KONG 的 Blog 上看了一篇文章:That’s So Random: (Pseudo)Random Data Generation in Kong API Gateway,文章中介绍了 KONG 是怎么处理随机数问题的,读后受益良多,在此做一个分享。

seed 的生成

在 OpenResty 中如果使用 ngx.now() 设置种子的话,将会导致各个 worker 的种子相同,也就是说每个 worker 的随机性其实是一样的。一个优化的方案是 ngx.now()*1000 + ngx.worker.pid(), 但是在分布式的环境中,这样依然会有一定的概率产生相同的种子。

KONG 的解决方案是利用 OpenSSL 的 RAND_bytes() 来生成种子。具体方法是:先读取 8 个字节,之后按每个字节做 byte 操作,再用 concat 连接起来。由于 Lua 的 number 其实是 double float,小数有效位是 15-16 位,为了防止其越界,KONG 只取了其前 12 位做为种子。具体实现如下:

代码语言:javascript
复制
local bytes, err = util.get_rand_bytes(8)
if bytes then
  ngx.log(ngx.DEBUG, "seeding PRNG from OpenSSL RAND_bytes()")

  local t = {}
  for i = 1, #bytes do
    local byte = string.byte(bytes, i)
    t[#t+1] = byte
  end
  local str = table.concat(t)
  if #str > 12 then
    -- truncate the final number to prevent integer overflow,
    -- since math.randomseed() could get cast to a platform-specific
    -- integer with a different size and get truncated, hence, lose
    -- randomness.
    -- double-precision floating point should be able to represent numbers
    -- without rounding with up to 15/16 digits but let's use 12 of them.
    str = string.sub(str, 1, 12)
  end
  seed = tonumber(str)
else
  ngx.log(ngx.ERR, "could not seed from OpenSSL RAND_bytes, seeding ",
                   "PRNG with time and worker pid instead (this can ",
                   "result to duplicated seeds): ", err)

  seed = ngx.now()*1000 + ngx.worker.pid()
end

token 的生成

早期的 KONG 生成 token 用的是 UUID,去掉 - 连字符,是一个 32 位长的字符串。但是其 UUID 生成依赖的 LuaJIT 的 PRNG,并不属于 CSPRNG,所以不适合这一类对安全要求比较高的场景。目前 KONG 用的是系统的 urandom,可以认为是一个真随机的实现。相关实现如下:

代码语言:javascript
复制
local function urandom_bytes(buf, size)
  local fd = ffi.C.open("/dev/urandom", O_RDONLY, 0) -- mode is ignored
  if fd < 0 then
    ngx_log(WARN, "Error opening random fd: ",
                  ffi_str(ffi.C.strerror(ffi.errno())))

    return false
  end

  local res = ffi.C.read(fd, buf, size)
  if res <= 0 then
    ngx_log(WARN, "Error reading from urandom: ",
                  ffi_str(ffi.C.strerror(ffi.errno())))

    return false
  end

  if ffi.C.close(fd) ~= 0 then
    ngx_log(WARN, "Error closing urandom: ",
                  ffi_str(ffi.C.strerror(ffi.errno())))
  end

  return true
end

local function random_string()
  return encode_base64(get_rand_bytes(24, true))
         :gsub("/", char(rand(48, 57)))  -- 0 - 10
         :gsub("+", char(rand(65, 90)))  -- A - Z
         :gsub("=", char(rand(97, 122))) -- a - z
end

但是这样有个缺点就是会阻塞 worker,至于为什么不用性能更好的 OpenSSL’s CSPRNG。KONG 也给出了解释:

  • 目前 OpenSSL’s RNG 的被发现有一些缺陷,可能会在未来修复。当下对于 KONG 来说,使用内核的 CSPRNG 无疑是最好的选择。
  • 生成 token 的动作由 KONG admin 发起,并不会很频繁,而且阻塞时间很短是可以接受的。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 poslua 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • seed 的生成
  • token 的生成
相关产品与服务
SSL 证书
腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档