openresty使用lua修改HTTP-Header
背景
内网部署,只通过IP访问相应服务,而渗透测试反馈说如果请求头中的Host被修改成恶意地址,302重定向就会跳转到Host字段对应的地址。
本来是想用proxy_set_header + $server_addr解决这个安全风险
nginx
server {
...
proxy_set_header Host $server_addr;
...
}
但是考虑到如下两点,还是决定用lua解决。
- 使用$server_addr时,每次请求都会触发一次系统调用,有性能隐患;
- 因为$server_addr取的是接收请求或连接的那块网卡的地址,所以如果nginx部署在docker网络中,或nginx前还有其他前置机,会导致Host的值是客户端到达不了的地址。
脚本编写
对外开放的IP写在了一个配置文件中,所以可以读取这个文件中的IP,保存到全局变量中,修改Header时直接用全局变量中的值。
假设lua脚本保存路径为/etc/demo.lua,配置文件位置为/etc/demo.conf,IP字段名为sys.en.ip,举个例子
properties
...
foo.bar1=34g
foo.bar2=3444
sys.en.ip=10.10.10.10
...
读取IP的lua脚本如下
lua
function readEntranceAddr()
local file = io.open('/etc/demo.conf','r')
local sysEntranceAddr = nil;
for line in file:lines() do
sysEntranceAddr = line:match('^sys%.en%.ip%s?=(.*)$')
if (sysEntranceAddr ~= nil) then
break
end
end
file:close()
return sysEntranceAddr
end
entraceAddr = readEntranceAddr()
OpenResty加载lua脚本
使用init_by_lua_file指令,加载读IP的脚本。
按照openresty的文档,init_by_lua_file应该在http的context中,在实际测试时,放到http或server中都可以。
为了在所有服务中都能使用这个脚本,init_by_lua_file放在了nginx.conf的http中。
nginx
http {
...
init_by_lua_file '/etc/demo.lua';
...
}
修改Request中的Header
如果接收到请求之后,还要通过proxy_pass等指令将请求代理走,可能会使用proxy_set_header修改请求头。
当nginx的变量不能满足要求时,就得通过lua脚本修改请求头字段了。
以背景描述的需求为例,需要把请求头中的Host强制改为配置文件中指定的IP。
对应的生命周期是access阶段,所以配置文件如下
nginx
http {
...
init_by_lua_file '/etc/demo.lua';
access_by_lua_block {
ngx.req.set_header("Host", entraceAddr .. ":" .. ngx.var.server_port)
}
...
}
修改Response中的Header
主要是用来给浏览器发送安全策略,假设Response中也需要带回Host字段,且值为配置文件中指定的IP。
可以使用header_filter_by_lua指令完成,配置文件如下
nginx
http {
...
init_by_lua_file '/etc/demo.lua';
header_filter_by_lua '
ngx.header["Host"] = entraceAddr .. ":" .. ngx.var.server_port
';
...
}