Files
UNIT3D/app/Helpers/HiddenCaptcha.php
2024-05-23 13:58:29 +00:00

120 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Helpers;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
use Exception;
class HiddenCaptcha
{
/**
* Set the hidden captcha tags to put in your form.
*/
public static function render(string $mustBeEmptyField = '_username'): string
{
$ts = time();
$random = Str::random(16);
// Generate the token
$token = [
'timestamp' => $ts,
'session_id' => session()->getId(),
'ip' => request()->ip(),
'user_agent' => request()->header('User-Agent'),
'random_field_name' => $random,
'must_be_empty' => $mustBeEmptyField,
];
// Encrypt the token
$token = Crypt::encrypt(serialize($token));
return (string) view('partials.captcha', ['mustBeEmptyField' => $mustBeEmptyField, 'ts' => $ts, 'random' => $random, 'token' => $token]);
}
/**
* Check the hidden captcha values.
*/
public static function check(Validator $validator, int $minLimit = 0, int $maxLimit = 1_200): bool
{
$formData = $validator->getData();
// Check post values
if (!isset($formData['_captcha']) || !($token = self::getToken($formData['_captcha'])) || !\is_array($token)) {
return false;
}
// Hidden "must be empty" field check
if (!\array_key_exists($token['must_be_empty'], $formData) || !empty($formData[$token['must_be_empty']])) {
return false;
}
// Check time limits
$now = time();
if ($now - $token['timestamp'] < $minLimit || $now - $token['timestamp'] > $maxLimit) {
return false;
}
// Check the random posted field
if (empty($formData[$token['random_field_name']])) {
return false;
}
// Check if the random field value is similar to the token value
$randomField = $formData[$token['random_field_name']];
return ctype_digit((string) $randomField) && $token['timestamp'] == $randomField;
}
/**
* Get and check the token values.
*
* @return bool|array<string, mixed>
*/
private static function getToken(string $captcha): bool|array
{
// Get the token values
try {
$token = Crypt::decrypt($captcha);
} catch (Exception) {
return false;
}
$token = @unserialize($token);
// Token is null or unserializable
if (!$token || !\is_array($token)) {
return false;
}
// Check token values
if (empty($token['session_id']) ||
empty($token['ip']) ||
empty($token['user_agent']) ||
$token['session_id'] !== session()->getId() ||
$token['ip'] !== request()->ip() ||
$token['user_agent'] !== request()->header('User-Agent')
) {
return false;
}
return $token;
}
}