Privilege Escalation in Chi with Mutual Tls
Privilege Escalation in Chi with Mutual Tls
In Chi, privilege escalation via misconfigured mutual TLS (mTLS) often occurs when access controls are enforced at the transport layer but not complemented by application-level authorization. Chi services typically rely on mTLS to authenticate clients using certificates, yet if the server maps a valid client certificate to a high-privilege role without validating scopes, subject attributes, or business permissions, an attacker who compromises a low-privilege certificate can perform vertical privilege escalation.
Consider a Chi HTTP server that uses mTLS to verify client certificates but applies role-based checks only at the route level. If the server reads the certificate’s Common Name (CN) or Organization (O) and directly assigns a role (e.g., admin) based on that value, an attacker who can craft a certificate with a trusted CN can escalate to admin privileges. This is a BOLA/IDOR-like issue specific to mTLS where the trust boundary is incorrectly assumed to be sufficient for authorization.
Chi’s runtime does not inherently enforce least privilege; it is the developer’s responsibility to ensure that mTLS authentication is followed by explicit authorization checks. Without these, the API surface remains vulnerable to unauthenticated-like access from a malicious actor holding a low-privilege but valid certificate. Attack patterns include requesting admin-only endpoints using a certificate issued to a non-admin subject, or exploiting missing validation of certificate extensions (e.g., key usage, extended key usage) that should restrict usage.
Additionally, if the Chi service uses certificate-based identity but does not cross-check claims (e.g., from a certificate’s SAN or OIDC-style custom extensions) against an authorization model, horizontal privilege escalation can occur when one user accesses another user’s data. For example, an API endpoint /api/users/{userId} might trust the mTLS certificate to identify the caller but fail to ensure that the caller’s subject matches the requested userId. This is a BOLA flaw rooted in the assumption that transport-layer identity equals resource ownership.
Real-world mappings include references to OWASP API Top 10 2023:1 — Broken Object Level Authorization, which often intersects with mTLS misconfigurations. Unlike application-level tokens, mTLS identities are often treated as authoritative without secondary checks, increasing risk. The scanner checks for such gaps by correlating mTLS authentication findings with authorization checks across endpoints.
Mutual Tls-Specific Remediation in Chi
Remediation centers on ensuring that mTLS authentication is strictly separated from authorization and that identity claims are validated against an access control model. In Chi, prefer explicit authorization checks over implicit trust in certificate fields.
1. Validate certificate attributes beyond CN
Do not assign roles based solely on the certificate’s CN. Instead, inspect extended fields (e.g., SAN, custom OIDs) and map them to an authorization model. Below is a Chi handler that reads a custom SAN extension and enforces role checks:
open import Http
open import Data.String
open import Relation.Nullary
postHandler : Handler Response
postHandler = handler $
\req -> do
let clientCerts = getClientCertificates req
case clientCerts of
[] -> pure $ response401
cert:_ -> do
let san = getSAN cert
case lookupRoleFromSAN san of
Nothing -> pure $ response403
(Just role) -> if role =="admin" || role =="user"
then authorizedHandler req role
else pure $ response403
where
getSAN : Cert -> Maybe String
getSAN = ... -- extract SAN extension
lookupRoleFromSAN : String -> Maybe String
lookupRoleFromSAN san = ... -- map SAN to role
authorizedHandler : Request String -> String -> Handler Response
authorizedHandler req role =
if req.path =="/admin" && role /="admin"
then pure $ response403
else pure $ response200
2. Enforce least privilege with per-endpoint checks
For endpoints that act on user-specific resources, validate that the certificate identity matches the resource owner. Here’s an example for a user profile endpoint in Chi:
userProfileHandler : String -> Handler Response
userProfileHandler requestedUserId = handler $
\req -> do
clientCerts <- getClientCertificates req
case clientCerts of
[] -> pure $ response401
cert : _ -> do
let callerId = getSubjectId cert
if callerId == requestedUserId || callerHasScope cert "profile:read"
then pure $ response200
else pure $ response403
where
getSubjectId : Cert -> String
getSubjectId = ...
callerHasScope : Cert -> String -> Boolean
callerHasScope cert scope = ...
3. Combine mTLS with scopes or claims
Use certificate extensions or signed claims (e.g., from a private CA) to embed scopes, and validate them in Chi handlers. For example, a custom OID carrying scope=users:read can be checked before allowing read access:
scopeCheckHandler : Handler Response
scopeCheckHandler = handler $
\req -> do
certs <- getClientCertificates req
case certs of
[] -> pure $ response401
cert : _ -> if hasScope cert "users:read"
then pure $ response200
else pure $ response403
where
hasScope : Cert -> String -> Boolean
hasScope cert scope = ...
4. Avoid default admin mapping
Never map a certificate CN like admin directly to an admin role. Instead, maintain a server-side allowlist that maps certificate fingerprints or SANs to roles:
roleMapping : List (String, String) -- (certFingerprint, role)
roleMapping = [ ("a1b2c3...", "user")
, ("d4e5f6...", "admin")
]
resolveRole : String -> Maybe String
resolveRole fingerprint = lookup fingerprint roleMapping
5. Use Chi’s request context for identity
Store resolved identity in the request context after mTLS validation, and use it in handlers:
validateMTLS : Request a -> Maybe RequestWithIdentity
validateMTLS req = ... -- validate cert chain, extract identity
main : IO ()
main = do
routed $ \req -> case validateMTLS req of
Nothing -> response401
Just reqIdentity -> route $
[ ("admin", adminHandler reqIdentity)
, ("users", userHandler reqIdentity)
]
adminHandler : RequestWithIdentity -> Handler Response
adminHandler req = do
unless (isAdmin req) (pure response403)
-- proceed with admin logic
These patterns ensure that mTLS is used for authentication while Chi-specific authorization logic enforces least privilege, preventing privilege escalation across user, role, and permission boundaries.