ProxyCheck   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 43
dl 0
loc 144
rs 10
c 0
b 0
f 0
wmc 18

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getProxyEnvConfig() 0 10 3
A splitOnComma() 0 4 1
A get() 0 19 4
A getXForwardedFor() 0 3 1
A getFor() 0 14 3
A getProxyHeader() 0 8 3
A validateRoutableIP() 0 6 2
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
namespace Xmf;
13
14
/**
15
 * ProxyCheck
16
 *
17
 * @category  Xmf\ProxyCheck
18
 * @package   Xmf
19
 * @author    Richard Griffith <[email protected]>
20
 * @copyright 2019-2020 XOOPS Project (https://xoops.org)
21
 * @license   GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
22
 */
23
class ProxyCheck
24
{
25
    const PROXY_ENVIRONMENT_VARIABLE = 'proxy_env';
26
27
    const FORWARDED = 'HTTP_FORWARDED';
28
29
    /** @var string|false header name determines how to process */
30
    protected $proxyHeaderName = false;
31
32
    /** @var string|false header data to process */
33
    protected $proxyHeader = false;
34
35
    /**
36
     * ProxyCheck constructor.
37
     */
38
    public function __construct()
39
    {
40
        /* must declare expected proxy in $xoopsConfig['proxy_env'] */
41
        $this->proxyHeaderName = $this->getProxyEnvConfig();
42
        $this->proxyHeader = $this->getProxyHeader();
43
    }
44
45
    /**
46
     * Get IP address from proxy header specified in $xoopsConfig['proxy_env']
47
     *
48
     * Returns proxy revealed valid client address, or false if such address was
49
     * not found.
50
     *
51
     * @return string|false
52
     */
53
    public function get()
54
    {
55
        if (false===$this->proxyHeaderName || false===$this->proxyHeader) {
56
            return false;
57
        }
58
        $proxyVars = $this->splitOnComma($this->proxyHeader);
0 ignored issues
show
Bug introduced by
It seems like $this->proxyHeader can also be of type true; however, parameter $header of Xmf\ProxyCheck::splitOnComma() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

58
        $proxyVars = $this->splitOnComma(/** @scrutinizer ignore-type */ $this->proxyHeader);
Loading history...
59
        // only consider the first (left most) value
60
        $header = reset($proxyVars);
61
        $ip = false;
62
        switch ($this->proxyHeaderName) {
63
            case static::FORWARDED:
64
                $ip = $this->getFor($header);
65
                break;
66
            default:
67
                $ip = $this->getXForwardedFor($header);
68
                break;
69
        }
70
71
        return $ip;
72
    }
73
74
    /**
75
     * Split comma delimited string
76
     *
77
     * @param string $header
78
     *
79
     * @return string[]
80
     */
81
    protected function splitOnComma($header)
82
    {
83
        $parts = explode(',', $header);
84
        return array_map('trim', $parts);
85
    }
86
87
    /**
88
     * get configured proxy environment variable
89
     *
90
     * @return string|bool
91
     */
92
    protected function getProxyEnvConfig()
93
    {
94
        global $xoopsConfig;
95
96
        /* must declare expected proxy in $xoopsConfig['proxy_env'] */
97
        if (!isset($xoopsConfig[static::PROXY_ENVIRONMENT_VARIABLE])
98
            || empty($xoopsConfig[static::PROXY_ENVIRONMENT_VARIABLE])) {
99
            return false;
100
        }
101
        return trim($xoopsConfig[static::PROXY_ENVIRONMENT_VARIABLE]);
102
    }
103
104
    /**
105
     * get the configured proxy header
106
     *
107
     * @return string|false
108
     */
109
    protected function getProxyHeader()
110
    {
111
        if (false === $this->proxyHeaderName || empty($_SERVER[$this->proxyHeaderName])) {
112
            return false;
113
        }
114
115
        // Use PHP 5.3 compatible type casting
116
        return (string)$_SERVER[$this->proxyHeaderName];
117
    }
118
119
    /**
120
     * Extract 'for' IP address in FORWARDED header as in RFC 7239
121
     *
122
     * @param string $header
123
     *
124
     * @return string|false IP address, or false if invalid
125
     */
126
    protected function getFor($header)
127
    {
128
        $start = strpos($header, 'for=');
129
        if ($start === false) {
130
            return false;
131
        }
132
        $ip = substr($header, $start+4);
133
        $end = strpos($ip, ';');
134
        if ($end !== false) {
135
            $ip = substr($ip, 0, $end);
136
        }
137
        $ip = trim($ip, '"[] ');
138
139
        return $this->validateRoutableIP($ip);
140
    }
141
142
    /**
143
     * Process an X-Forwarded-For or Client-IP style header
144
     *
145
     * @param string $ip expected to be an IP address
146
     *
147
     * @return string|false IP address, or false if invalid
148
     */
149
    protected function getXForwardedFor($ip)
150
    {
151
        return $this->validateRoutableIP($ip);
152
    }
153
154
    /**
155
     * Validate that an IP address is routable
156
     *
157
     * @param string $ip an IP address to validate
158
     *
159
     * @return string|false IP address or false if invalid
160
     */
161
    protected function validateRoutableIP($ip)
162
    {
163
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
164
            return false;
165
        }
166
        return $ip;
167
    }
168
}
169