如何使用 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 示例或指出我的代码中的任何明显错误吗?
Google's STokenUtils.java example uses com.google.common.io.BaseEncoding.base64url()
(see BaseEncoding
),其编码分别使用'-'和'_'代替'+'和'/'。
PHP 的 base64_encode
不会进行这些替换。请参阅 https://gist.github.com/nathggns/6652997 中的 base64url_encode
,但您会看到它只是将“+”更改为“-”,将“/”更改为“_”,并修剪尾随的“=”。
您可能还有其他问题,但我刚刚在 Java 版本中使用自制的 Base64 编码器解决了同样的问题 (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
您的代码中存在许多问题。首先,当实现需要 SHA1 散列的 前 16 个字节 时,您的 $secretKey
值被计算为填充的 SHA1 散列。
$secretKey = substr(hash('sha1', $secretKey, true), 0, 16);
其次,您正在尝试对密钥执行 base64 解码,这在此处无效。 mcrypt_encrypt()
的第二个参数应该是 $sKey
,而不是 base64_decode($sKey)
.
最后,如 x77686d 的回答中所述,您应该使用 "URL-safe" base64。这是 base64 的变体,未填充且不使用 +
或 /
字符。相反,-
和 _
字符用于它们的位置。
ReCaptcha 的安全令牌 老实说有点麻烦。它们是不安全的,并且算法没有记录。我和你处于同样的位置,需要一个实施,所以我 wrote one and published it on Packagist as "slushie/recaptcha-secure-token"。我建议使用它 and/or 贡献,即使只是因为缺少该算法的替代实现。
试试这个:
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;
}
我正在尝试为 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 示例或指出我的代码中的任何明显错误吗?
Google's STokenUtils.java example uses com.google.common.io.BaseEncoding.base64url()
(see BaseEncoding
),其编码分别使用'-'和'_'代替'+'和'/'。
PHP 的 base64_encode
不会进行这些替换。请参阅 https://gist.github.com/nathggns/6652997 中的 base64url_encode
,但您会看到它只是将“+”更改为“-”,将“/”更改为“_”,并修剪尾随的“=”。
您可能还有其他问题,但我刚刚在 Java 版本中使用自制的 Base64 编码器解决了同样的问题 (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
您的代码中存在许多问题。首先,当实现需要 SHA1 散列的 前 16 个字节 时,您的 $secretKey
值被计算为填充的 SHA1 散列。
$secretKey = substr(hash('sha1', $secretKey, true), 0, 16);
其次,您正在尝试对密钥执行 base64 解码,这在此处无效。 mcrypt_encrypt()
的第二个参数应该是 $sKey
,而不是 base64_decode($sKey)
.
最后,如 x77686d 的回答中所述,您应该使用 "URL-safe" base64。这是 base64 的变体,未填充且不使用 +
或 /
字符。相反,-
和 _
字符用于它们的位置。
ReCaptcha 的安全令牌 老实说有点麻烦。它们是不安全的,并且算法没有记录。我和你处于同样的位置,需要一个实施,所以我 wrote one and published it on Packagist as "slushie/recaptcha-secure-token"。我建议使用它 and/or 贡献,即使只是因为缺少该算法的替代实现。
试试这个:
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;
}