如何用PHP生成一个Google ReCaptcha V2安全令牌


How to generate a Google ReCaptcha V2 secure token with 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示例或指出任何明显的错误在我的代码?

您的代码中有许多问题。首先,当实现需要SHA1散列的前16个字节时,您的$secretKey值被计算为填充的SHA1散列。

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

第二,您正在尝试对密钥执行base64解码,这在这里是无效的。mcrypt_encrypt()的第二个参数应该是$sKey,而不是base64_decode($sKey)

最后,正如在x77686d的回答中所解释的,您应该使用"url安全"的base64。这是base64的一种变体,没有填充,不使用+/字符。相反,-_字符被用于它们的位置。

老实说,ReCaptcha的安全令牌有点麻烦。它们是不安全的,而且算法没有记录。我和你处于同样的位置,需要一个实现,所以我写了一个,并在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。

您可能有其他其他问题,但我刚刚通过这样做,在使用本地Base64编码器的Java版本修复了同样的问题(ERROR: Invalid stoken):

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

作为一个固定目标,尝试加密和编码这个对象:

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

使用这个秘密密钥

6Lc0MgoTAAAAAAXFM388zn66iPtjOdQgREfZAgqZ

,看看你是否得到这个:(注意中间的下划线!)

XlPyYFtyfzmsf5rnRIzyuZ4MZo5GoCSxNcI_wAeOqb18zCxhSM5cYxU8fFerrdcC

顺便说一句,简单地使用这个安全令牌应该会产生一个不同的错误:ERROR: Stoken expired。让下划线变成斜杠,你就回到了ERROR: Invalid stoken !

参见base64url 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;
}