Api Rate Abuse in Phoenix with Hmac Signatures
Api Rate Abuse in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Rate abuse in Phoenix applications that rely on HMAC signatures for request authentication can occur when rate limiting is evaluated before or independently of signature validation. HMAC signatures bind a request to a shared secret and often include a timestamp or nonce to prevent replay. If a server first checks request rate limits based on an identifier such as an API key or IP address, and then later validates the HMAC, an attacker can flood the endpoint with valid-looking signed requests. Each request consumes quota and processing time, leading to denial of service or resource exhaustion for legitimate users.
The vulnerability is not in HMAC itself, which remains cryptographically sound, but in the ordering and scope of checks. For example, an endpoint that uses HMAC-SHA256 to sign requests but applies rate limits only per API key may be abused by a single compromised key or by rotating keys within a time window if the system does not track a per-client sliding window with sufficient granularity. A common anti-pattern is to verify the HMAC after counting requests, which can allow an attacker to generate many valid signatures (e.g., by exploiting weak nonce management or predictable timestamps) to consume rate allowance without triggering defenses early enough.
middleBrick detects this class of risk as part of its Rate Limiting and Authentication checks, examining how rate controls interact with signed requests. The scanner evaluates whether rate limiting occurs before cryptographic verification, whether nonces and timestamps are enforced with sufficient strictness, and whether per-client tracking is robust across multiple identifiers. Findings include prioritization guidance to align rate control with signature validation so that abusive requests are rejected before they consume significant backend capacity.
Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes
To mitigate rate abuse while preserving the security of HMAC signatures in Phoenix, enforce rate limits before performing expensive cryptographic operations and ensure that per-client tracking accounts for replay protections such as timestamps and nonces. Below is a minimal, production-style example that demonstrates a safe pattern using Plug and Guardian (or similar HMAC handling) in a Phoenix controller.
defmodule MyAppWeb.ApiRateLimitPlug do
@moduledoc """
A plug that enforces rate limits based on client ID extracted from the HMAC headers,
before signature verification proceeds.
"""
import Plug.Conn
alias MyApp.RateLimiter
def init(opts), do: opts
def call(conn, _opts) do
client_id = get_client_id_from_headers(conn)
# Reject early if over limit, avoiding HMAC work for abusive clients
if RateLimiter.exceeded?(client_id) do
conn
|> put_resp_content_type("application/json")
|> send_resp(429, Jason.encode!(%{error: "rate limit exceeded"}))
|> halt()
else
conn
end
end
defp get_client_id_from_headers(conn) do
# Expects "X-Client-Id" and HMAC headers; client_id is not secret
conn.req_headers
|> Enum.find_value(&match_client_id/1)
end
defp match_client_id({"x-client-id", value}), do: value
defp match_client_id(_), do: nil
end
In your HMAC verification logic, validate the timestamp window and nonce after the rate check to prevent replay without amplifying resource usage for abusive clients. Here is an example HMAC verification step that integrates with the rate-limited pipeline:
defmodule MyAppWeb.HmacAuthPlug do
@moduledoc """
Verifies HMAC signature and replay protections only after rate limiting.
"""
import Plug.Conn
alias MyApp.Crypto
def init(opts), do: opts
def call(conn, _opts) do
with { :ok, client_id } <- extract_client_id(conn),
{ :ok, signature } <- extract_signature(conn),
true <- Crypto.verify_hmac(client_id, conn_req_body(conn), signature),
{ :ok, _ts, _nonce } <- Crypto.validate_timestamp_and_nonce(client_id, conn) do
# Attach verified identity for downstream use
assign(conn, :client_id, client_id)
else
_ -> handle_unauthorized(conn)
end
end
defp extract_client_id(conn) do
case List.keyfind(conn.req_headers, "x-client-id", 0) do
{"x-client-id", value} -> { :ok, value }
nil -> { :error, :missing_client_id }
end
end
defp extract_signature(conn) do
case List.keyfind(conn.req_headers, "x-api-signature", 0) do
{"x-api-signature", value} -> { :ok, value }
nil -> { :error, :missing_signature }
end
end
defp conn_req_body(conn), do:
case conn.body_params do
%{} = body -> Jason.encode!(body)
_ -> ""
end
defp handle_unauthorized(conn) do
conn
|> put_resp_content_type("application/json")
|> send_resp(401, Jason.encode!(%{error: "invalid signature"}))
|> halt()
end
end
These examples illustrate how to structure plugs so that rate limiting is applied early using a lightweight identifier, while HMAC verification (including timestamp and nonce checks) follows. For broader protection, combine this with system-level rate limiting at the edge or API gateway, and monitor for bursts of valid signatures that could indicate credential compromise. middleBrick’s scan can surface whether your Phoenix endpoints apply rate controls before cryptographic checks and whether nonces/timestamps are enforced consistently.