@@ -14,6 +14,13 @@ class Request extends UtopiaRequest
1414 */
1515 protected SwooleRequest $ swoole ;
1616
17+ /**
18+ * List of trusted proxy header names to check for client IP address
19+ *
20+ * @var array<string>
21+ */
22+ protected array $ trustedIpHeaders = [];
23+
1724 /**
1825 * Request constructor.
1926 */
@@ -64,18 +71,55 @@ public function setServer(string $key, string $value): static
6471 return $ this ;
6572 }
6673
74+ /**
75+ * Set trusted ip headers
76+ *
77+ * WARNING: Only set these headers if your application is behind a trusted proxy.
78+ * Trusting these headers when accepting direct client connections is a security risk.
79+ *
80+ * @param array<string> $headers List of header names to trust (e.g., ['x-forwarded-for', 'x-real-ip'])
81+ * @return static
82+ */
83+ public function setTrustedIpHeaders (array $ headers ): static
84+ {
85+ $ normalized = array_map ('strtolower ' , $ headers );
86+ $ trimmed = array_map ('trim ' , $ normalized );
87+ $ this ->trustedIpHeaders = array_filter ($ trimmed );
88+
89+ return $ this ;
90+ }
91+
6792 /**
6893 * Get IP
6994 *
70- * Returns users IP address.
71- * Support HTTP_X_FORWARDED_FOR header usually return
72- * from different proxy servers or PHP default REMOTE_ADDR
95+ * Extracts the client's IP address from trusted headers or falls back to the remote address.
96+ * Prioritizes headers like X-Forwarded-For when behind proxies or load balancers,
97+ * defaulting to REMOTE_ADDR when trusted headers are unavailable.
98+ *
99+ * @return string The validated client IP address or '0.0.0.0' if unavailable
73100 */
74101 public function getIP (): string
75102 {
76- $ ips = explode (', ' , $ this ->getHeader ('x-forwarded-for ' , $ this ->getServer ('remote_addr ' ) ?? '0.0.0.0 ' ));
103+ $ remoteAddr = $ this ->getServer ('REMOTE_ADDR ' ) ?? '0.0.0.0 ' ;
104+
105+ foreach ($ this ->trustedIpHeaders as $ header ) {
106+ $ headerValue = $ this ->getHeader ($ header );
107+
108+ if (empty ($ headerValue )) {
109+ continue ;
110+ }
111+
112+ // Leftmost IP address is the address of the originating client
113+ $ ips = explode (', ' , $ headerValue );
114+ $ ip = trim ($ ips [0 ]);
115+
116+ // Validate IP format (supports both IPv4 and IPv6)
117+ if (filter_var ($ ip , FILTER_VALIDATE_IP )) {
118+ return $ ip ;
119+ }
120+ }
77121
78- return trim ( $ ips [ 0 ] ?? '' ) ;
122+ return $ remoteAddr ;
79123 }
80124
81125 /**
0 commit comments