神秘的 Google 两步验证 - 调试中






4.97/5 (11投票s)
获取 OTP 代码的步骤,以及为 Web 应用程序添加新的安全级别。
介绍
您是否已为您的 Google 帐户启用了两步验证?如果还没有,我强烈建议您这样做。您知道 Google Authenticator 生成的代码的本质吗?这里没有神话 - 这只是 RFC6238 的实现。更重要的是 - 您可以非常轻松地为您的应用程序添加新的安全级别,而无需使用一些庞大的安全库。
我将在本文中使用 PHP - 这意味着服务器端可以使用此代码来验证客户端代码。但没有什么能阻止您在 JavaScript 中实现 OTP 生成算法。
背景
两步验证大大降低了您的 Google 帐户中的个人信息被他人窃取的几率。为什么?因为黑客不仅需要获取您的密码和用户名,还需要获取您用于生成六位数组合的个人密钥。
这个组合是如何生成的?让我们一步步来看。
假设 base32 中的秘密代码是 GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ(这实际上是 base32 编码的密钥 12345678901234567890。
为什么使用 base32 而不是 base64?我的猜测考虑了以下几点:
- 生成的字符集全为大写(通常表示为大写),这在使用不区分大小写的文件系统、口头交流或人类记忆时通常很有用。
- 选择此字母表是为了避免相似但不同的符号对,因此字符串可以准确地手动转录。(例如,符号集省略了 1、8 和零的符号,因为它们可能与字母 'I'、'B' 和 'O' 混淆。)
- 结果可以包含在 URL 中而无需对任何字符进行编码。
换句话说,编码后的消息比 base 64 更容易记住。
代码的变异性是 **时间**(更准确地说,是 30 秒的间隔)。考虑到并非所有设备都使用 NTP 进行同步,我们可能需要检查 3-5 个连续的代码以确保输入了正确的代码。您的解决方案越安全,您可能想要检查的 30 秒间隔就越少。
我们取当前的 Unix 时间戳:
UnixTimeStamp (time()/30): 44376117.366667
并计算基于 HMAC 的 HOTP - 一次性密码(http://en.wikipedia.org/wiki/HOTP)。
我们需要什么来计算 6 位数代码?取上述值的截断 - **44376117** 并转换为十六进制 **2a52035**
打包成字节字符串:
5(35)
(20)
¥(a5)
<(2)
(0)
(0)
(0)
(0)
如果十六进制字符串少于 16 个字符,则在左侧用 **0** 字符进行填充。
十进制表示:
0 0 0 0 2 165 32 53
现在计算 sha1 HMAC(基于消息的认证码)(http://en.wikipedia.org/wiki/HMAС):
hash_hmac ('sha1', '...<¥ 5...', 12345678901234567890) Result: af2b88048dc8979b528af4e37085061d88aaaaa5
hash_hmac
是一个常用的加密函数,您可以在任何加密库或工具包中找到它。
让我们将其转换为 6 位数序列
步骤 A:转换为十六进制数组
Array ( [0] => af [1] => 2b [2] => 88 [3] => 04 [4] => 8d [5] => c8 [6] =>
97 [7] => 9b [8] => 52 [9] => 8a [10] => f4 [11] => e3 [12] => 70 [13] => 85 [14] =>
06 [15] => 1d [16] => 88 [17] => aa [18] => aa [19] => a5 )
步骤 B 将数组中的每个十六进制转换为其十进制形式
Array ( [0] => 175 [1] => 43 [2] => 136 [3] => 4 [4] => 141 [5] => 200 [6] =>
151 [7] => 155 [8] => 82 [9] => 138 [10] => 244 [11] => 227 [12] => 112 [13] =>
133 [14] => 6 [15] => 29 [16] => 136 [17] => 170 [18] => 170 [19] => 165 )
步骤 C 取第 19 个数组元素(在本例中为 165)
并执行按位运算符 **&** 和掩码 **0xf** - 在当前示例中我们得到 5。
(hmac_result[offset+0] & 0x7f) << 24 = 200& 0x7f) <<
24 = 11001000&100100111<< 24 = 1001000 << 24 =1001000000000000000000000000000=1207959552
(hmac_result[offset+1] & 0xff) << 16 = 151& 0xff) << 16 = 10010111&1001010101<<
16=10111 << 16 = 100101110000000000000000
(hmac_result[offset+2] & 0xff) << 8 = 155& 0xff) << 8 = 10011011&1001010101<< 8=10111
<< 16 = 1001101100000000
(hmac_result[offset+3] & 0xff) = 82& 0xff) = 1010010&1001010101=10111 << 16 =1010010
1001000000000000000000000000000 | 100101110000000000000000 | 1001101100000000 | 1010010 =
1001000100101111001101101010010 = 1217895250
步骤 D:取结果
**1217895250** 并获取其除以 **10** 的幂(对于 **6**,它是 **1000000**)的模。
我们来计算 - 我们将得到 **1217.89525**,因此模是 - **895250**。
我们的算法完成了:这就是 Google Authenticator 应用程序生成的特定结果:895250.
代码
让我们使用 PHP 来实现上述算法。
所需库:为了简化开发并避免重复造轮子,尝试查找是否有人已经实现它总是很有用的。对于 PHP,我采用了
- Bryan Ruiz 的 PHP Base32 实现。
- PHP HMAC hash 实现来自 https://php.ac.cn/manual/ru/function.hash-hmac.php 的社区反馈。
结果是 RFC6238 的概念验证实现:rfc6238.php,其中包含助手类 TokenAuth6238
和一些有用的函数。
生成密钥
密钥用于为您的应用程序和生成代码的设备提供验证用户身份的基础。密钥很重要,应通过安全通道传输。如果攻击者能够访问密钥,他们就可以生成验证码并绕过安全程序。
secret = Base32::encode("yourrandomsecretkey")
Google Authenticator
Google 提供 Android 和 iPhone 应用程序,用于为用户生成验证码。
安装应用程序并通过输入代码来创建新帐户。随意命名您的帐户,并输入上一步生成的密钥。选择一个基于时间的令牌。
现在您可以在智能手机上看到一个 6 个字符的密码,用于验证用户身份。
验证完整性
现在我们有了密钥,并且智能手机正在生成验证码,让我们尝试验证它。
<?php require_once("rfc6238.php");
$secretkey = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'; //your secret code
$currentcode = '571427'; //code to validate, for example received from device
if (TokenAuth6238::verify($secretkey,$currentcode))
{
echo "Code is valid\n";
}
else
{
echo "Invalid code\n";
}
生成代码
您也可以使用该库自行生成验证码。
print TokenAuth6238::getTokenCodeDebug($secretkey,0);
为 GOOGLE Authenticator 生成 QRCode
您还可以生成可用于移动设备配置身份验证程序的图像。
print sprintf('<img src="%s"/>',TokenAuth6238::getBarCodeUrl('','',$secretkey));
当您运行这样的脚本并输入正确的密钥和正确的验证码时,它将在标准输出上显示“Code is valid”或“Invalid code”。
关注点
通过这些简单的步骤,您可以为您的应用程序的身份验证过程添加额外的验证层,从而为您的用户提供更高的安全性。
上面提到的 OTP 助手类可以在 GitHub 上 Fork:https://github.com/Voronenko/PHPOTP。