xref: /dokuwiki/_test/tests/inc/common_clientip.test.php (revision d5dd5d1b1d2bd9d3299e5d0114855dae264944df)
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