HIGH rate limiting bypasschibasic auth

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 IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

Can Basic Auth credentials be safely transmitted without TLS when using Chi?
No. Basic Auth credentials are base64-encoded but not encrypted. Always use TLS (HTTPS) to prevent eavesdropping. middleBrick’s Encryption check will flag endpoints that accept Basic Auth without transport-layer encryption.
How does middleBrick detect rate limiting issues when Basic Auth is involved?
During a scan, middleBrick tests authenticated and unauthenticated paths in parallel. It checks whether rate limiting is tied to the authenticated identity (e.g., per username) rather than to IP or endpoint alone. If limits can be bypassed by rotating valid Basic Auth tokens, the scan reports findings in Authentication, BOLA/IDOR, and Rate Limiting categories with specific remediation guidance.