-- Server-side fingerprint generation -- Generates a consistent fingerprint from IP + User-Agent + Accept-Language -- This cannot be spoofed by clients as it's computed server-side local resty_sha256 = require "resty.sha256" local str = require "resty.string" local _M = {} -- Cache fingerprints to avoid recomputation (10 min TTL) local fingerprint_cache = ngx.shared.fingerprints -- Generate a fingerprint from request characteristics -- Uses: Client IP, User-Agent, Accept-Language function _M.generate(custom_ip) -- Get client IP (respects X-Real-IP set by upstream proxy) local client_ip = custom_ip or ngx.var.remote_addr -- Get User-Agent (empty string if not present) local user_agent = ngx.var.http_user_agent or "" -- Get Accept-Language for additional entropy local accept_lang = ngx.var.http_accept_language or "" -- Create a cache key from the raw inputs local cache_key = client_ip .. "|" .. user_agent .. "|" .. accept_lang -- Check cache first if fingerprint_cache then local cached = fingerprint_cache:get(cache_key) if cached then return cached end end -- Generate SHA256 hash local sha256 = resty_sha256:new() if not sha256 then ngx.log(ngx.ERR, "Failed to create SHA256 instance") return nil end -- Hash the combined data sha256:update(client_ip) sha256:update("|") sha256:update(user_agent) sha256:update("|") sha256:update(accept_lang) local digest = sha256:final() local fingerprint = str.to_hex(digest) -- Cache for 10 minutes (600 seconds) if fingerprint_cache then fingerprint_cache:set(cache_key, fingerprint, 600) end return fingerprint end -- Inject fingerprint header for proxied requests -- Call this in access_by_lua_block before proxy_pass function _M.inject_header() local fp = _M.generate() if fp then ngx.req.set_header("X-Server-Fingerprint", fp) end return fp end return _M