Completed
Push — master ( 534dd4...28e496 )
by Richard
16s queued 10s
created

ProxyCheck::validateRoutableIP()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 2
nc 2
nop 1
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 XOOPS Project (https://xoops.org)
21
 * @license   GNU GPL 2 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 (!isset($_SERVER[$this->proxyHeaderName]) || empty($_SERVER[$this->proxyHeaderName])) {
112
            return false;
113
        }
114
        return $_SERVER[$this->proxyHeaderName];
115
    }
116
117
    /**
118
     * Extract 'for' IP address in FORWARDED header as in RFC 7239
119
     *
120
     * @param string $header
121
     *
122
     * @return string|false IP address, or false if invalid
123
     */
124
    protected function getFor($header)
125
    {
126
        $start = strpos($header, 'for=');
127
        if ($start === false) {
128
            return false;
129
        }
130
        $ip = substr($header, $start+4);
131
        $end = strpos($ip, ';');
132
        if ($end !== false) {
133
            $ip = substr($ip, 0, $end);
134
        }
135
        $ip = trim($ip, '"[] ');
136
137
        return $this->validateRoutableIP($ip);
138
    }
139
140
    /**
141
     * Process an X-Forwarded-For or Client-IP style header
142
     *
143
     * @param string $ip expected to be an IP address
144
     *
145
     * @return string|false IP address, or false if invalid
146
     */
147
    protected function getXForwardedFor($ip)
148
    {
149
        return $this->validateRoutableIP($ip);
150
    }
151
152
    /**
153
     * Validate that an IP address is routable
154
     *
155
     * @param string $ip an IP address to validate
156
     *
157
     * @return string|false IP address or false if invalid
158
     */
159
    protected function validateRoutableIP($ip)
160
    {
161
        if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
162
            return false;
163
        }
164
        return $ip;
165
    }
166
}