Broken Authentication in Phoenix with Hmac Signatures
Broken Authentication in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Broken Authentication occurs when application functions related to authentication and session management are implemented incorrectly, enabling attackers to compromise passwords, keys, or session tokens. In Phoenix, using Hmac Signatures for request authentication can introduce subtle vulnerabilities when the signature mechanism is misapplied or when related controls are weak.
Hmac Signatures typically involve generating a cryptographic hash-based message authentication code from a request payload and a shared secret. If the signature is computed over only part of the request—such as the body but excluding critical headers like timestamps, nonces, or API keys—an attacker can reuse a valid signature across requests by altering the unprotected elements. This is especially risky when endpoints accept mutable or predictable parameters such as user identifiers or resource IDs without signature coverage, enabling IDOR or BOLA-style attacks.
In a Phoenix API, consider a common pattern where a client sends an HTTP request with an authorization header containing a hex-encoded HMAC (e.g., Hmac-SHA256) of the request body using a shared client secret. If the server does not enforce strict method and path consistency, and if it also relies on unauthenticated route parameters to locate the resource, an attacker can intercept or modify the URL to access another user’s resource while presenting a valid signature for the body. This mismatch between what is signed and what is trusted leads to Broken Authentication and can be discovered by scanners that test unauthenticated attack surfaces.
Additionally, weak handling of replay attacks compounds the issue. Without a nonce or timestamp included in the signed string, an attacker can capture a valid request and replay it within the server’s validity window to perform unauthorized actions. Phoenix applications that use Hmac Signatures should ensure the signature includes a short-lived timestamp and, where applicable, a nonce to prevent reuse. Without these, the authentication boundary is effectively limited to the shared secret alone, which is insufficient when endpoints expose sensitive operations without additional controls.
Another contributing factor is improper secret management. If the shared Hmac secret is stored in configuration files exposed to version control or if it is transmitted or logged inadvertently, the integrity of the entire scheme collapses. Combined with unvalidated input and missing rate limiting, this can enable credential leakage or brute-force attempts. Security scans that include authentication and authorization checks alongside input validation and rate limiting are better equipped to surface these interrelated weaknesses in the unauthenticated attack surface.
Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes
To remediate Broken Authentication when using Hmac Signatures in Phoenix, align the signed payload with the full context of the request and enforce strict validation on server side. The following examples show a secure approach that includes method, path, timestamp, nonce, and body in the signature, and verifies these elements on each request.
First, client-side signature generation. The client constructs the string to sign from components that the server will also validate. Note the inclusion of timestamp and nonce to prevent replay, and canonical ordering to avoid ambiguity:
timestamp = System.system_time(:second) |> Integer.to_string()
nonce = :crypto.strong_rand_bytes(16) |> Base.url_encode64(padding: false)
body = Jason.encode!(%{user_id: "123", action: "update"})
string_to_sign = ["POST", "/api/v1/resource", timestamp, nonce, body] |> Enum.join("\n")
signature = :crypto.mac(:hmac, :sha256, secret_key, string_to_sign)
headers = %{
"authorization" => "Hmac-SHA256 key=\"#{client_key}\", timestamp=\"#{timestamp}\", nonce=\"#{nonce}\", signature=\"#{Base.url_encode64(signature, padding: false)}\"",
"content-type" => "application/json"
}
On the server side in Phoenix, a plug validates the signature, timestamp freshness, and nonce uniqueness. The plug retrieves the client key, reconstructs the same string to sign, and compares the signatures in constant time to avoid timing attacks:
defmodule MyAppWeb.HmacAuthPlug do
import Plug.Conn
import Phoenix.Controller, only: [json: 2, put_status: 2]
def init(opts), do: opts
def call(conn, _opts) do
with ["Hmac-SHA256 key=\"" <> key_id <> "\", timestamp=\"" <> ts <> "\", nonce=\"" <> nonce <> "\", signature=\"" <> received_sig <> "\"]" <- get_req_header(conn, "authorization"),
{:ok, secret} <- fetch_secret(key_id),
{:ok, true} <- verify_timestamp(ts),
{:ok, true} <- verify_nonce(nonce),
true <- valid_signature?(conn, secret, ts, nonce, received_sig) do
assign(conn, :authenticated, true)
else
_ -> send_resp(conn, 401, "Unauthorized") |> halt()
end
end
defp verify_timestamp(ts, max_age \\ 30) do
with {ts_int, ""} <- Integer.parse(ts),
now <- System.system_time(:second),
diff <- abs(now - ts_int),
true <- diff <= max_age do
{:ok, true}
else
_ -> {:ok, false}
end
end
defp verify_nonce(nonce) do
# Use a distributed cache or DB to ensure uniqueness within a window
case MyApp.NonceStore.check_and_insert(nonce) do
{:ok, true} -> {:ok, true}
_ -> {:ok, false}
end
end
defp valid_signature?(conn, secret, ts, nonce, received_sig) do
body = case conn.body_params do
%{} = p -> p |> Jason.encode!()
_ -> ""
end
string_to_sign = ["#{conn.method}", conn.request_path, ts, nonce, body] |> Enum.join("\n")
expected_sig = :crypto.mac(:hmac, :sha256, secret, string_to_sign)
# Constant-time compare
:crypto.verify(:hmac, :sha256, secret, string_to_sign, Base.url_decode64!(received_sig <> "=="))
end
defp fetch_secret(key_id) do
# Retrieve secret securely from a vault or encrypted configuration
case MyApp.SecretBackend.get(key_id) do
{:ok, secret} -> {:ok, secret}
_ -> {:error, :not_found}
end
end
end
In your router, plug the HmacAuthPlug before the action handling the endpoint to enforce authenticated requests:
pipeline :api do
plug MyAppWeb.HmacAuthPlug
plug :accepts, ["json"]
end
scope "/api", MyAppWeb do
pipe_through :api
put "/resource/:id", ResourceController, :update
end
These changes ensure that the signature covers the full request context, that replay windows are limited, and that secrets are retrieved securely. By combining these practices with continuous monitoring and scans that include authentication and input validation, you reduce the surface for Broken Authentication when using Hmac Signatures in Phoenix.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |