@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 77 lines 2.6 kB view raw
1<?php 2 3/** 4 * Parse the "X_FORWARDED_FOR" HTTP header to determine the original client 5 * address. 6 * 7 * @param int $layers (optional) Number of devices to trust. 8 * @return void 9 */ 10function preamble_trust_x_forwarded_for_header($layers = 1) { 11 if (!is_int($layers) || ($layers < 1)) { 12 echo 13 'preamble_trust_x_forwarded_for_header(<layers>): '. 14 '"layers" parameter must an integer larger than 0.'."\n"; 15 echo "\n"; 16 exit(1); 17 } 18 19 if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 20 return; 21 } 22 23 $forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR']; 24 if (!strlen($forwarded_for)) { 25 return; 26 } 27 28 $address = preamble_get_x_forwarded_for_address($forwarded_for, $layers); 29 30 $_SERVER['REMOTE_ADDR'] = $address; 31} 32 33function preamble_get_x_forwarded_for_address($raw_header, $layers) { 34 // The raw header may be a list of IPs, like "1.2.3.4, 4.5.6.7", if the 35 // request the load balancer received also had this header. In particular, 36 // this happens routinely with requests received through a CDN, but can also 37 // happen illegitimately if the client just makes up an "X-Forwarded-For" 38 // header full of lies. 39 40 // We can only trust the N elements at the end of the list which correspond 41 // to network-adjacent devices we control. Usually, we're behind a single 42 // load balancer and "N" is 1, so we want to take the last element in the 43 // list. 44 45 // In some cases, "N" may be more than 1, if the network is configured so 46 // that that requests are routed through multiple layers of load balancers 47 // and proxies. In this case, we want to take the Nth-to-last element of 48 // the list. 49 50 $addresses = explode(',', $raw_header); 51 52 // If we have more than one trustworthy device on the network path, discard 53 // corresponding elements from the list. For example, if we have 7 devices, 54 // we want to discard the last 6 elements of the list. 55 56 // The final device address does not appear in the list, since devices do 57 // not append their own addresses to "X-Forwarded-For". 58 59 $discard_addresses = ($layers - 1); 60 61 // However, we don't want to throw away all of the addresses. Some requests 62 // may originate from within the network, and may thus not have as many 63 // addresses as we expect. If we have fewer addresses than trustworthy 64 // devices, discard all but one address. 65 66 $max_discard = (count($addresses) - 1); 67 68 $discard_count = min($discard_addresses, $max_discard); 69 if ($discard_count) { 70 $addresses = array_slice($addresses, 0, -$discard_count); 71 } 72 73 $original_address = end($addresses); 74 $original_address = trim($original_address); 75 76 return $original_address; 77}