CsrfManager::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Jidaikobo\Kontiki\Managers;
4
5
use Aura\Session\Session;
6
7
class CsrfManager
8
{
9
    private const SEGMENT_NAME = 'jidaikobo\kontiki\csrf';
10
    private const MAX_HISTORY = 10;
11
    private const EXPIRATION_TIME = 300; // sec.
12
    private Session $session;
13
    private $segment;
14
15
    public function __construct(Session $session)
16
    {
17
        $this->session = $session;
18
        $this->segment = $this->session->getSegment(self::SEGMENT_NAME);
19
    }
20
21
    /**
22
     * Obtaining a CSRF token
23
     *
24
     * @return string
25
     */
26
    public function getToken(): string
27
    {
28
        return $this->session->getCsrfToken()->getValue();
29
    }
30
31
    /**
32
     * Validating CSRF tokens
33
     *
34
     * @param string|null $token
35
     * @return bool
36
     */
37
    public function isValid(?string $token): bool
38
    {
39
        $currentToken = $this->getToken();
40
        $history = $this->getTokenHistory();
41
42
        // Allows current token or tokens in history
43
        return (!empty($currentToken) && hash_equals($currentToken, $token)) ||
0 ignored issues
show
Bug introduced by
It seems like $token can also be of type null; however, parameter $user_string of hash_equals() 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

43
        return (!empty($currentToken) && hash_equals($currentToken, /** @scrutinizer ignore-type */ $token)) ||
Loading history...
44
               in_array($token, array_column($history, 'token'), true);
45
    }
46
47
    /**
48
     * Regenerating the CSRF token
49
     *
50
     * @return void
51
     */
52
    public function regenerate(): void
53
    {
54
        $csrfToken = $this->session->getCsrfToken();
55
56
        // Add the current token to the history
57
        $history = $this->getTokenHistory();
58
        $history[] = [
59
            'token' => $csrfToken->getValue(),
60
            'timestamp' => time(),
61
        ];
62
63
        // Trim history (remove old stuff)
64
        $history = array_filter($history, function ($entry) {
65
            return time() - $entry['timestamp'] <= self::EXPIRATION_TIME;
66
        });
67
        if (count($history) > self::MAX_HISTORY) {
68
            $history = array_slice($history, -self::MAX_HISTORY);
69
        }
70
71
        // Save history in session
72
        $this->segment->set('csrf_token_history', $history);
73
74
        // Regenerating the CSRF token
75
        $csrfToken->regenerateValue();
76
    }
77
78
    /**
79
     * Get Token History
80
     *
81
     * @return array
82
     */
83
    private function getTokenHistory(): array
84
    {
85
        return $this->segment->get('csrf_token_history') ?? [];
86
    }
87
}
88