Как создать безопасный токен Google ReCaptcha V2 с PHP?

Я пытаюсь создать безопасный токен для ReCaptcha V2, как описано здесь: https://developers.google.com/recaptcha/docs/secure_token

к сожалению, мой сгенерированный stoken недействителен, и я не могу найти способ проверить, почему он не работает. Существует рабочий пример Java (STokenUtils.java), но я не могу перевести его на PHP.

public static function generateSecurityToken($secretKey){
    $stoken = array(
        'session_id' => session_id(),
        'ts_ms' => round(microtime(true)*1000)
    );
    $secretKey = self::pkcs5_pad(hash('sha1', $secretKey), 16);
    $stoken_json = json_encode($stoken);
    $stoken_crypt = self::encrypt(self::pkcs5_pad($stoken_json, 16), $secretKey);
    return $stoken_crypt;
}

public static function encrypt($sStr, $sKey) {
    return base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_128, 
            base64_decode($sKey),
            $sStr,
            MCRYPT_MODE_ECB
        )
    );
}

public static function pkcs5_pad ($text, $blocksize) { 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
}

может ли кто-нибудь предоставить рабочий пример PHP или указать на какие-либо очевидные ошибки в моем коде?

3 ответов


в вашем коде есть ряд проблем. Во-первых, ваш $secretKey значение вычисляется как дополненный хэш SHA1, когда реализация требует первые шестнадцать байт хэша SHA1.

$secretKey = substr(hash('sha1', $secretKey, true), 0, 16);

во-вторых, вы пытаетесь выполнить декодирование base64 секретного ключа, который здесь недействителен. Второй аргумент к mcrypt_encrypt() должно быть $sKey, а не base64_decode($sKey).

наконец, как объясняется в ответе x77686d, вы должны использовать " URL-безопасный" в base64. Это вариация base64, которая не загружена и не использует + или / символы. Вместо - и _ символы используются на своих местах.

рекапчи это!--18-->надежные маркеры немного больно, честно говоря. Они небезопасны, а алгоритм недокументирован. Я был в том же положении, что и вы, и нуждался в реализации, поэтому я писал и опубликовал его на Packagist как "slushie / recaptcha-secure-token". Я бы рекомендовал использовать его и / или внести свой вклад, хотя бы из-за отсутствия альтернативных реализаций этого алгоритма.

Google STokenUtils.пример java использует com.google.common.io.BaseEncoding.base64url() (см. BaseEncoding), и его кодировка использует " - " и " _ "вместо" + " и " / " соответственно.

PHP base64_encode не делает эти замены. См.https://gist.github.com/nathggns/6652997 для base64url_encode, но вы увидите, что он просто меняет " + "На" -", " / " На " _ "и обрезает трейлинг" = " s.

у вас могут быть другие проблемы, но я только что исправил это та же проблема (ERROR: Invalid stoken) в версии Java с использованием доморощенного кодировщика Base64, выполнив следующее:

encoded = encoded.replace('+','-').replace('/','_').replace("=","");

в качестве фиксированной цели попробуйте зашифровать и кодировать этот объект:

{"session_id":"1","ts_ms":1437712654577}

С этим секретным ключом

6Lc0MgoTAAAAAAXFM388zn66iPtjOdQgREfZAgqZ

и посмотрите, если вы получите это: (обратите внимание, что подчеркивание в середине!)

XlPyYFtyfzmsf5rnRIzyuZ4MZo5GoCSxNcI_wAeOqb18zCxhSM5cYxU8fFerrdcC

BTW, просто используя этот безопасный токен, как есть, должен генерировать другую ошибку:ERROR: Stoken expired. Сделайте это подчеркивание косой чертой, и вы вернетесь к ERROR: Invalid stoken!

см. также base64url on https://en.wikipedia.org/wiki/Base64


попробуйте это:

public static function generateSecurityToken($secretKey){
    $stoken = array(
        'session_id' => session_id(),
        'ts_ms' => round(microtime(true)*1000)
    );


    $stoken_json = json_encode($stoken);
    $stoken_json = str_replace('+', '-', $stoken_json);
    $stoken_json = str_replace('/', '_', $stoken_json);
    $stoken_json = str_replace('=', '', $stoken_json);

    $secretKey = pack('H*', substr(hash('sha1', $secretKey), 0, 32));

    $stoken_crypt = self::encrypt(self::pkcs5_pad($stoken_json, 16), $secretKey);
    return $stoken_crypt;
}

public static function encrypt($sStr, $sKey) {
    $json = base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_128, 
            $sKey,
            $sStr,
            MCRYPT_MODE_ECB
        )
    );
    $sStr = str_replace('+', '-', $json);
    $sStr = str_replace('/', '_', $sStr);
    $sStr = str_replace('=', '', $sStr);
    return $sStr;
}