Brute Force Attack in Phoenix with Hmac Signatures
Brute Force Attack in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A brute force attack against an API using Hmac Signatures in Phoenix typically targets the timestamp and nonce components of the signing process. Hmac Signatures rely on a shared secret to generate a hash that includes parameters such as the HTTP method, request path, query parameters, headers, and a timestamp. If the server does not enforce strict constraints on the timestamp window and nonce uniqueness, an attacker can rapidly iterate through possible timestamps and nonces to find a valid signature.
In Phoenix, this can occur when a request is accepted even if its timestamp is significantly out of sync with the server’s time. For example, an endpoint that only checks that the timestamp is not in the future, but allows a large skew window (e.g., several minutes or hours), enables an attacker to brute force the timestamp within that window. Combined with a predictable or insufficiently random nonce, this can allow an attacker to replay requests or guess valid signatures.
The attack flow often involves intercepting a valid request, then modifying the timestamp and nonce while systematically submitting requests to the Phoenix endpoint. Because the Hmac signature is verified by recomputing the hash using the provided parameters and the shared secret, an attacker does not need to know the secret if they can find a valid timestamp–nonce pair that passes the server’s validation logic. This is especially problematic when rate limiting is absent or weak, allowing many attempts without being blocked.
Such vulnerabilities map to the Authentication and BOLA/IDOR checks performed by middleBrick, which detects weak timestamp handling and missing nonce enforcement during unauthenticated scans. The scanner identifies whether the API accepts requests with timestamps that deviate excessively from server time and whether replay protections are insufficient.
Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes
To remediate brute force risks with Hmac Signatures in Phoenix, enforce a tight timestamp window and require unique, non-repeating nonces. Below are concrete, realistic code examples that demonstrate secure validation in a Phoenix controller and a plug that can be reused across endpoints.
1. Shared Secret and Signature Verification
Ensure the shared secret is stored securely, for example via environment variables, and never logged. Use constant-time comparison when needed.
defmodule MyApp.Hmac do
@moduledoc """
Utilities for Hmac Signature verification with strict timestamp and nonce checks.
"""
@timestamp_tolerance_ms 30_000 # 30 seconds tolerance
@max_nonce_age_s 300 # reject nonces older than 5 minutes
def verify_signature(params, headers, secret) do
with {:ok, timestamp} <- get_timestamp(headers),
{:ok, nonce} <- get_nonce(headers),
true <- within_time_window?(timestamp),
true <- nonce_not_recent?(nonce),
signature = generate_signature(params, timestamp, nonce, secret),
true <- constant_time_compare(signature, get_signature(headers)) do
{:ok, %{timestamp: timestamp, nonce: nonce}}
else
_ -> {:error, :invalid_signature}
end
end
defp get_timestamp(headers) do
case List.keyfind(headers, "x-timestamp", 0) do
{"x-timestamp", ts} -> {value, _} = Integer.parse(ts); {:ok, value}
_ -> {:error, :missing_timestamp}
end
end
defp get_nonce(headers) do
case List.keyfind(headers, "x-nonce", 0) do
{"x-nonce", nonce} -> {:ok, nonce}
_ -> {:error, :missing_nonce}
end
end
defp generate_signature(params, timestamp, nonce, secret) do
string_to_sign = "GET\n/api/resource\n" <> URI.encode_query(params) <> "\n" <> to_string(timestamp) <> "\n" <> nonce
:crypto.mac(:hmac, :sha256, secret, string_to_sign)
|> Base.encode16(case: :lower)
end
defp constant_time_compare(a, b) when is_binary(a) and is_binary(b), do: :crypto.secure_compare(a, b)
defp constant_time_compare(_, _), do: false
end
2. Plug to Reject Invalid Timestamps and Replayed Nonces
Use a plug that validates the timestamp and nonce before the request reaches your router or controller. Store recently seen nonces in a transient in-memory set (for single-node) or a distributed cache like Redis for multi-node setups, with a TTL slightly longer than @max_nonce_age_s.
defmodule MyApp.Plugs.HmacAuth do
use Plug.Builder
plug :fetch_headers
plug :validate_timestamp_and_nonce
defp validate_timestamp_and_nonce(conn, _opts) do
secret = System.get_env("HMAC_SECRET")
with {:ok, auth} <- MyApp.Hmac.verify_signature(conn.params, conn.req_headers, secret) do
# Optionally store nonce with TTL to prevent replays within the tolerance window
MyApp.NonceCache.put(auth.nonce, :timer.now(), :timer.seconds(@max_nonce_age_s))
conn
else
{:error, _} ->
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(401, Jason.encode!(%{error: "unauthorized"}))
|> Plug.Conn.halt()
end
end
end
3. Enforce a Strict Timestamp Window
Reject requests where the timestamp is older or too far in the future. This prevents attackers from brute forcing old or future timestamps.
defmodule MyApp.Hmac do
# ... previous code ...
defp within_time_window?(timestamp) do
now = DateTime.utc_now() |> DateTime.to_unix(:millisecond)
abs(timestamp - now) <= @timestamp_tolerance_ms
end
end
4. Require Server-Side Nonce Tracking
Ensure each nonce is used only once within the acceptable time window. For distributed systems, use a shared store with short TTL to avoid replay across nodes.
defmodule MyApp.NonceCache do
@ttl_seconds 300
def put(nonce, timestamp, ttl \\ @ttl_seconds) do
# Example using Agent or Redis; adapt to your infrastructure
Agent.update(__MODULE__, &Map.put(&1, nonce, timestamp))
# Schedule cleanup after ttl
end
def recent_nonces do
Agent.get(__MODULE__, & &1)
end
end
By combining a narrow timestamp tolerance, strict nonce uniqueness, and server-side validation, you significantly reduce the feasibility of brute forcing Hmac Signatures in Phoenix. These measures align with security checks that middleBrick performs, highlighting weak authentication handling and replay risks.