X-Forwarded-For parsing and trusted proxy detection for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 105 lines 3.4 kB view raw
1(** X-Forwarded-For parsing and trusted proxy detection. 2 3 This module provides utilities for extracting client IP addresses from 4 X-Forwarded-For headers when running behind reverse proxies, with support 5 for trusted proxy validation to prevent IP spoofing. 6 7 {2 Example} 8 9 {[ 10 (* Configure trusted proxies *) 11 let trusted = Xff.private_ranges 12 13 (* Extract client IP from request *) 14 let socket_ip = Some (Ipaddr.of_string_exn "10.0.0.1") 15 16 let client_ip = 17 Xff.client_ip_string ~socket_ip 18 ~xff_header:(Some "203.0.113.50, 10.0.0.1") 19 ~trusted_proxies:(Some trusted) 20 21 let () = assert (client_ip = "203.0.113.50") 22 ]} 23 24 {2 References} 25 26 - {{:https://datatracker.ietf.org/doc/html/rfc7239} RFC 7239} - Forwarded 27 HTTP Extension 28 - X-Forwarded-For is a de-facto standard *) 29 30(** {1 Types} *) 31 32type ip = Ipaddr.t 33(** An IP address (IPv4 or IPv6). *) 34 35type prefix = Ipaddr.Prefix.t 36(** A CIDR prefix for matching IP ranges. *) 37 38(** {1 CIDR Parsing} *) 39 40val parse_cidr : string -> (prefix, [ `Msg of string ]) result 41(** [parse_cidr s] parses a CIDR string like ["192.168.0.0/16"] or 42 ["2001:db8::/32"]. *) 43 44val parse_cidr_exn : string -> prefix 45(** [parse_cidr_exn s] is like {!parse_cidr} but raises [Invalid_argument] on 46 error. *) 47 48(** {1 Trusted Proxy Detection} *) 49 50val ip_in_prefix : ip -> prefix -> bool 51(** [ip_in_prefix ip prefix] returns [true] if [ip] is within the CIDR [prefix]. 52*) 53 54val is_trusted_proxy : ip -> prefix list -> bool 55(** [is_trusted_proxy ip prefixes] returns [true] if [ip] matches any prefix in 56 the list. *) 57 58(** {1 X-Forwarded-For Parsing} *) 59 60val parse_xff : string -> ip list 61(** [parse_xff header] parses an X-Forwarded-For header value and returns all 62 valid IP addresses. The header format is ["client, proxy1, proxy2, ..."]. 63 Invalid entries are silently skipped. Port suffixes are stripped. *) 64 65val first_xff_ip : string -> ip option 66(** [first_xff_ip header] extracts the leftmost (original client) IP from an 67 X-Forwarded-For header. Returns [None] if no valid IP is found. *) 68 69(** {1 Client IP Extraction} *) 70 71val client_ip : 72 socket_ip:ip option -> 73 xff_header:string option -> 74 trusted_proxies:prefix list option -> 75 ip option 76(** [client_ip ~socket_ip ~xff_header ~trusted_proxies] extracts the real client 77 IP address considering trusted proxy configuration. 78 79 - If [socket_ip] is from a trusted proxy and [xff_header] is present, 80 returns the client IP from X-Forwarded-For 81 - Otherwise returns [socket_ip] 82 - Returns [None] if [socket_ip] is [None] 83 84 This prevents IP spoofing: only connections from trusted proxies can set the 85 client IP via X-Forwarded-For. *) 86 87val client_ip_string : 88 socket_ip:ip option -> 89 xff_header:string option -> 90 trusted_proxies:prefix list option -> 91 string 92(** [client_ip_string ~socket_ip ~xff_header ~trusted_proxies] is like 93 {!client_ip} but returns a string. Returns ["unknown"] if the IP cannot be 94 determined. *) 95 96(** {1 Common Trusted Proxy Ranges} *) 97 98val private_ranges : prefix list 99(** [private_ranges] is a list of RFC 1918 private ranges, loopback, and IPv6 100 equivalents: [10.0.0.0/8], [172.16.0.0/12], [192.168.0.0/16], [127.0.0.0/8], 101 [::1/128], [fc00::/7], [fe80::/10]. *) 102 103val cloudflare_ranges : prefix list 104(** Cloudflare proxy IP ranges (IPv4 and IPv6). See 105 {{:https://www.cloudflare.com/ips/} Cloudflare IPs}. *)