summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php
blob: c59e1f91c292f2d860444f3f4a5a8af2af4a86b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */

namespace Icinga\Web\Form\Element;

use Icinga\Web\Session;
use Icinga\Web\Form\FormElement;
use Icinga\Web\Form\InvalidCSRFTokenException;

/**
 * CSRF counter measure element
 *
 * You must not set a value to successfully use this element, just give it a name and you're good to go.
 */
class CsrfCounterMeasure extends FormElement
{
    /**
     * Default form view helper to use for rendering
     *
     * @var string
     */
    public $helper = 'formHidden';

    /**
     * Counter measure element is required
     *
     * @var bool
     */
    protected $_ignore = true;

    /**
     * Ignore element when retrieving values at form level
     *
     * @var bool
     */
    protected $_required = true;

    /**
     * Initialize this form element
     */
    public function init()
    {
        $this->setDecorators(['ViewHelper']);
        $this->setValue($this->generateCsrfToken());
    }

    /**
     * Check whether $value is a valid CSRF token
     *
     * @param   string      $value          The value to check
     * @param   mixed       $context        Context to use
     *
     * @return  bool                        True, in case the CSRF token is valid
     *
     * @throws  InvalidCSRFTokenException   In case the CSRF token is not valid
     */
    public function isValid($value, $context = null)
    {
        if (parent::isValid($value, $context) && $this->isValidCsrfToken($value)) {
            return true;
        }

        throw new InvalidCSRFTokenException();
    }

    /**
     * Check whether the given value is a valid CSRF token for the current session
     *
     * @param   string  $token  The CSRF token
     *
     * @return  bool
     */
    protected function isValidCsrfToken($token)
    {
        if (strpos($token, '|') === false) {
            return false;
        }

        list($seed, $hash) = explode('|', $token);

        if (false === is_numeric($seed)) {
            return false;
        }

        return $hash === hash('sha256', Session::getSession()->getId() . $seed);
    }

    /**
     * Generate a new (seed, token) pair
     *
     * @return  string
     */
    protected function generateCsrfToken()
    {
        $seed = mt_rand();
        $hash = hash('sha256', Session::getSession()->getId() . $seed);
        return sprintf('%s|%s', $seed, $hash);
    }
}