Path: blob/master/support/startup/preamble-utils.php
12240 views
<?php12/**3* Parse the "X_FORWARDED_FOR" HTTP header to determine the original client4* address.5*6* @param int Number of devices to trust.7* @return void8*/9function preamble_trust_x_forwarded_for_header($layers = 1) {10if (!is_int($layers) || ($layers < 1)) {11echo12'preamble_trust_x_forwarded_for_header(<layers>): '.13'"layers" parameter must an integer larger than 0.'."\n";14echo "\n";15exit(1);16}1718if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {19return;20}2122$forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR'];23if ($forwarded_for === null || !strlen($forwarded_for)) {24return;25}2627$address = preamble_get_x_forwarded_for_address($forwarded_for, $layers);2829$_SERVER['REMOTE_ADDR'] = $address;30}3132function preamble_get_x_forwarded_for_address($raw_header, $layers) {33// The raw header may be a list of IPs, like "1.2.3.4, 4.5.6.7", if the34// request the load balancer received also had this header. In particular,35// this happens routinely with requests received through a CDN, but can also36// happen illegitimately if the client just makes up an "X-Forwarded-For"37// header full of lies.3839// We can only trust the N elements at the end of the list which correspond40// to network-adjacent devices we control. Usually, we're behind a single41// load balancer and "N" is 1, so we want to take the last element in the42// list.4344// In some cases, "N" may be more than 1, if the network is configured so45// that that requests are routed through multiple layers of load balancers46// and proxies. In this case, we want to take the Nth-to-last element of47// the list.4849$addresses = explode(',', $raw_header);5051// If we have more than one trustworthy device on the network path, discard52// corresponding elements from the list. For example, if we have 7 devices,53// we want to discard the last 6 elements of the list.5455// The final device address does not appear in the list, since devices do56// not append their own addresses to "X-Forwarded-For".5758$discard_addresses = ($layers - 1);5960// However, we don't want to throw away all of the addresses. Some requests61// may originate from within the network, and may thus not have as many62// addresses as we expect. If we have fewer addresses than trustworthy63// devices, discard all but one address.6465$max_discard = (count($addresses) - 1);6667$discard_count = min($discard_addresses, $max_discard);68if ($discard_count) {69$addresses = array_slice($addresses, 0, -$discard_count);70}7172$original_address = end($addresses);73$original_address = trim($original_address);7475return $original_address;76}777879