71 lines
2 KiB
Lua
71 lines
2 KiB
Lua
-- 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
|