use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
class HiddenCaptcha
{
public static function render($mustBeEmptyField = '_username')
{
$ts = \time();
$random = Str::random(16);
$token = [
'timestamp' => $ts,
'session_id' => \session()->getId(),
'ip' => \request()->ip(),
'user_agent' => \request()->header('User-Agent'),
'random_field_name' => $random,
'must_be_empty' => $mustBeEmptyField,
];
$token = Crypt::encrypt(\serialize($token));
return (string) \view('partials.captcha', ['mustBeEmptyField' => $mustBeEmptyField, 'ts' => $ts, 'random' => $random, 'token' => $token]);
}
public static function check(Validator $validator, $minLimit = 0, $maxLimit = 1_200)
{
$formData = $validator->getData();
if (! isset($formData['_captcha']) || ! ($token = self::getToken($formData['_captcha']))) {
return false;
}
if (! \array_key_exists($token['must_be_empty'], $formData) || ! empty($formData[$token['must_be_empty']])) {
return false;
}
$now = \time();
if ($now - $token['timestamp'] < $minLimit || $now - $token['timestamp'] > $maxLimit) {
return false;
}
if (empty($formData[$token['random_field_name']])) {
return false;
}
$randomField = $formData[$token['random_field_name']];
return \ctype_digit($randomField) && $token['timestamp'] == $randomField;
}
private static function getToken($captcha): string | bool | array
{
try {
$token = Crypt::decrypt($captcha);
} catch (\Exception) {
return false;
}
$token = @\unserialize($token);
if (! $token || ! \is_array($token) || empty($token)) {
return false;
}
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;
}
}