Skip to content

ackerleytng/clj-http-ssrf

 
 

Repository files navigation

clj-http-ssrf Build Status Clojars Project

A clj-http middleware designed to prevent SSRF attacks

Leiningen

[xtreak/clj-http-ssrf "0.2.1"]

Rationale

Since many web applications accept URLs to which some payload will be posted from the app like webhook integrations there is a possibility that the URL might be an internal URL.

E.g. On notification a payload {"success": true} will be posted to some Webhook URL so that the integrated application will consume the payload. But in some cases malicious users might enter private URLs of the app that can be accessed only from the app subnet. Since HTTP requests from the apps pass the subnet checks the app might end up posting a payload to it's own private endpoint. In the above case when the URL is given as http://private.app.com/private/secret/url/ then the payload will be passed to that endpoint which might cause some unforseen actions.

clj-http enables building custom middlewares so that we can effectively block the request from being made. We can return custom status like 404 instead of 403 so that the attacker might think the URL doesn't exist reducing the attack vector.

Usage

foo.core> (require '[clj-http-ssrf.core :refer :all])
foo.core> (client/with-middleware (conj client/default-middleware (wrap-validators :hosts ["10.0.0.0/1"]))
                      (client/get "http://10.0.10.11/secret/private/endpoint/"))
{:status 403, :headers {}, :body ""}
foo.core> (client/with-middleware (conj client/default-middleware (wrap-validators :hosts ["10.0.0.0/1"] :status 404))
                      (client/get "http://10.0.10.11/secret/private/endpoint/"))
{:status 404, :headers {}, :body ""}

If you'd prefer to work with predicates,

  • Add :ignore-unknown-host as true to ignore unknown hosts instead of raising exceptions.
(ns foo.core
  (:require [clj-http.client :as client]
            [clj-http-ssrf.core :refer :all]
            [clj-http-ssrf.reserved :as reserved]))

(defn safe-scheme?
  [scheme]
  (#{:http :https} scheme))

(defn safe-url?
  [url]
  (not (some #(re-find % url) [#"private" #"secret"])))

(defn safe-host?
  [ip-address]
  (not (some #(inet.data.ip/network-contains? % ip-address)
             (reserved/reserved-ip-ranges [:private]))))

(defn safe-port?
  [port]
  (#{80 443} port))

(client/with-middleware
  (conj client/default-middleware
        (wrap-predicates
         :scheme-pred safe-scheme?
         :url-pred safe-url?
         :host-pred safe-host?
         :port-pred safe-port?))
  (client/get "https://ifconfig.co" {:ignore-unknown-host true}))

Thanks

Implementation in other languages

Resources regarding SSRF

License

Copyright © 2018 Karthikeyan S

Distributed under the MIT License

About

A clj-http middleware to prevent SSRF attacks

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Clojure 100.0%