Rate Limiting Bypass in Chi with Basic Auth
Rate Limiting Bypass in Chi with Basic Auth — how this specific combination creates or exposes the vulnerability
Chi is a lightweight HTTP client for Elixir that allows developers to make requests with custom headers, including Basic Authentication. When Basic Auth is used, credentials are typically passed in the Authorization header as a base64-encoded username:password string. If an API relies solely on Basic Auth for authentication but does not enforce rate limiting at the user or credential level, an attacker who obtains or guesses a valid set of credentials can bypass intended rate controls by rotating among multiple stolen or guessed credential pairs.
In a black-box scan, middleBrick tests unauthenticated attack surfaces and, when configured to probe authenticated endpoints, it can detect whether rate limiting is applied per authenticated identity rather than per IP or per endpoint. With Basic Auth, the server may incorrectly tie rate limits to the connection or IP address instead of the authenticated user. This creates a bypass vector: an attacker can make many requests using a single valid Basic Auth token, and if the server does not track request counts per user, the limit may never be enforced. middleBrick’s Authentication and Rate Limiting checks run in parallel and can surface cases where authenticated paths lack per-user throttling while unauthenticated paths are properly limited.
Chi clients usually construct requests with headers like {"authorization", "Basic " <> Base.encode64("user:pass")}. If the server implements rate limiting in a plug or router that does not extract the user identity from the Basic Auth token, limits may be applied incorrectly. For example, a limit of 100 requests per minute might be enforced per IP, allowing an attacker behind a single IP to cycle through valid Basic Auth credentials and exceed the intended per-user quota. middleBrick’s scan will flag this as a BOLA/IDOR or BFLA/Privilege Escalation risk when combined with weak rate limiting, noting that attackers can escalate privileges or abuse functionality intended for individual users.
Additionally, if the API exposes an OpenAPI spec with security schemes defined as basic, middleBrick’s spec analysis resolves the $ref paths and cross-references them with runtime behavior. It checks whether the rate limiting checks occur after authentication parsing and whether the identity extracted from Basic Auth is used as the key for rate limiting counters. Without this linkage, what appears to be a protected endpoint in the spec can be abused in practice. The scan’s per-category breakdown will highlight missing user-bound throttling and provide remediation guidance focused on tying limits to the authenticated user identity rather than transport-level identifiers.
Basic Auth-Specific Remediation in Chi — concrete code fixes
To prevent rate limiting bypass with Basic Auth in Chi, enforce limits using the authenticated user identity extracted from the credentials. Avoid relying on IP-based or connection-level counters. Below are concrete Chi examples that demonstrate how to implement per-user rate limiting using a token bucket approach with ETS and how to safely parse and validate Basic Auth headers.
Example 1: Per-user rate limiting with Basic Auth in Chi
defmodule MyApp.RateLimiter do
use GenServer
def start_link(opts) do
GenServer.start_link(__MODULE__, :ok, opts)
end
def init(:ok) do
# ETS table keyed by username extracted from Basic Auth
:ets.new(:rate_limits, [:named_table, :public, :set])
{:ok, %{}}
end
def allow_request?(username, limit_per_minute) do
now = System.system_time(:second)
window_start = now - 60
# Cleanup old entries and count recent requests
count = clean_and_count(username, window_start)
if count < limit_per_minute do
:ets.insert(:rate_limits, {{username, now}, 1})
true
else
false
end
end
defp clean_and_count(username, window_start) do
:ets.filter_delete(:rate_limits, fn
{{^username, ts}, _} when ts < window_start -> true
_ -> false
end)
:ets.select_count(:rate_limits, [{{ {username, :_}, :_ }, [], [true]}])
end
end
defmodule MyAppWeb.EndpointPlug do
import Plug.Conn
import Base
def init(opts), do: opts
def call(conn, _opts) do
case get_basic_auth_user(conn) do
{:ok, username} ->
if MyApp.RateLimiter.allow_request?(username, 100) do
conn
else
conn
|> put_resp_header("retry-after", "60")
|> send_resp(429, "Rate limit exceeded")
|> halt()
end
:error ->
conn
|> put_resp_header("www-authenticate", "Basic realm=\"API\"")
|> send_resp(401, "Unauthorized")
|> halt()
end
end
defp get_basic_auth_user(conn) do
with ["Basic " <> encoded] <- get_req_header(conn, "authorization"),
{:ok, creds} <- Base.decode64(encoded),
[username, password] <- String.split(creds, ":", parts: 2) do
# In practice, validate password against a secure store
{:ok, username}
else
_ -> :error
end
end
end
Example 2: Rejecting requests with missing or malformed Authorization
defmodule MyAppWeb.Plugs.EnsureAuth do
import Plug.Conn
import Base
def init(opts), do: opts
def call(conn, _opts) do
case get_req_header(conn, "authorization") do
["Basic " <> encoded] ->
case Base.decode64(encoded) do
{:ok, creds} ->
case String.split(creds, ":", parts: 2) do
[username, _password] when byte_size(username) > 0 ->
# Attach identity for downstream rate limiter
assign(conn, :auth_user, username)
_ ->
send_resp(conn, 400, "Bad credentials") |> halt()
end
_ ->
send_resp(conn, 400, "Invalid base64") |> halt()
end
_ ->
send_resp(conn, 401, "Missing Authorization header") |> halt()
end
end
end
These examples ensure that rate limiting is evaluated after extracting the username from the Basic Auth token. By storing counts keyed by username in ETS and cleaning expired entries within a sliding window, you prevent attackers from cycling credentials to bypass limits. middleBrick’s scans can validate that such controls are present and correctly scoped, reducing risks identified under Authentication, BOLA/IDOR, and Rate Limiting categories.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |