You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenResty中ssl_certificate_by_lua_block执行前如何获取请求User-Agent

核心结论

不存在能同时满足「早于ssl_certificate_by_lua_block执行」和「可读取User-Agent」的OpenResty指令,这是网络协议分层的物理限制,和API权限设计无关。

原因说明

  • SSL握手流程发生在HTTP请求传输之前:TCP三次握手完成后,客户端首先发送SSL Client Hello报文启动TLS握手,ssl_client_hello_by_lua_block、ssl_certificate_by_lua_block都运行在这个阶段。此时加密通道尚未建立,客户端根本不会发送HTTP请求报文,自然不存在User-Agent这类HTTP头字段。你调用ngx.req.get_headers()抛出API禁用的错误,本质是当前上下文根本没有可读取的HTTP请求数据,不是权限配置问题。
  • SSL Client Hello报文本身仅携带TLS版本、支持的加密套件、SNI(访问域名)等TLS层字段,不包含任何HTTP协议相关内容,不可能从中解析出User-Agent。
  • rewrite_by_lua_block属于HTTP请求处理阶段的指令,必须等SSL握手完成、加密通道建立、客户端发送完HTTP请求头之后才会执行,执行时机必然晚于ssl_certificate_by_lua_block,无法满足证书生成前判断UA的需求。

可行替代方案

协议层面不可能在第一次SSL握手时拿到UA,只能通过分层逻辑实现接近需求的效果:

  1. 拆分拦截逻辑为两层:
    • SSL握手阶段:对所有首次发起请求的客户端正常走动态证书签发流程
    • HTTP请求处理最前置(用rewrite_by_lua_block即可):第一时间读取User-Agent做判断,如果是需要拦截的请求,直接返回444状态码断开连接,同时将客户端IP写入lua共享内存的临时黑名单,设置合理的过期时间(比如10分钟)
    • SSL Client Hello阶段增加校验:每次握手时先检查客户端IP是否在共享内存黑名单里,如果在直接拒绝SSL握手,不再走证书签发流程
  2. 这种方案的效果是:恶意UA的第一次请求会走完证书流程,但不会得到任何业务响应,后续同IP的恶意请求会在SSL握手阶段被直接拦截,不会消耗证书签发资源,是当前协议限制下的最优解。

参考配置逻辑示例

lua_shared_dict ssl_blacklist 10m;

server {
    listen 443 ssl;

    # SSL握手最先执行的阶段
    ssl_client_hello_by_lua_block {
        local blacklist = ngx.shared.ssl_blacklist
        local client_ip = ngx.var.remote_addr
        -- 如果IP在黑名单直接拒绝握手
        if blacklist:get(client_ip) == 1 then
            return ngx.exit(444)
        end
        local ssl_clt = require "ngx.ssl.clienthello"
        local host, err = ssl_clt.get_client_hello_server_name()
        ngx.log(ngx.ERR, "handshake host -- > ", host)
    }

    ssl_certificate_by_lua_block {
        -- 首次请求正常走证书签发逻辑
        auto_ssl:ssl_certificate()
    }
    ssl_certificate /etc/ssl/resty-auto-ssl-fallback.crt;
    ssl_certificate_key /etc/ssl/resty-auto-ssl-fallback.key;

    # HTTP阶段最先执行,拿到UA做判断
    rewrite_by_lua_block {
        local blacklist = ngx.shared.ssl_blacklist
        local user_agent = ngx.req.get_headers()["user-agent"] or ""
        ngx.log(ngx.ERR, "request user_agent -- > ", user_agent)
        -- 这里替换成你自己的UA判断规则,比如拦截特定扫描器
        if string.find(string.lower(user_agent), "bad_scanner", 1, true) then
            local client_ip = ngx.var.remote_addr
            -- 把IP加入黑名单10分钟
            blacklist:set(client_ip, 1, 600)
            return ngx.exit(444)
        end
    }

    location / {
        proxy_pass http://backend_proxy$request_uri;
    }
}

注意:不存在任何方式能在首次SSL握手时获取HTTP层的User-Agent,所有声称能实现该能力的方案都违反TCP/SSL/HTTP协议栈的分层设计逻辑。

内容的提问来源于stack exchange,提问作者Nagri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 22:57:22