Ssrf Server Side in Chi with Jwt Tokens
Ssrf Server Side in Chi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in a Chi application that validates or forwards requests with JWT tokens can expose internal services and bypass access controls. When an endpoint in Chi accepts a target URL and an optional authorization header containing a JWT token, and then performs an HTTP request on the server side, an attacker may supply a sensitive internal address (e.g., http://169.254.169.254/latest/meta-data/) while the server includes a valid JWT intended for a downstream service. The SSRF becomes critical if the forwarded request includes the JWT token, because the token may be accepted by internal services that trust it, leading to unauthorized data access or administrative actions.
Chi is a lightweight HTTP library for Clojure, and SSRF typically arises when routing or HTTP client logic does not validate or restrict the destination of outbound requests. If your Chi routes construct request URLs from user input and forward credentials (such as JWT tokens extracted from incoming headers), an attacker can force the server to interact with cloud metadata endpoints, internal RPC interfaces, or other restricted systems. JWT tokens often carry scopes or roles; leaking them via SSRF can expose token introspection endpoints or enable token replay against internal APIs. The risk is compounded when the JWT contains elevated privileges and the internal services rely solely on token presence for authorization without additional network-level restrictions.
For example, consider a Chi handler that receives a URL and an Authorization bearer token, then issues an outbound request while preserving the token:
(ns myapp.routes
(:require [cheshire.core :as json]
[clj-http.client :as http]
[compojure.core :refer [GET POST defroutes]]))
(defn proxy-handler [req]
(let [target-url (get-in req [:params "url"])
token (get-in req [:headers "authorization"])]
(try
(let [response (http/get target-url
{:headers {"Authorization" (str "Bearer " token)}})]
{:status (:status response)
:body (:body response)})
(catch Exception e
{:status 500
:body "Request failed"}))))
In this pattern, if target-url is user-controlled and the server blindly forwards the JWT, an attacker can set the URL to an internal metadata service. The server’s outbound request will include the JWT, and if the metadata service accepts it (or if the token is accidentally treated as valid elsewhere), sensitive information can be retrieved. Even without a trusted internal service, SSRF can be used for port scanning, SSRF-to-RCE via file:// or dict:// wrappers, or to exhaust internal resources.
To mitigate this within the Chi ecosystem, treat user-supplied URLs as untrusted and avoid forwarding JWT tokens to arbitrary destinations. Validate hostnames and ports, restrict schemes to HTTPS where possible, and enforce a strict allowlist for target hosts. If you must propagate authentication, prefer short-lived tokens scoped to specific internal endpoints and enforce additional authorization checks on the server receiving the token. Logging outbound requests and monitoring for unusual token usage can also help detect SSRF attempts that involve JWT tokens.
Jwt Tokens-Specific Remediation in Chi — concrete code fixes
Remediation focuses on preventing the SSRF vector and ensuring JWT tokens are not inadvertently used when calling untrusted URLs. Below are concrete, realistic code examples for Chi that demonstrate secure handling.
1. Avoid forwarding JWT tokens to user-provided URLs
Do not pass the incoming Authorization header to outbound calls. Instead, either omit credentials or use a dedicated service account with minimal scope:
(ns myapp.routes
(:require [cheshire.core :as json]
[clj-http.client :as http]
[compojure.core :refer [GET defroutes]]))
(defn safe-proxy-handler [req]
(let [target-url (get-in req [:params "url"])]
;; Validate target host against an allowlist
(when (some #(clojure.string/ends-with? target-url %) ["api.trusted.com" "internal.service.local"])
(let [response (http/get target-url
{:headers {}})]
{:status (:status response)
:body (:body response)}))))
2. Strict URL validation and scheme/host allowlisting
Ensure the target URL uses HTTPS and resolves to an allowed host to prevent redirection to internal addresses:
(ns myapp.security
(:require [clj-http.client :as http]
[net.cgrand.enlive-html :as html]))
(defn allowed-host? [url]
(let [parsed (java.net.URL. url)]
(and "https" (= "https" (.getProtocol parsed))
(contains? #{"api.trusted.com" "api.partner.com"} (.getHost parsed)))))
(defn validated-get [url]
(if (allowed-host? url)
(http/get url {:accept :json})
(throw (IllegalArgumentException. "Destination not allowed"))))
3. Use a sidecar token only for specific internal endpoints
If you must call internal services, keep the JWT usage narrow and do not derive it from user input:
(ns myapp.internal
(:require [clj-http.client :as http]))
(defn fetch-internal-metadata []
(let [token "service-specific-token" ;; never derived from user-supplied Authorization
url "https://internal.service.local/metadata"]
(http/get url {:headers {"Authorization" (str "Bearer " token)}})))
4. Log and monitor suspicious patterns
Log outbound target URLs and response statuses to detect SSRF attempts involving JWT-like strings:
(ns myapp.logging
(:require [taoensso.timbre :as log]))
(defn log-request [url status]
(log/info "outbound-request" :url url :status status))