vlambda博客
学习文章列表

基于 nginx/openresty 通用参数加密、时间戳验证

openresty_request_encrypt

作用

  • 时间戳验证(请求时间相距大于x秒,拒绝请求)

  • 参数加密(RSA加密报文)

在openresty/nginx对请求上来的加密字符解密。这种在网关前置验证请求带来的好处是,无需下游服务考虑接口校验问题,统一在网关处理。

使用

前提:

  • 安装openresty(一个基于NGINX的可伸缩的Web平台,强大的web应用服务器)

依赖

  • lua-resty-rsa 提供rsa加密算法

clone lua-resty-rsa项目,把/lib/resty/rsa.lua复制到openresty/lualib/resty目录下

本脚本使用RSA加密算法对请求参数加密/解密,所以在使用前先生成RSA秘钥。

  • 使用本项目中的generate_rsa_keys.lua生成,方法如下:

resty generate_rsa_keys.lua
  • 自行生成(脚本默认使用1024位秘钥、 PKCS#8格式)

配置/例子

新建一个nginx配置文件

-- test.conf

location /proxy/ {
    # 对body解密必须有下面这行
    lua_need_request_body on;
    # 配置脚本位置
    rewrite_by_lua_file decrypt.lua;
    # 代理转发到 http://127.0.0.1:8080/
    proxy_pass http://127.0.0.1:8080/;
}

打开decrypt.lua,替换RSA_PRIV_KEY为你生成的RSA私钥

更多配置项

-- 请求参数不合法返回内容
local REQUEST_ERROR = "{\"code\": \"500\", \"msg\": \"request error\"}"
-- 网关出错返回内容
local GATEWAY_ERROR = "{\"code\": \"500\", \"msg\": \"gateway error\"}"
-- 加密数据的参数名
local REQUEST_PARAM_NAME = "_"
-- 时间戳的参数名
local TIMESTAMP_PARAM_NAME = "t";
-- 限制请求
local LIMIT_SECEND = 60
-- 秘钥的长度大小
local KEY_BITS = 1024
-- 数据加密分段大小
local BLOCK_SIZE = KEY_BITS / 8
-- 加密body请求头前缀 后面加真实的Content-Type
local HEADER_PREFIX = "encrypt/"

请求

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIXoCgIan4PtIvoYXgE99cZ2lz
wdptOcC/J1n/waDhzGvsP1hEKfUKIp0KeXuLBuH9WOWJ/NiUI4eJGVfpWQ+cUbXo
CmQYJg3ynzX4uQom/QpKi9MztzmpG1EpO1w7nGizGeAwmTdtaaBSNXu/DYhXDGS4
geLztVdK7efB+kskUQIDAQAB
-----END PUBLIC KEY-----

-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMhegKAhqfg+0i+h
heAT31xnaXPB2m05wL8nWf/BoOHMa+w/WEQp9QoinQp5e4sG4f1Y5Yn82JQjh4kZ
V+lZD5xRtegKZBgmDfKfNfi5Cib9CkqL0zO3OakbUSk7XDucaLMZ4DCZN21poFI1
e78NiFcMZLiB4vO1V0rt58H6SyRRAgMBAAECgYEAkQmPE9qqXS6kGKRT8uqPoSSd
+ZPWF4BZnETQ6cfwO+IsMNt9egHhBRAfGujq260Ews2pgePLphe90SjOMPQtzlCM
pfHW56hd10+65TrERwPLQnTrg9EUMX7+GH7IYvR1La6LUfCtMFHAxk2fu/vBHTx3
426NibBWt4K8J0xHYJ0CQQD9P/EgoitqMn6O6h251mLWEIGecj3TWN6Cp37UE7j/
MTrS1kG0lX9WemqxMXCmMxMpxipxmIuM37y6lFhoKLofAkEAyouMJeVWsjYPjBKP
4tuJRCyXNY+q9Td4gVd5zHdpaexS/ASatC1y2SWiXXaCpMKCkRQ6vaO17SMCxWtJ
MKkzjwJAShPEEomdLWkrv94XZ96f9oHJiHFeSE38eDdKT/qc6Hib/kQR4CLCpqcU
QlR14QebmWKP076NQ13GtMTjv0P6fQJACNX7oC+YD6AyH28z3basD1BOrGR/FcF8
vU++nX/cFmXb3Oiqgw+0geqVYbRo0J03qvKR+XHp3tV3KnuarsfC2wJBAObcNe7c
QMWC4bBTzTBv0cTM00KEaCfFmjmKtcT5HwTLU/ThKHOYk2fIB84NRfZAVKzNMgzr
KTPzCgYSOghVmbc=
-----END PRIVATE KEY-----

请求参数中必须包含当前的时间戳用于验证,默认的参数key是t,参考上文可以修改为其他

query参数加密

原始参数

arg0=123&arg1=456

需要拼接当前时间戳(秒),java(System.currentTimeMillis() / 1000

arg0=123&arg1=456&t=1587898570

原始请求:http://127.0.0.1/proxy/hello?arg0=123&arg1=456&t=1587898570'

使用公钥加密

61fe759c74de978ee666c81aed0dc1ddad6496a4722e9da739f27807e14a566bf0ac89526ec6f3c4692ba30454d69933d69479a57ac7610d894115b0f83f6da3275eb0de11998ef7096c72d6286a35fadbe566d812460eb366cca7c54ae63ceb85576c7f986d231c40901258ca0d02a797127c274a2c25d6cb02aa74a08a9808

加密后的请求:http://127.0.0.1/proxy/hello?_=61fe759c74de978ee666c81aed0dc1ddad6496a4722e9da739f27807e14a566bf0ac89526ec6f3c4692ba30454d69933d69479a57ac7610d894115b0f83f6da3275eb0de11998ef7096c72d6286a35fadbe566d812460eb366cca7c54ae63ceb85576c7f986d231c40901258ca0d02a797127c274a2c25d6cb02aa74a08a9808

body加密

原body

{
    "username""xxx",
     "pwd""xx"
}

加密后body

c2e322c381ba8feacda7e911dfd19a15f8d28924f28d2a93c50301c50c0f91516bddcdc468f2ccc668d12132a3a6234d8f20146b2f0d3f6607a59fc6f101425e703315d44f95a62622f79057b789a55096d747b4475dff167426d939aa57a404087428823ee5dd0a030ddfc6aa40b5d8399fd0da3feefe5b8e5d929e024ec8f0

直接替换即可,需要在原来的Content-Type前加encrypt/,如application/json改成encrypt/application/json

解密body,前提需要配置[ lua_need_request_body on ],在OpenResty里请求体数据总是先
被读入内存,但是为了减小内存占用,设定了一个限制:8KB(32位系统)16KB(64位系统),
超过这个值会存放到硬盘上,这个值可以通过 [ client_body_buffer_size ]改变。

通常来说,内存的速度比硬盘快得多,典型的空间换时间的场景,可以在节约前提下,尽量让数据在内存中。

解密body的逻辑只会读取内存中的body,如果读取不到,不会在尝试读取文件来获取body,尽可能的配置
[ client_body_buffer_size ]的值可以容纳请求体