Lua URL地址混淆解密

Eave 2026.05.08

Nginx配置lua.gramess.com.conf

server
{
    listen       80;
    server_name  lua.gramess.com;

    lua_code_cache on;

    access_log  /var/log/nginx/lua.gramess.com.log access;

    location ~* /gateway
    {
        set $target '';
        access_by_lua_file '/usr/local/nginx/lua/gateway.lua';

        proxy_pass               http://$target;
        proxy_set_header         Host                  $http_host;
        proxy_set_header         X-Real-IP             $remote_addr;
        proxy_set_header         X-Real-Port           $remote_port;
        proxy_set_header         X-Remote-Addr         $remote_addr;
        proxy_set_header         X-Forwarded-For       $proxy_add_x_forwarded_for;

        proxy_http_version       1.1;
        proxy_set_header         Connection            "";
    }
}

Lua脚本文件gateway.lua

local redis = require("resty.redis")

-- 1. 设置响应头
ngx.header["Content-Type"] = "text/html;charset=UTF-8"


-- 2. 路径验证和参数提取
local action = ngx.var.uri
if #action <= 1 then  -- 使用 # 替代 string.len,更高效
    ngx.exit(ngx.HTTP_FORBIDDEN)
end


-- 3. 提取 key(复用一次即可)
local key = ngx.var.uri:gsub("^/[a-z]+/", "")
ngx.header["Key"] = key


-- 4. 配置参数(可以提取到文件头部或配置文件)
local config = {
    redis_host = "127.0.0.1",
    redis_port = 6379,
    redis_timeout = 1000  -- ms
}


-- 5. Redis 操作函数(封装可复用)
local function get_from_redis(key)
    local red = redis:new()
    if not red then
        return nil, "failed to create redis object"
    end

    red:set_timeout(config.redis_timeout)

    local ok, err = red:connect(config.redis_host, config.redis_port)
    if not ok then
        return nil, "connect failed: " .. (err or "unknown")
    end

    -- 执行查询
    local value, err = red:get(key)

    -- 归还连接到池(无论成功与否)
    local keepalive_ok, keepalive_err = red:set_keepalive(10000, 100)
    if not keepalive_ok then
        ngx.log(ngx.WARN, "failed to set keepalive: ", keepalive_err)
    end

    if err then
        return nil, err
    end

    return value, nil
end


-- 6. 执行查询
local server, err = get_from_redis(key)

if err then
    -- 记录错误日志
    ngx.log(ngx.ERR, "Failed to get: ", key, ", error: ", err)

    -- ngx.status = ngx.HTTP_NOT_FOUND
    ngx.header["Content-Type"] = "application/json;charset=UTF-8"
    ngx.say('{"code":500,"message":"Failed to get: ' .. err .. '"}')
    ngx.exit(ngx.HTTP_NOT_FOUND)
end


-- 7. 处理空值和查询参数
if server == ngx.null or server == "" then
    -- 记录错误日志
    ngx.log(ngx.ERR, "Not Found: ", key)

    -- ngx.status = ngx.HTTP_NOT_FOUND
    ngx.header["Content-Type"] = "application/json;charset=UTF-8"
    ngx.say('{"code":500,"message":"Not Found: ' .. key .. '"}')
    ngx.exit(ngx.HTTP_NOT_FOUND)
end

-- 如果 key 包含 IP:Port 格式,提取纯 URI 路径
local function strip_ip_port(key_str)
    if not key_str or key_str == "" then
        return key_str
    end
    
    -- 匹配并移除 IP:Port 或 IP 格式
    -- 支持格式: 10.10.10.110:80/path 或 10.10.10.110/path
    local stripped = key_str:gsub("^%d+%.%d+%.%d+%.%d+:?%d*", "")
    
    -- 如果移除后为空或只有斜杠,返回原值(避免丢失数据)
    if stripped == "" or stripped == "/" then
        return key_str
    end
    
    return stripped
end

ngx.header["Origin-Url"] = strip_ip_port(server)


-- 8. 拼接查询参数
local query_string = ngx.var.QUERY_STRING
if query_string and query_string ~= "" then
    server = server .. "?" .. query_string
end


-- 9. 设置变量供后续代理使用
ngx.var.target = server


-- 10. 可选:记录访问日志
ngx.log(ngx.INFO, string.format("Redirect: key=%s, target=%s", key, server))