Arp Spoofing in Phoenix with Hmac Signatures
Arp Spoofing in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Arp Spoofing is a Layer 2 attack where an adversary sends falsified ARP messages to associate their MAC address with the IP of a legitimate host, such as a gateway or another service in Phoenix. When Hmac Signatures are used to protect API requests or internal service communication, an attacker who successfully performs Arp Spoofing can intercept, modify, or replay signed requests. This becomes critical when Hmac Signatures are computed over a subset of request properties and the attacker can observe or alter the intercepted traffic before it reaches the intended signer or verifier.
In a typical Phoenix setup using Hmac Signatures, clients sign requests with a shared secret and include the signature in headers. If an attacker on the same network in Phoenix poisons ARP tables, they can position themselves as a man-in-the-middle, capturing the request and response pairs. Because the attacker can see the headers, method, path, and signed parameters, they may attempt to reissue the same request (replay) or modify non-signature-covered parameters (such as timestamps or IDs) and forward it to the server. If the server does not enforce strict nonce and timestamp validation alongside signature checks, the forged request may be accepted as valid.
The exposure is compounded when services in Phoenix use Hmac Signatures over HTTP rather than HTTPS, as ARP spoofing is a local-network attack and HTTPS would prevent tampering with the payload after interception. Even with HTTPS, if the client-side signature logic does not bind the signature tightly to the exact request context (including headers and body), an attacker who can intercept and slightly alter the request might bypass integrity checks. Additionally, if the shared Hmac key is transmitted or derived insecurely on the Phoenix network, ARP spoofing facilitates key observation or injection, weakening the overall assurance provided by Hmac Signatures.
Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes
Remediation focuses on ensuring that Hmac Signatures in Phoenix cover all relevant request dimensions and that replay and tampering are prevented. Use HTTPS to protect traffic at the network layer, and design signing logic that includes a timestamp, a nonce, and a stable representation of the request body and selected headers. Validate these elements rigorously on the server before checking the signature.
Below are concrete Elixir examples for Phoenix that demonstrate secure Hmac Signature generation and verification, including replay protection with a timestamp and nonce.
defmodule MyApp.Hmac do
@moduledoc """
Secure Hmac signing and verification for Phoenix requests and responses.
"""
@ttl 300_000 # 5 minutes in milliseconds
@doc """
Build a canonical string for signing. Includes method, path, timestamp, nonce, and hashed body.
"""
def canonical_string(method, path, timestamp, nonce, body) do
body_hash = :crypto.hash(:sha256, body)
Base.encode16(body_hash, case: :lower) <> "\n" <> method <> "\n" <> path <> "\n" <> to_string(timestamp) <> "\n" <> nonce
end
@doc """
Sign the canonical string using Hmac.Sha256 with a secret fetched securely at runtime.
"""
def sign(canonical, secret) do
:crypto.mac(:hmac, :sha256, secret, canonical)
|> Base.encode16(case: :lower)
end
@doc """
Verify the signature and enforce replay/tamper protection.
"""
def verify(method, path, timestamp, nonce, body, received_signature, secret) do
now = System.system_time(:millisecond)
case within_tolerance?(timestamp, now) do
true ->
canonical = canonical_string(method, path, timestamp, nonce, body)
expected = sign(canonical, secret)
if secure_compare(expected, received_signature) and !replayed?(nonce, timestamp) do
:ok
else
{:error, :invalid_signature}
end
false ->
{:error, :stale_timestamp}
end
end
defp within_tolerance?(timestamp, now, tolerance_ms // @ttl) do
abs(now - timestamp) <= tolerance_ms
end
defp replayed?(nonce, _timestamp) do
# Implement a distributed cache check for the nonce (e.g., Redis or Mnesia)
# Returning false here as a placeholder.
false
end
defp secure_compare(a, b) when byte_size(a) == byte_size(b) do
# Constant-time comparison to avoid timing attacks
:crypto.verify(:hmac, :sha256, a, b)
end
defp secure_compare(_a, _b), do: false
end
defmodule MyAppWeb.ApiController do
use MyAppWeb, :controller
plug :ensure_signed_request when action in [:create, :update, :delete]
defp ensure_signed_request(conn, _opts) do
method = conn.method
path = conn.request_path
timestamp = conn.req_headers["x-request-timestamp"] |> List.first() |> String.to_integer()
nonce = conn.req_headers["x-request-nonce"] |> List.first()
body = conn.body_params |> Jason.encode!()
received_signature = conn.req_headers["x-signature"] |> List.first()
secret = fetch_secret_for_peer(conn) # retrieve securely, e.g., from config or vault
case MyApp.Hmac.verify(method, path, timestamp, nonce, body, received_signature, secret) do
:ok -> conn
{:error, reason} -> conn |> put_status(:unauthorized) |> json(%{error: reason}) |> halt()
end
end
defp fetch_secret_for_peer(conn) do
# Example: map IP or peer identifier to a shared secret stored securely
peer = conn.remote_ip |> :inet.ntoa() |> to_string()
Application.get_env(:my_app, :hmac_secrets)[peer] || raise "No secret for peer"
end
end