HIGH sql injectionchiapi keys

Sql Injection in Chi with Api Keys

Sql Injection in Chi with Api Keys — how this specific combination creates or exposes the vulnerability

Chi is a lightweight HTTP routing library for OCaml, and using it with API keys often involves extracting the key from request headers and passing it into application logic or downstream services. If API key handling is combined with dynamic SQL construction, the typical injection pattern becomes possible: user-controlled input (e.g., an ID or filter parameter) is concatenated into a SQL string while the API key is used only for authentication at the handler level. The presence of an API key does not sanitize inputs; if developers mistakenly trust the API key context and build queries by string interpolation, the attack surface remains wide open.

Consider a Chi route that reads an API key from headers and uses a query parameter to fetch a user record:

let get_user = Chi.Router.get "/user" @@ fun req ->
  let open Lwt.Infix in
  match Cohttp.Header.get (Cohttp.Request.headers req) "x-api-key" with
  | Some key when validate_key key ->
    let user_id = Chi.Param.get req "id" |> Option.get in
    let query = Printf.sprintf "SELECT * FROM users WHERE id = %s" user_id in
    Lwt.return (Cohttp_lwt_unix.Body.to_string (Pool.Pool.find_by_sql query))
  | _ ->
    Cohttp_lwt_unix.Server.respond_string ~status:`Unauthorized ~body:"Invalid API key" ()

In this example, the API key is validated, but user_id is taken directly from the URL parameter and interpolated into the SQL string. An attacker can supply id=1 OR 1=1 and, if the underlying pool or driver does not enforce strict parameterization, the query may bypass intended filters. The API key check is orthogonal to data access logic; it does not prevent malicious SQL execution. Even if the API key is required, the operation can still read or modify unintended rows, demonstrating that authentication and input validation are separate concerns.

OWASP API Top 10 A03:2023 (Injection) applies here. The risk is elevated when developers conflate authentication (API key presence) with safe data handling. Real-world parallels include cases where SQL injection leads to data exfiltration or privilege escalation despite token-based checks. Tools like middleBrick detect such patterns by correlating API key handling routes with dynamic SQL usage and flagging missing parameterization.

middleBrick can scan a Chi service endpoint without credentials and highlight these risky patterns in its findings, providing prioritized guidance and references to the relevant OWASP category.

Api Keys-Specific Remediation in Chi — concrete code fixes

Remediation centers on strict separation of authentication and data access, and using parameterized queries or an ORM that enforces compile-time safety. Never concatenate user input into SQL strings, even when an API key has been validated.

1) Use a proper SQL client with parameterized statements. For example, with postgresql-simple:

let get_user_safe req pool =
  let open Lwt.Infix in
  match Cohttp.Header.get (Cohttp.Request.headers req) "x-api-key" with
  | Some key when validate_key key ->
    let user_id = Chi.Param.get req "id" in
    (match user_id with
     | Some uid -
       Postgresql.find pool [uid]
       |> Lwt.map (function
           | [row] -> Cohttp_lwt_unix.Server.respond_string ~status:`OK ~body:(Sexplib.Sexp.to_string_hum (row_to_sexp row))
           | _ -> Cohttp_lwt_unix.Server.respond_string ~status:`Not_found ~body:"Not found")
     | None -> Cohttp_lwt_unix.Server.respond_string ~status:`Bad_request ~body:"Missing id")
  | _ ->
    Cohttp_lwt_unix.Server.respond_string ~status:`Unauthorized ~body:"Invalid API key" ()

In this version, Postgresql.find uses a parameterized query under the hood, ensuring uid is not interpreted as SQL. The API key check remains separate and early, but the SQL execution is safe.

2) Use an OCaml type-safe query builder or SORM that enforces structure. For instance, with ocaml-sqlt or a similar library, construct queries via AST rather than strings:

let build_select id =
  let open Sql.Infix in
  Sql.(select @@ from "users" @@ where ("id" =? id))

let execute_safe req pool =
  let open Lwt.Infix inn
  match Cohttp.Header.get (Cohttp.Request.headers req) "x-api-key" with
  | Some key when validate_key key ->
    let user_id = Chi.Param.get req "id" in
    (match user_id with
     | Some uid ->
       let query = build_select uid in
       Pool.execute_sql pool query
       |> Lwt.map (function
           | Ok rows -> Cohttp_lwt_unix.Server.respond_string ~status:`OK ~body:(Sexplib.Sexp.to_string_hum (rows_to_sexp rows))
           | Error _ -> Cohttp_lwt_unix.Server.respond_string ~status:`Internal_server_error ~body:"DB error")
     | None -> Cohttp_lwt_unix.Server.respond_string ~status:`Bad_request ~body:"Missing id")
  | _ ->
    Cohttp_lwt_unix.Server.respond_string ~status:`Unauthorized ~body:"Invalid API key" ()

3) Validate and parse IDs strictly. Enforce integer parsing and reject non-numeric input before it reaches SQL:

let parse_id id_str =
  try Some (int_of_string id_str) with
  | Failure _ -> None

let get_user_typed req pool =
  let open Lwt.Infix in
  match Cohttp.Header.get (Cohttp.Request.headers req) "x-api-key" with
  | Some key when validate_key key ->
    let user_id = Chi.Param.get req "id" |> parse_id in
    (match user_id with
     | Some uid ->
       let query = Printf.sprintf "SELECT * FROM users WHERE id = $1" in
       Pool.Pool.find_by_sql_typed pool query [uid]
       |> Lwt.map (fun rows -> Cohttp_lwt_unix.Server.respond_string ~status:`OK ~body:(rows_to_json rows))
     | None -> Cohttp_lwt_unix.Server.respond_string ~status:`Bad_request ~body:"Invalid id")
  | _ ->
    Cohttp_lwt_unix.Server.respond_string ~status:`Unauthorized ~body:"Invalid API key" ()

These patterns ensure that API key authentication does not inadvertently encourage unsafe SQL construction. middleBrick’s scans can verify that endpoints requiring keys do not exhibit injection-prone behaviors and align with secure coding practices.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does validating an API key prevent SQL injection in Chi endpoints?
No. API key validation is an authentication check and does not sanitize inputs used in SQL. Injection risk depends on how the application builds queries; always use parameterized queries regardless of authentication.
Can middleBrick detect SQL injection risks in Chi services that use API keys?
Yes. middleBrick scans the unauthenticated attack surface and can identify patterns where user-influenced data reaches SQL construction, even when API key checks are present. Its findings include severity, context, and remediation guidance.