PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / http / remote-address / class-remote-address.php

class-remote-address.php in Defender Security – Malware Scanner, Login Security & Firewall 6.3.0, at src/component/http/remote-address/class-remote-address.php

292 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles the determination of the remote IP address based on server settings and configurations.
4 *
5 * @package WP_Defender\Component\Http\Remote_Address
6 */
7
8 namespace WP_Defender\Component\Http\Remote_Address;
9
10 use WP_Defender\Traits\IP;
11 use WP_Defender\Component\Trusted_Proxy_Preset\Trusted_Proxy_Preset;
12
13 /**
14 * Inspired from Laminas.
15 *
16 * @see https://github.com/laminas/laminas-http/blob/2.16.0/src/PhpEnvironment/RemoteAddress.php
17 */
18 class Remote_Address {
19
20 use IP;
21
22 /**
23 * Whether to use proxy addresses or not.
24 * As default this setting is disabled - IP address is mostly needed to increase
25 * security. HTTP_* are not reliable since can easily be spoofed. It can be enabled
26 * just for more flexibility, but if user uses proxy to connect to trusted services
27 * it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].
28 *
29 * @var bool
30 */
31 protected $use_proxy = false;
32
33 /**
34 * List of trusted proxy IP addresses.
35 *
36 * @var array
37 */
38 protected $trusted_proxies = array();
39
40 /**
41 * HTTP header to introspect for proxies.
42 *
43 * @var string
44 */
45 protected $proxy_header = 'HTTP_X_FORWARDED_FOR';
46
47 /**
48 * The trusted proxy preset.
49 *
50 * @var string
51 */
52 protected $trusted_proxy_preset = '';
53
54 /**
55 * Changes proxy handling setting.
56 * This must be static method, since validators are recovered automatically
57 * at session read, so this is the only way to switch setting.
58 *
59 * @param bool $use_proxy Whether to check also proxied IP addresses.
60 *
61 * @return $this
62 */
63 public function set_use_proxy( $use_proxy = true ): self {
64 $this->use_proxy = $use_proxy;
65
66 return $this;
67 }
68
69 /**
70 * Checks proxy handling setting.
71 *
72 * @return bool Current setting value.
73 */
74 public function get_use_proxy(): bool {
75 return $this->use_proxy;
76 }
77
78 /**
79 * Get the list of trusted proxy addresses.
80 *
81 * @return array
82 * @since 4.6.0
83 */
84 public function get_trusted_proxies(): array {
85 /**
86 * Filter the list of trusted proxies.
87 *
88 * @param array $trusted_proxies List of trusted proxy IP addresses or CIDR ranges.
89 *
90 * @since 4.6.0
91 */
92 $trusted_proxies = apply_filters( 'wpdef_firewall_trusted_proxies', $this->trusted_proxies );
93 $trusted_proxies = ! is_array( $trusted_proxies ) ? (array) $trusted_proxies : $trusted_proxies;
94
95 return $trusted_proxies;
96 }
97
98 /**
99 * Set list of trusted proxy addresses.
100 *
101 * @param array $trusted_proxies The array of trusted proxy IP addresses or CIDR ranges.
102 *
103 * @return $this
104 */
105 public function set_trusted_proxies( array $trusted_proxies ): self {
106 $this->trusted_proxies = $trusted_proxies;
107
108 return $this;
109 }
110
111 /**
112 * Set the header to introspect for proxy IPs.
113 *
114 * @param string $header The header to set for proxy IPs. Defaults to 'X-Forwarded-For'.
115 *
116 * @return $this
117 */
118 public function set_proxy_header( $header = 'X-Forwarded-For' ): self {
119 $this->proxy_header = $this->normalize_proxy_header( $header );
120
121 return $this;
122 }
123
124 /**
125 * Set the trusted proxy preset.
126 *
127 * @param string $trusted_proxy_preset The trusted proxy preset to set.
128 *
129 * @return $this
130 */
131 public function set_trusted_proxy_preset( string $trusted_proxy_preset ): self {
132 $this->trusted_proxy_preset = $trusted_proxy_preset;
133
134 return $this;
135 }
136
137 /**
138 * Returns client IP address.
139 *
140 * @return string IP address.
141 */
142 public function get_ip_address(): string {
143 $ip = $this->get_ip_address_from_proxy();
144
145 if ( $ip ) {
146 return $ip;
147 }
148
149 return defender_get_data_from_request( 'REMOTE_ADDR', 's' );
150 }
151
152 /**
153 * Attempt to get the IP address for a proxied client.
154 *
155 * @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
156 * @return false|string
157 */
158 protected function get_ip_address_from_proxy() {
159 if ( ! $this->use_proxy ) {
160 return false;
161 }
162
163 $header = $this->proxy_header;
164
165 $server = defender_get_data_from_request( null, 's' );
166 if ( ! isset( $server[ $header ] ) || ! is_string( $server[ $header ] ) || '' === $server[ $header ] ) {
167 return false;
168 }
169
170 $trusted_proxies = $this->get_trusted_proxies();
171 $remote_address = $server['REMOTE_ADDR'] ?? '';
172
173 // Forwarding headers are trustworthy only when the immediate peer is a known proxy.
174 if ( ! $this->is_trusted_proxy( $remote_address, $trusted_proxies ) ) {
175 return false;
176 }
177
178 // Extract IPs.
179 $ips = array_reverse( explode( ',', $server[ $header ] ) );
180 foreach ( $ips as $ip ) {
181 // trim, so we can compare against trusted proxies properly.
182 $ip = trim( $ip );
183 if ( false === filter_var( $ip, FILTER_VALIDATE_IP ) ) {
184 continue;
185 }
186
187 // @see http://en.wikipedia.org/wiki/X-Forwarded-For .
188 // Since we've removed any known, trusted proxy servers, the right-most
189 // address represents the first IP we do not know about -- i.e., we do
190 // not know if it is a proxy server, or a client. As such, we treat it
191 // as the originating IP.
192 foreach ( $trusted_proxies as $trusted_proxy ) {
193 if (
194 ( str_contains( $trusted_proxy, '/' ) && $this->compare_cidr( $ip, $trusted_proxy ) ) ||
195 $trusted_proxy === $ip
196 ) {
197 continue 2;
198 }
199 }
200
201 if ( $this->is_ip_in_trusted_proxy_preset( $ip ) ) {
202 continue;
203 }
204
205 return $ip;
206 }
207
208 return false;
209 }
210
211 /**
212 * Check whether an IP address belongs to a configured trusted proxy.
213 *
214 * @param string $ip The IP address to check.
215 * @param array $trusted_proxies Trusted proxy IP addresses or CIDR ranges.
216 *
217 * @return bool
218 */
219 protected function is_trusted_proxy( string $ip, array $trusted_proxies ): bool {
220 if ( false === filter_var( $ip, FILTER_VALIDATE_IP ) ) {
221 return false;
222 }
223
224 foreach ( $trusted_proxies as $trusted_proxy ) {
225 if (
226 ( str_contains( $trusted_proxy, '/' ) && $this->compare_cidr( $ip, $trusted_proxy ) ) ||
227 $trusted_proxy === $ip
228 ) {
229 return true;
230 }
231 }
232
233 return $this->is_ip_in_trusted_proxy_preset( $ip );
234 }
235
236 /**
237 * Normalize a header string.
238 * Normalizes a header string to a format that is compatible with
239 * $_SERVER.
240 *
241 * @param string $header The header string to normalize.
242 *
243 * @return string
244 */
245 protected function normalize_proxy_header( $header ): string {
246 $header = strtoupper( $header );
247 $header = str_replace( '-', '_', $header );
248
249 if ( ! str_starts_with( $header, 'HTTP_' ) ) {
250 $header = 'HTTP_' . $header;
251 }
252
253 return $header;
254 }
255
256 /**
257 * Check if IP is in trusted proxy preset.
258 *
259 * @param string $ip The IP address to check.
260 *
261 * @return bool
262 */
263 public function is_ip_in_trusted_proxy_preset( string $ip ): bool {
264 $trusted_ips = array();
265 if ( '' !== $this->trusted_proxy_preset ) {
266 /**
267 * Retrieve Trusted_Proxy_Preset instance.
268 *
269 * @var Trusted_Proxy_Preset $trusted_proxy_preset ;
270 */
271 $trusted_proxy_preset = wd_di()->get( Trusted_Proxy_Preset::class );
272 $trusted_proxy_preset->set_proxy_preset( $this->trusted_proxy_preset );
273 $trusted_ips = $trusted_proxy_preset->get_ips();
274 }
275
276 if ( ! is_array( $trusted_ips ) || array() === $trusted_ips ) {
277 return false;
278 }
279
280 foreach ( $trusted_ips as $trusted_ip ) {
281 if (
282 ( str_contains( $trusted_ip, '/' ) && $this->compare_cidr( $ip, $trusted_ip ) ) ||
283 $trusted_ip === $ip
284 ) {
285 return true;
286 }
287 }
288
289 return false;
290 }
291 }
292