Unicode Normalization in Chi with Api Keys
Unicode Normalization in Chi with Api Keys — how this specific combination creates or exposes the vulnerability
Chi is an Elixir web framework that uses pattern-based routing and parameter parsing. When API keys are passed in HTTP headers or query parameters, Chi developers often normalize route segments or controller parameters to avoid duplicate routes or injection-like issues. Unicode normalization can interact poorly with Api Keys when keys are compared after normalization transformations, or when keys are embedded in paths or headers that Chi routes and parses.
For example, an Api Key containing characters like é (U+00E9) might be normalized by an upstream proxy or client into its composed form é (U+0065 U+0301). If Chi normalizes incoming request paths or headers differently than the system that issued the key, the normalized key no longer matches the stored canonical key. This mismatch can cause authentication bypasses or forced key collisions, effectively weakening the intended secrecy of the Api Key.
In addition, if an API endpoint accepts an Api Key as a user-controlled parameter (for example, ?api_key=...) and then uses that key in internal logic or logs without canonical normalization, an attacker can supply multiple visually identical representations to probe for key comparisons, timing differences, or logging inconsistencies. These behaviors can be discovered by the 12 security checks in middleBrick, including Input Validation and Property Authorization, which flag inconsistencies between spec-defined parameter constraints and runtime behavior.
When OpenAPI specs are provided, middleBrick resolves $ref definitions and cross-references them against runtime findings. This helps identify whether Api Key definitions in the spec tolerate multiple Unicode representations that could lead to authentication bypass or information leakage across normalization boundaries. The scanner does not modify code, but highlights how normalization inconsistencies may expose keys through authentication, authorization, or introspection endpoints.
Api Keys-Specific Remediation in Chi — concrete code fixes
To reduce risk, normalize Api Keys consistently in Chi before storage, comparison, and logging. Use a deterministic normalization strategy and apply it uniformly across issuance, validation, and introspection.
Example: Canonical normalization before comparison
defmodule MyApp.ApiKey do
@moduledoc """
Handles Api Key normalization and comparison.
"""
@doc """
Normalize an Api Key by NFC and downcasing ASCII letters.
"""
def normalize(key) when is_binary(key) do
key
|> String.downcase()
|> :unicode.characters_to_binary(:nfc)
end
@doc """
Validate and compare keys in constant time.
"""
def valid_key?(input, stored_key) do
input
|> normalize()
|> MyApp.Crypto.secure_compare(stored_key)
end
end
Example: Using the normalized key in a Plug pipeline
defmodule MyApp.ApiKeyPlug do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
case conn.query_params["api_key"] || get_req_header(conn, "x-api-key") do
[raw_key] ->
normalized = MyApp.ApiKey.normalize(raw_key)
# Perform lookup using the normalized key
case MyApp.Repo.get_by(MyApp.ApiKey, key_hash: hash_key(normalized)) do
nil -> send_resp(conn, 401, "Unauthorized")
key_record -> assign(conn, :api_key, key_record)
end
_ ->
send_resp(conn, 400, "Missing api_key")
end
end
defp hash_key(key) do
:crypto.hash(:sha256, key)
end
end
Example: Issuing keys with normalization at creation
defmodule MyApp.KeyGenerator do
@doc """
Generate and normalize an Api Key before persisting.
"""
def generate_key() do
raw_key = MyApp.Crypto.secure_random_hex(32)
normalized = MyApp.ApiKey.normalize(raw_key)
# Store only the normalized version or its hash
%MyApp.ApiKey{key_hash: hash_key(normalized)}
end
defp hash_key(key) do
:crypto.hash(:sha256, key)
end
end
For broader coverage, including detection of inconsistencies between spec definitions and runtime behavior, you can run a scan with middleBrick. Use the CLI to validate your endpoints: middlebrick scan https://api.example.com/openapi.json. The dashboard and reports can help track how parameter handling and authentication patterns evolve over time. The Pro plan adds continuous monitoring and CI/CD integration to flag regressions before deployment, while the MCP Server enables scanning directly from AI coding assistants in your workflow.