基于 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 ]的值可以容纳请求体