1<?php 2 3class common_clientIP_test extends DokuWikiTest { 4 5 /** 6 * @var mixed[] $configs Possible values for $conf['trustedproxies']. 7 */ 8 private $configs = [ 9 '^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)', 10 ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], 11 ]; 12 13 /** 14 * The data provider for clientIP() tests. 15 * 16 * @return mixed[][] Returns an array of test cases. 17 */ 18 public function client_ip_all_provider() : array { 19 // Malicious code in a header. 20 $bad = '<?php die("hacked"); ?>'; 21 22 // Letters A, B, C, D, E will be substitued with an IPv4 or IPv6 address. 23 $tests = [ 24 // A single IP with no other headers. 25 ['A', false, '', '', false, 'A'], 26 ['A', true, '', '', false, 'A'], 27 ['A', false, '', '', true, 'A'], 28 ['A', true, '', '', true, 'A'], 29 30 // A X-Real-IP header. 31 ['A', false, 'B', '', false, 'A'], 32 ['A', true, 'B', '', false, 'B,A'], 33 ['A', false, 'B', '', true, 'A'], 34 ['A', true, 'B', '', true, 'B'], 35 36 // An X-Forwarded-For header from an untrusted proxy. 37 ['A', false, 'B', 'C', false, 'A'], 38 ['A', true, 'B', 'C', false, 'B,A'], 39 ['A', false, 'B', 'C', true, 'A'], 40 ['A', true, 'B', 'C', true, 'B'], 41 42 // An X-Forwarded-For header from a trusted proxy. 43 ['D', false, 'B', 'C', false, 'C,D'], 44 ['D', true, 'B', 'C', false, 'B,C,D'], 45 ['D', false, 'B', 'C', true, 'C'], 46 ['D', true, 'B', 'C', true, 'B'], 47 48 // An X-Forwarded-For header with proxies from an untrusted proxy. 49 ['A', false, 'B', 'C,E', false, 'A'], 50 ['A', true, 'B', 'C,E', false, 'B,A'], 51 ['A', false, 'B', 'C,E', true, 'A'], 52 ['A', true, 'B', 'C,E', true, 'B'], 53 54 // An X-Forwarded-For header with untrusted proxies from a trusted proxy. 55 ['D', false, 'B', 'C,E', false, 'D'], 56 ['D', true, 'B', 'C,E', false, 'B,D'], 57 ['D', false, 'B', 'C,E', true, 'D'], 58 ['D', true, 'B', 'C,E', true, 'B'], 59 60 // An X-Forwarded-For header with an invalid proxy from a trusted proxy. 61 ['D', false, 'B', 'C,invalid,E', false, 'D'], 62 ['D', true, 'B', 'C,invalid,E', false, 'B,D'], 63 ['D', false, 'B', 'C,invalid,E', true, 'D'], 64 ['D', true, 'B', 'C,invalid,E', true, 'B'], 65 66 // Malicious X-Real-IP and X-Forwarded-For headers. 67 ['A', false, $bad, $bad, false, 'A'], 68 ['A', true, $bad, $bad, false, 'A'], 69 ['A', false, $bad, $bad, true, 'A'], 70 ['A', true, $bad, $bad, true, 'A'], 71 72 // Malicious remote address, X-Real-IP and X-Forwarded-For headers. 73 [$bad, false, $bad, $bad, false, '0.0.0.0'], 74 [$bad, true, $bad, $bad, false, '0.0.0.0'], 75 [$bad, false, $bad, $bad, true, '0.0.0.0'], 76 [$bad, true, $bad, $bad, true, '0.0.0.0'], 77 ]; 78 79 return $tests; 80 } 81 82 /** 83 * Test clientIP() with IPv6 addresses. 84 * 85 * @dataProvider client_ip_all_provider 86 * 87 * @param string $remoteAddr The TCP/IP remote IP address. 88 * @param bool $useRealIp True if using the X-Real-IP header is enabled in the config. 89 * @param string $realIp The X-Real-IP header. 90 * @param string $forwardedFor The X-Forwarded-For header. 91 * @param bool $single True to return the most likely client IP, false to return all candidates. 92 * @param string $expected The expected function result. 93 * 94 * @return void 95 */ 96 public function test_client_ip_v4(string $remoteAddr, bool $useRealIp, string $realIp, string $forwardedFor, bool $single, string $expected) : void { 97 global $conf; 98 99 $addresses = [ 100 'A' => '123.123.123.123', 101 'B' => '22.22.22.22', 102 'C' => '33.33.33.33', 103 'D' => '192.168.11.1', 104 'E' => '44.44.44.44', 105 ]; 106 107 $_SERVER['REMOTE_ADDR'] = str_replace(array_keys($addresses), array_values($addresses), $remoteAddr); 108 $_SERVER['HTTP_X_REAL_IP'] = str_replace(array_keys($addresses), array_values($addresses), $realIp); 109 $_SERVER['HTTP_X_FORWARDED_FOR'] = str_replace(array_keys($addresses), array_values($addresses), $forwardedFor); 110 $conf['realip'] = $useRealIp; 111 112 foreach ($this->configs as $config) { 113 $conf['trustedproxies'] = $config; 114 $this->assertEquals(str_replace(array_keys($addresses), array_values($addresses), $expected), clientIP($single)); 115 } 116 } 117 118 /** 119 * Test clientIP() with IPv6 addresses. 120 * 121 * @dataProvider client_ip_all_provider 122 * 123 * @param string $remoteAddr The TCP/IP remote IP address. 124 * @param bool $useRealIp True if using the X-Real-IP header is enabled in the config. 125 * @param string $realIp The X-Real-IP header. 126 * @param string $forwardedFor The X-Forwarded-For header. 127 * @param bool $single True to return the most likely client IP, false to return all candidates. 128 * @param string $expected The expected function result. 129 * 130 * @return void 131 */ 132 public function test_client_ip_v6(string $remoteAddr, bool $useRealIp, string $realIp, string $forwardedFor, bool $single, string $expected) : void { 133 global $conf; 134 135 $addresses = [ 136 'A' => '1234:1234:1234:1234:1234:1234:1234:1234', 137 'B' => '22:aa:22:bb:22:cc:22:dd', 138 'C' => '33:aa:33:bb:33:cc:33:dd', 139 'D' => '::1', 140 'E' => '44:aa:44:bb:44:cc:44:dd', 141 ]; 142 143 $_SERVER['REMOTE_ADDR'] = str_replace(array_keys($addresses), array_values($addresses), $remoteAddr); 144 $_SERVER['HTTP_X_REAL_IP'] = str_replace(array_keys($addresses), array_values($addresses), $realIp); 145 $_SERVER['HTTP_X_FORWARDED_FOR'] = str_replace(array_keys($addresses), array_values($addresses), $forwardedFor); 146 $conf['realip'] = $useRealIp; 147 148 foreach ($this->configs as $config) { 149 $conf['trustedproxies'] = $config; 150 $this->assertEquals(str_replace(array_keys($addresses), array_values($addresses), $expected), clientIP($single)); 151 } 152 } 153} 154