Completed
Push — master ( 92b32e...089ad5 )
by Richard
13:15
created

Fingerprint   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 9
c 1
b 1
f 0
lcom 0
cbo 2
dl 0
loc 79
ccs 0
cts 30
cp 0
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A makeInert() 0 4 1
A takePrint() 0 10 1
C checkSessionPrint() 0 27 7
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 Xoops\Core\Session;
13
14
use Xoops\Core\AttributeInterface;
15
use Xoops\Core\HttpRequest;
16
17
/**
18
 * Session management
19
 *
20
 * @category  Xoops\Core\Session
21
 * @package   Fingerprint
22
 * @author    Richard Griffith <[email protected]>
23
 * @copyright 2015 XOOPS Project (http://xoops.org)
24
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
25
 * @link      http://xoops.org
26
 */
27
class Fingerprint implements FingerprintInterface
28
{
29
    /**
30
     * The current request's client IP
31
     *
32
     * @var string
33
     */
34
    protected $clientFingerprint = array();
35
36
    /**
37
     * grab things from the http request we need to use.
38
     *
39
     * @return string[] array of fingerprint values
40
     */
41
    protected function takePrint()
42
    {
43
        $clientFingerprint = array();
44
        $httpRequest = HttpRequest::getInstance();
45
        $clientFingerprint['clientIp'] = $httpRequest->getClientIp();
46
        $clientFingerprint['userAgent'] = $this->makeInert($httpRequest->getHeader('USER_AGENT'));
47
        $clientFingerprint['acceptLanguage'] = $this->makeInert($httpRequest->getHeader('ACCEPT_LANGUAGE'));
48
49
        return $clientFingerprint;
50
    }
51
52
    /**
53
     * Neutralize some sequences that might be used to slip nefarious bits into our fingerprint.
54
     * This does not impair the similarity check, but does interfere with serialized object injection.
55
     *
56
     * @param string $value fingerprint string to be escaped
57
     *
58
     * @return string
59
     */
60
    protected function makeInert($value)
61
    {
62
        return str_replace(['\\', '{', '}', ':'], '-', $value);
63
    }
64
65
    /**
66
     * This method manages the session fingerprint
67
     *
68
     * Check current client Fingerprint against the values saved in the session.
69
     * Save the current Fingerprint to the session
70
     * Rate the fingerprint match pass/fail based on any changes
71
     * On fail, clear the session, leaving only the new client fingerprint
72
     *
73
     * @param AttributeInterface $session session manager object or another
74
     *                                    AttributeInterface implementing object
75
     *
76
     * @return bool true if matched, false if not
77
     */
78
    public function checkSessionPrint(AttributeInterface $session)
0 ignored issues
show
Coding Style introduced by
checkSessionPrint uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
79
    {
80
        $score = 0;   // combined levenshtein distance of changes
81
        $changes = 0; // number of changed fields
82
        $currentFingerprint = $this->takePrint();
83
        $savedFingerprint = unserialize($session->get('SESSION_FINGERPRINT'));
84
        if ($savedFingerprint === false) {
85
            $savedFingerprint = $currentFingerprint;
86
            $changes = empty($_SESSION) ? 0 : 3; // in a populated session - force fail;
87
        }
88
89
        foreach ($currentFingerprint as $key => $current) {
90
            $distance = levenshtein($current, $savedFingerprint[$key]);
91
            $score += $distance;
92
            $changes += ($distance>0) ? 1 : 0;
93
        }
94
95
        $return = true;
96
97
        // if more than one field changed, or if that change is a distance greater than 30, fail it.
98
        if (($changes > 1) || ($score > 30)) {
99
            $session->clear(); // session data should not be preserved
100
            $return = false;
101
        }
102
        $session->set('SESSION_FINGERPRINT', serialize($currentFingerprint));
103
        return $return;
104
    }
105
}
106