index/src/Security/CSRFPToken.php

102 lines
2.6 KiB
PHP

<?php
// CSRFPToken.php
// Created: 2021-06-11
// Updated: 2022-02-27
namespace Index\Security;
use ErrorException;
use InvalidArgumentException;
use Stringable;
use Index\Serialisation\Serialiser;
/**
* Represents a CSRF prevention token.
*/
class CSRFPToken implements Stringable {
private int $timestamp;
private int $tolerance;
private string $hash;
/**
* Construct a CSRFPToken instance.
*
* @param int $timestamp Timestamp at which the token was generated.
* @param int $tolerance Period for how long this token is valid.
* @param string $hash A HMAC hash to prevent tampering.
* @return CSRFPToken A new instance of CSRFPToken.
*/
public function __construct(
int $timestamp,
int $tolerance,
string $hash
) {
$this->timestamp = $timestamp;
$this->tolerance = $tolerance;
$this->hash = $hash;
}
/**
* Gets the timestamp value of this token.
*
* @return int Timestamp for this token.
*/
public function getTimestamp(): int {
return $this->timestamp;
}
/**
* Gets the tolerance value of this token.
*
* @return int Tolerance for this token.
*/
public function getTolerance(): int {
return $this->tolerance;
}
/**
* Gets the hash of this token.
*
* @return string Hash for this token.
*/
public function getHash(): string {
return $this->hash;
}
/**
* Encodes the CSRFPToken instance as a token string.
*
* @return string CSRF prevention token string.
*/
public function encode(): string {
return Serialiser::uriBase64()->serialise(pack('Vv', $this->timestamp, $this->tolerance) . $this->hash);
}
/**
* Decodes a token string to a CSRFPToken instance.
*
* If an invalid token is provided, no exception will be thrown.
*
* @param string $token Input token string.
* @return CSRFPToken Instance representing the provided token.
*/
public static function decode(string $token): CSRFPToken {
$token = Serialiser::uriBase64()->deserialise($token);
try {
$decode = unpack('Vtimestamp/vtolerance', $token);
} catch(ErrorException $ex) {
$decode = [
'timestamp' => -1,
'tolerance' => 0,
];
}
// arbitrary length
$hash = substr($token, 6, 128);
return new CSRFPToken($decode['timestamp'], $decode['tolerance'], $hash);
}
public function __toString(): string {
return $this->encode();
}
}