Completed
Branch master (6afa16)
by Pierre-Henry
34:15
created

_protected/framework/Security/CSRF/Token.class.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @title          Token CSRF (Cross-site request forgery)
4
 * @desc           Protects against Cross-site request forgery attack.
5
 *
6
 * @author         Pierre-Henry Soria <[email protected]>
7
 * @copyright      (c) 2012-2017, Pierre-Henry Soria. All Rights Reserved.
8
 * @license        GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
9
 * @package        PH7 / Framework / Security / CSRF
10
 * @version        1.2
11
 */
12
13
namespace PH7\Framework\Security\CSRF;
14
15
defined('PH7') or exit('Restricted access');
16
17
use PH7\Framework\Session\Session;
18
use PH7\Framework\Navigation\Browser;
19
use PH7\Framework\Util\Various;
20
use PH7\Framework\Mvc\Model\DbConfig;
21
use PH7\Framework\Mvc\Request\Http;
22
use PH7\Framework\Ip\Ip;
23
use PH7\UserCore;
24
use PH7\AdminCore;
25
use PH7\AffiliateCore;
26
27
/**
28
 * This class provides functions of numbers against the XSS (Cross-site scripting) vulnerability.
29
 * PH Security Token (PHST)
30
 */
31
final class Token
32
{
33
    /**
34
     * @internal We have commented on "security_token_http_referer_*" because it causes bugs and it doesn't
35
     * play a big role for safety because this variable can be changed by users (and the web browser).
36
     */
37
38
    const VAR_NAME = 'pHST';
39
40
    /** @var Session */
41
    private $_oSession;
42
43
    /** @var null|string */
44
    private $_sHttpReferer;
45
46
    /** @var null|string */
47
    private $_sUserAgent;
48
49
    public function __construct()
50
    {
51
        $this->_oSession = new Session;
52
53
        $oBrowser = new Browser;
54
        $this->_sHttpReferer = $oBrowser->getHttpReferer();
0 ignored issues
show
Documentation Bug introduced by
It seems like $oBrowser->getHttpReferer() can also be of type array. However, the property $_sHttpReferer is declared as type null|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
55
        $this->_sUserAgent = $oBrowser->getUserAgent();
0 ignored issues
show
Documentation Bug introduced by
It seems like $oBrowser->getUserAgent() can also be of type array. However, the property $_sUserAgent is declared as type null|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
56
        unset($oBrowser);
57
    }
58
59
    /**
60
     * Generate a random token.
61
     *
62
     * @param string $sName
63
     *
64
     * @return string The Token generated random.
65
     */
66
    public function generate($sName)
67
    {
68
        // If the token is still valid, it returns the correct token
69
        if ($this->_oSession->exists('security_token_' . $sName)) {
70
            return $this->_oSession->get('security_token_' . $sName);
71
        } else {
72
            $sToken = Various::genRnd($sName);
73
74
            $aSessionData = [
75
                'security_token_' . $sName => $sToken,
76
                'security_token_time_' . $sName => time(),
77
                //'security_token_http_referer_' . $sName => $this->_sHttpReferer,
78
                'security_token_ip_' . $sName => Ip::get(),
79
                'security_token_http_user_agent_' . $sName => $this->_sUserAgent
80
            ];
81
82
            $this->_oSession->set($aSessionData);
83
            return $sToken;
84
        }
85
    }
86
87
    /**
88
     * @param string $sName Name of the Token.
89
     *
90
     * @param string $sInputToken The name of the token inserted in the hidden tag of the form.
91
     * (e.g. for a from with method "post" and the field "<input type="hidden" name="my_token" />" the name of the token is "$_POST['my_token']" Default NULL
92
     *
93
     * @param integer $iTime Lifetime of token in seconds. Default NULL (value specified in the database settings).
94
     *
95
     * @return boolean Returns TRUE if the token is validated, FALSE otherwise.
96
     */
97
    public function check($sName, $sInputToken = null, $iTime = null)
98
    {
99
        $iTime = (empty($iTime)) ? DbConfig::getSetting('securityTokenLifetime') : $iTime;
100
101
        // The default tag name for the security token
102
        $sInputToken = (empty($sInputToken)) ? (new Http)->post('security_token') : $sInputToken;
103
104
        $aCheckSession = [
105
            'security_token_' . $sName,
106
            'security_token_time_' . $sName,
107
            //'security_token_http_referer_' . $sName,
108
            'security_token_ip_' . $sName,
109
            'security_token_http_user_agent_' . $sName
110
        ];
111
112
        if ($this->_oSession->exists($aCheckSession) && !empty($sInputToken))
113
            if ($this->_oSession->get('security_token_' . $sName) === $sInputToken)
114
                if ($this->_oSession->get('security_token_time_' . $sName) >= (time() - $iTime))
115
                    //if ($this->_sHttpReferer === $this->_oSession->get('security_token_http_referer_' . $sName))
116
                        if (Ip::get() === $this->_oSession->get('security_token_ip_' . $sName))
117
                            if ($this->_sUserAgent === $this->_oSession->get('security_token_http_user_agent_' . $sName))
118
                            {
119
                                // Delete the token and data sessions expired
120
                                $this->_oSession->remove($aCheckSession);
121
                                return true;
122
                            }
123
124
        // Delete the token and data sessions expired
125
        $this->_oSession->remove($aCheckSession);
126
        return false;
127
    }
128
129
    /**
130
     * The Get Token parameter for the URL if someone is logged (User, Admin or Affiliate), nothing otherwise.
131
     *
132
     * @return string
133
     */
134
    public function url()
135
    {
136
        return ($this->currentSess() !== true) ? '?' . static::VAR_NAME . '=' . $this->currentSess() : '';
137
    }
138
139
    /**
140
     * Checks the URL Token.
141
     *
142
     * @return boolean
143
     */
144
    public function checkUrl()
145
    {
146
        $oHttpRequest = new Http;
147
        $bRet = ( ($this->currentSess() === true) || $oHttpRequest->currentUrl() === PH7_URL_ROOT || ($oHttpRequest->get(static::VAR_NAME) === $this->currentSess()) );
148
        unset($oHttpRequest);
149
150
        return $bRet;
151
    }
152
153
    /**
154
     * Gets The Current Session Token.
155
     *
156
     * @return string|boolean The "token" if a user is logged or "true" if no user is logged.
157
     */
158
    protected function currentSess()
159
    {
160
        if (UserCore::auth())
161
            $sToken = $this->_oSession->get('member_token');
162
        elseif (AdminCore::auth())
163
            $sToken = $this->_oSession->get('admin_token');
164
        elseif (AffiliateCore::auth())
165
            $sToken = $this->_oSession->get('affiliate_token');
166
        else $sToken = true; // If nobody is logged on, we did not need to do this test, so it returns true
167
168
        return $sToken;
169
    }
170
171
    /**
172
     * Clone is set to private to stop cloning.
173
     */
174
    private function __clone() {}
175
}
176