Limen.Signal.HttpShape (limen v0.2.1)

Copy Markdown View Source

Checks whether a request looks like what its user agent claims to be.

Browsers send a predictable set of headers. Automation that borrows a browser's user agent string rarely reproduces all of them, and never reproduces them consistently. This signal classifies the user agent (see Limen.Signal.UserAgent) and reports inconsistencies as flags:

FlagMeaning
:no_user_agentno user-agent header
:no_acceptno accept header
:no_accept_languageno accept-language header
:no_accept_encodingno accept-encoding header
:generic_accepta browser navigation with accept: */*
:no_sec_fetcha browser recent enough to send sec-fetch-* headers did not
:no_client_hintsa Chromium browser recent enough to send sec-ch-ua did not
:unexpected_client_hintsa non-Chromium browser sent sec-ch-ua
:client_hint_brand_mismatchsec-ch-ua does not name the claimed browser
:client_hint_platform_mismatchsec-ch-ua-platform disagrees with the user agent
:client_hint_mobile_mismatchsec-ch-ua-mobile disagrees with the user agent
:headlessthe user agent or client hints name a headless browser

Browsers only send sec-fetch-* headers (what a request is for, such as a page or an image) and client hints (sec-ch-ua*, in which Chromium browsers describe themselves) to secure origins, so those checks only apply to HTTPS requests. Behind a TLS terminator, make sure conn.scheme reflects the original scheme (for example with Plug.RewriteOn) before Limen.Plug runs.

This is an independent design, not JA4H (FoxIO's HTTP fingerprint).

Shape

:shape is a stable hash of the request's header names in the order they were received, leaving out headers added by proxies. Clients built on the same HTTP stack share a shape, so it works well in lists and rate keys. Header order is only meaningful when the web server preserves it: Bandit does, Cowboy does not.

Provides :ua_family (see Limen.Signal.UserAgent for the families), :ua_name (the name of a known automated client, such as "googlebot", "gptbot" or "curl", else nil), :ua_version, :shape and :shape_flags, with the parsed user agent as evidence for :ua_family.

Summary

Functions

The inconsistencies between a request's headers and its user agent.

The header-order shape of a request.

Functions

flags(ctx, ua)

@spec flags(Limen.Context.t(), Limen.Signal.UserAgent.t()) :: [atom()]

The inconsistencies between a request's headers and its user agent.

shape(ctx)

@spec shape(Limen.Context.t()) :: String.t()

The header-order shape of a request.