Passed
Push — master ( 81ac23...a5d5f6 )
by El
03:02
created

lib/Persistence/TrafficLimiter.php (3 issues)

Severity

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
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1.1
11
 */
12
13
namespace PrivateBin\Persistence;
14
15
use PrivateBin\Configuration;
16
17
/**
18
 * TrafficLimiter
19
 *
20
 * Handles traffic limiting, so no user does more than one call per 10 seconds.
21
 */
22
class TrafficLimiter extends AbstractPersistence
23
{
24
    /**
25
     * time limit in seconds, defaults to 10s
26
     *
27
     * @access private
28
     * @static
29
     * @var    int
30
     */
31
    private static $_limit = 10;
32
33
    /**
34
     * key to fetch IP address
35
     *
36
     * @access private
37
     * @static
38
     * @var    string
39
     */
40
    private static $_ipKey = 'REMOTE_ADDR';
41
42
    /**
43
     * set the time limit in seconds
44
     *
45
     * @access public
46
     * @static
47
     * @param  int $limit
48
     */
49 45
    public static function setLimit($limit)
50
    {
51 45
        self::$_limit = $limit;
52 45
    }
53
54
    /**
55
     * set configuration options of the traffic limiter
56
     *
57
     * @access public
58
     * @static
59
     * @param Configuration $conf
60
     */
61 44
    public static function setConfiguration(Configuration $conf)
0 ignored issues
show
setConfiguration uses the super-global variable $_SERVER 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...
62
    {
63 44
        self::setLimit($conf->getKey('limit', 'traffic'));
64 44
        self::setPath($conf->getKey('dir', 'traffic'));
65 44
        if (($option = $conf->getKey('header', 'traffic')) !== null) {
66 2
            $httpHeader = 'HTTP_' . $option;
67 2
            if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
68 2
                self::$_ipKey = $httpHeader;
69
            }
70
        }
71 44
    }
72
73
    /**
74
     * get a HMAC of the current visitors IP address
75
     *
76
     * @access public
77
     * @static
78
     * @param  string $algo
79
     * @return string
80
     */
81 20
    public static function getHash($algo = 'sha512')
0 ignored issues
show
getHash uses the super-global variable $_SERVER 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...
82
    {
83 20
        return hash_hmac($algo, $_SERVER[self::$_ipKey], ServerSalt::get());
84
    }
85
86
    /**
87
     * traffic limiter
88
     *
89
     * Make sure the IP address makes at most 1 request every 10 seconds.
90
     *
91
     * @access public
92
     * @static
93
     * @throws Exception
94
     * @return bool
95
     */
96 45
    public static function canPass()
0 ignored issues
show
canPass uses the super-global variable $GLOBALS 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...
97
    {
98
        // disable limits if set to less then 1
99 45
        if (self::$_limit < 1) {
100 38
            return true;
101
        }
102
103 9
        $file = 'traffic_limiter.php';
104 9
        if (!self::_exists($file)) {
105 9
            self::_store(
106 9
                $file,
107 9
                '<?php' . PHP_EOL .
108 9
                '$GLOBALS[\'traffic_limiter\'] = array();' . PHP_EOL
109
            );
110
        }
111
112 9
        $path = self::getPath($file);
113 9
        require $path;
114 9
        $now = time();
115 9
        $tl  = $GLOBALS['traffic_limiter'];
116
117
        // purge file of expired hashes to keep it small
118 9
        foreach ($tl as $key => $time) {
119 3
            if ($time + self::$_limit < $now) {
120 3
                unset($tl[$key]);
121
            }
122
        }
123
124
        // this hash is used as an array key, hence a shorter hash is used
125 9
        $hash = self::getHash('sha256');
126 9
        if (array_key_exists($hash, $tl) && ($tl[$hash] + self::$_limit >= $now)) {
127 3
            $result = false;
128
        } else {
129 9
            $tl[$hash] = time();
130 9
            $result    = true;
131
        }
132 9
        self::_store(
133 9
            $file,
134 9
            '<?php' . PHP_EOL .
135 9
            '$GLOBALS[\'traffic_limiter\'] = ' .
136 9
            var_export($tl, true) . ';' . PHP_EOL
137
        );
138 9
        return $result;
139
    }
140
}
141