Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
66.67% covered (warning)
66.67%
24 / 36
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Csrf
66.67% covered (warning)
66.67%
24 / 36
33.33% covered (danger)
33.33%
1 / 3
21.26
0.00% covered (danger)
0.00%
0 / 1
 __construct
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
6.20
 createNewToken
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 setValidator
55.56% covered (warning)
55.56%
10 / 18
0.00% covered (danger)
0.00%
0 / 1
13.62
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Form\Element\Input;
15
16/**
17 * Form CSRF element class
18 *
19 * @category   Pop
20 * @package    Pop\Form
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    4.0.0
25 */
26
27class Csrf extends Hidden
28{
29
30    /**
31     * Current token data
32     * @var array
33     */
34    protected array $token = [];
35
36    /**
37     * Constructor
38     *
39     * Instantiate the CSRF input form element
40     *
41     * @param  string  $name
42     * @param  ?string $value
43     * @param  int     $expire
44     * @param  ?string $indent
45     */
46    public function __construct(string $name, ?string $value = null, int $expire = 300, ?string $indent = null)
47    {
48        // Start a session.
49        if (session_id() == '') {
50            session_start();
51        }
52
53        // If token does not exist, create one
54        if (!isset($_SESSION['pop_csrf'])) {
55            $this->createNewToken($value, $expire);
56        // Else, retrieve existing token
57        } else {
58            $this->token = unserialize($_SESSION['pop_csrf']);
59
60            // Check to see if the token has expired
61            if ($this->token['expire'] > 0) {
62                if (($this->token['expire'] + $this->token['start']) < time()) {
63                    $this->createNewToken($value, $expire);
64                }
65            }
66        }
67
68        parent::__construct($name, $this->token['value'], $indent);
69        $this->setRequired(true);
70        $this->setValidator();
71    }
72
73    /**
74     * Set the token of the csrf form element
75     *
76     * @param  ?string $value
77     * @param  int     $expire
78     * @return Csrf
79     */
80    public function createNewToken(?string $value = null, int $expire = 300): Csrf
81    {
82        $this->token = [
83            'value'  => sha1(rand(10000, getrandmax()) . $value),
84            'expire' => (int)$expire,
85            'start'  => time()
86        ];
87        $_SESSION['pop_csrf'] = serialize($this->token);
88        return $this;
89    }
90
91    /**
92     * Set the validator
93     *
94     * @throws Exception
95     * @return void
96     */
97    protected function setValidator(): void
98    {
99        // Get query data
100        if (!isset($_SERVER['REQUEST_METHOD'])) {
101            throw new Exception('Error: The server request method is not set.');
102        }
103
104        $queryData = [];
105        switch ($_SERVER['REQUEST_METHOD']) {
106            case 'GET':
107                $queryData = $_GET;
108                break;
109
110            case 'POST':
111                $queryData = $_POST;
112                break;
113
114            default:
115                $input = fopen('php://input', 'r');
116                $qData = null;
117                while ($data = fread($input, 1024)) {
118                    $qData .= $data;
119                }
120
121                parse_str($qData, $queryData);
122        }
123
124        // If there is query data, set validator to check against the token value
125        if (count($queryData) > 0) {
126            $val = (isset($queryData[$this->name])) ? $queryData[$this->name] : '';
127            $this->addValidator(new \Pop\Validator\Equal($val, 'The security token does not match.'));
128        }
129    }
130
131}