Как создать безопасный токен 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, которая не загружена и не использует + или / символы. Вместо - и _ символы используются на своих местах. 
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;
}
