Google Apple 设备上的身份验证器,某些秘密无效
Google Authenticator on Apple devices, certain secrets are not valid
我们使用 Google Authenticator 将双因素身份验证合并到我们的一个应用程序中。在 QA 中,出现了一些非常奇怪的事情。虽然我设法修复了它,但我不太确定为什么修复有效。
对于我们的共享机密,我们在用户开始 TFA 设置时为其分配了一个 GUID。 GUID 得到 base-32 编码,并放入 URL 中,该 URL 被转换为 QR 码并由用户使用他们的 phone:
扫描
otpauth://totp/myapp_user?secret=g5swmnddhbtggllbgi3dsljumi3tallbmuytgljtg5sdgnbxmy2dgyjwmy======
并且对于我们尝试过的所有非 ios 机器都运行良好。仅在 ios 上,它在大多数时候尝试扫描条形码时会抛出一个非常奇怪的错误:
Invalid barcode
The barcode '[same as above]' is not a valid authentication token barcode.
它满足 Google/RFC 4226 的最低机密要求(128 位),正确地进行了 Base32 编码,等等...为什么会失败?出现此消息的典型原因是 url 中的空格——但 none.
如果我在 guid 的开头添加一个小种子,一切正常:
otpauth://totp/myapp_user?secret=nfygq33omvzxky3lom3ggmzyha2tgnjnmu4gezbngqzdgyrnhbtdqzrnmeywimrwmjsgknzymi3a
本质上区别在于:
secret = enc.Encode32(Encoding.ASCII.GetBytes("iphonesucks" + Guid.NewGuid().ToString())); // Works
secret = enc.Encode(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString())); // Fails
newAuthUrl = string.Format("otpauth://totp/myapp_user?secret={0}", secret);
关于为什么这可能有效,我有两个疯狂的理论:
ios端口需要128位以上。我的 commentary/seed 足以使其超过该限制,无论发生什么......除了我实际上给它超过 128 位因为它是一个 guid-as-string.
Base32 解码后,ios 应用将秘密字符串识别为 guid,并用它做其他事情。
我讨厌修复一个 bug 却不知道修复的原因。任何人都可以解释一下吗? 也欢迎关于这个主题的其他阴谋论。
重要的不仅仅是密钥长度
示例 URI 应如下所示:
otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=示例
在你的情况下,URI 中没有 "issuer" 或 "accountname",两者都是 recommended。
PS。仅供参考,Google Authenticatior 也不支持很长的密钥。
我遇到了与上述相同的问题。事实证明,Google Authenticator 不喜欢 IPhone 应用程序中的 = 标志,但不会在 Android.
中抱怨
在我的例子中,我将编码为 base32 之前的字符串长度从 8 个字符增加到 10 个字符。这删除了字符串末尾的三个 === 。我在网上找到了为什么 = 符号出现在 base32 编码的字符串中:
在你上面的例子中,当你添加盐时,同样的情况发生了。您粘贴的第二个秘密末尾没有=。
希望对您有所帮助。
5 年后...我们只能让 Google 验证器接受 16 位长 的密钥。更短和更长的键都会失败 "Invalid barcode. The barcode ... is not a valid authentication token barcode." 浪费了很多时间来解决这个问题,希望它能有所帮助。
我也有这个问题。
这个link有效
otpauth://totp/xxx.yyy:usertest009%40xxx.yyy?secret=CC5FCZNWTKNTOVN6&period=30&digits=6&algorithm=SHA1&issuer=xxx.yyy
但是使用 diff 密钥会使它无效
otpauth://totp/xxx.yyy:usertest009%40xxx.yyy?secret=PBUPKS3SLJAP9V2T&period=30&digits=6&algorithm=SHA1&issuer=xxx.yyy
我发现了问题
密钥必须是 base32 字符串。
我使用的是 NodeJS,所以我使用 speakeasy 来创建秘密字符串 base32。现在可以使用了。
对于上面的答案,我想不出如何让 base32 对我有用(我只是在 PHP 中做到了)。我有行之有效的代码,也有行不通的代码,它们看起来同样随机。
我正在根据升档后的 md5 的前 16 个字符生成一个秘密。有时他们会工作,但大多数情况下不会。
经过很长时间的测试,我发现任何字符串中包含 0 或 1 的字符串都会失败。所有其他人似乎都很好。也许我错了(请随意检验你自己的假设),但这对我有用。
$秘密['1'] = 'XVQ2UIGO75XRUKJO'; //有效
$秘密['1'] = 'XVQ4UIG07SXRUKJO'; //失败
$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败
$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败
$秘密['1'] = 'ABCDEFGHIJKLMNOR'; //有效
$secret['1'] = '1234567890123456'; //失败
$秘密['1'] = '1A3B5C7D9E1F3G5H'; //失败
$秘密['1'] = 'AB1DE2GH3JK4MN5R'; //失败
$秘密['1'] = 'ABC1EFG2IJK3MNO4'; //失败
$秘密['1'] = 'ABCDEFG2IJK3MNO4'; //有效
$秘密['1'] = 'ABCD123456789012'; //失败
$秘密['1'] = 'ABCDE23456789012'; //失败
$秘密['1'] = 'ABCDEF3456789012'; //失败
$秘密['1'] = 'ABCDEFG456789012'; //失败
$秘密['1'] = 'ABCDEFGH56789012'; //失败
$秘密['1'] = 'ABCDEFGHIJBKCLMD'; //有效
$秘密['1'] = 'AAAAAAAAAAAAAAAA'; //有效
$secret['1'] = '1111111111111111'; //失败
$秘密['1'] = 'A1A1A1A1A1A1A1A1'; //失败
$秘密['1'] = 'AAA1AAA1AAA1AAAA'; //失败
$秘密['1'] = 'AAAAAAA1AAAAAAAA'; //失败
$秘密['1'] = 'AAAA1AAAAAAAAAAA'; //失败
$秘密['1'] = 'AAA1AAAAAAAAAAAA'; //失败
$秘密['1'] = 'AAAAAAAAAAAAAAAA'; //仍然有效
$秘密['1'] = 'AAAAAAA5AAAAAAAA'; //有效
$秘密['1'] = 'AAAAAAA1AAAAAAAA'; //失败
$秘密['1'] = 'A5A5A5A5A5A5A5A5'; //有效
$秘密['1'] = 'A2A2A2A2A2A2A2A2'; //有效
$秘密['1'] = 'A0A2A2A2A2A2A2A2'; //失败
$秘密['1'] = 'A3A2A2A2A2A2A2A2'; //假设任何 1 或 0 都会导致失败
$秘密['1'] = 'XVQ2UIG17SXRUKJO'; //失败 1
$秘密['1'] = 'XVQ2UIG27SXRUKJO'; //没有 1
也能工作
$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败0
$秘密['1'] = 'XVQ2UIG37SXRUKJO'; //适用于 3
$秘密['1'] = 'XV02UIG37SXRUKJO'; //失败添加了一个0
我们使用 Google Authenticator 将双因素身份验证合并到我们的一个应用程序中。在 QA 中,出现了一些非常奇怪的事情。虽然我设法修复了它,但我不太确定为什么修复有效。
对于我们的共享机密,我们在用户开始 TFA 设置时为其分配了一个 GUID。 GUID 得到 base-32 编码,并放入 URL 中,该 URL 被转换为 QR 码并由用户使用他们的 phone:
扫描 otpauth://totp/myapp_user?secret=g5swmnddhbtggllbgi3dsljumi3tallbmuytgljtg5sdgnbxmy2dgyjwmy======
并且对于我们尝试过的所有非 ios 机器都运行良好。仅在 ios 上,它在大多数时候尝试扫描条形码时会抛出一个非常奇怪的错误:
Invalid barcode
The barcode '[same as above]' is not a valid authentication token barcode.
它满足 Google/RFC 4226 的最低机密要求(128 位),正确地进行了 Base32 编码,等等...为什么会失败?出现此消息的典型原因是 url 中的空格——但 none.
如果我在 guid 的开头添加一个小种子,一切正常:
otpauth://totp/myapp_user?secret=nfygq33omvzxky3lom3ggmzyha2tgnjnmu4gezbngqzdgyrnhbtdqzrnmeywimrwmjsgknzymi3a
本质上区别在于:
secret = enc.Encode32(Encoding.ASCII.GetBytes("iphonesucks" + Guid.NewGuid().ToString())); // Works
secret = enc.Encode(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString())); // Fails
newAuthUrl = string.Format("otpauth://totp/myapp_user?secret={0}", secret);
关于为什么这可能有效,我有两个疯狂的理论:
ios端口需要128位以上。我的 commentary/seed 足以使其超过该限制,无论发生什么......除了我实际上给它超过 128 位因为它是一个 guid-as-string.
Base32 解码后,ios 应用将秘密字符串识别为 guid,并用它做其他事情。
我讨厌修复一个 bug 却不知道修复的原因。任何人都可以解释一下吗? 也欢迎关于这个主题的其他阴谋论。
重要的不仅仅是密钥长度
示例 URI 应如下所示: otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=示例
在你的情况下,URI 中没有 "issuer" 或 "accountname",两者都是 recommended。
PS。仅供参考,Google Authenticatior 也不支持很长的密钥。
我遇到了与上述相同的问题。事实证明,Google Authenticator 不喜欢 IPhone 应用程序中的 = 标志,但不会在 Android.
中抱怨在我的例子中,我将编码为 base32 之前的字符串长度从 8 个字符增加到 10 个字符。这删除了字符串末尾的三个 === 。我在网上找到了为什么 = 符号出现在 base32 编码的字符串中:
在你上面的例子中,当你添加盐时,同样的情况发生了。您粘贴的第二个秘密末尾没有=。
希望对您有所帮助。
5 年后...我们只能让 Google 验证器接受 16 位长 的密钥。更短和更长的键都会失败 "Invalid barcode. The barcode ... is not a valid authentication token barcode." 浪费了很多时间来解决这个问题,希望它能有所帮助。
我也有这个问题。
这个link有效
otpauth://totp/xxx.yyy:usertest009%40xxx.yyy?secret=CC5FCZNWTKNTOVN6&period=30&digits=6&algorithm=SHA1&issuer=xxx.yyy
但是使用 diff 密钥会使它无效
otpauth://totp/xxx.yyy:usertest009%40xxx.yyy?secret=PBUPKS3SLJAP9V2T&period=30&digits=6&algorithm=SHA1&issuer=xxx.yyy
我发现了问题 密钥必须是 base32 字符串。 我使用的是 NodeJS,所以我使用 speakeasy 来创建秘密字符串 base32。现在可以使用了。
对于上面的答案,我想不出如何让 base32 对我有用(我只是在 PHP 中做到了)。我有行之有效的代码,也有行不通的代码,它们看起来同样随机。
我正在根据升档后的 md5 的前 16 个字符生成一个秘密。有时他们会工作,但大多数情况下不会。
经过很长时间的测试,我发现任何字符串中包含 0 或 1 的字符串都会失败。所有其他人似乎都很好。也许我错了(请随意检验你自己的假设),但这对我有用。
$秘密['1'] = 'XVQ2UIGO75XRUKJO'; //有效
$秘密['1'] = 'XVQ4UIG07SXRUKJO'; //失败
$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败
$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败
$秘密['1'] = 'ABCDEFGHIJKLMNOR'; //有效
$secret['1'] = '1234567890123456'; //失败
$秘密['1'] = '1A3B5C7D9E1F3G5H'; //失败
$秘密['1'] = 'AB1DE2GH3JK4MN5R'; //失败
$秘密['1'] = 'ABC1EFG2IJK3MNO4'; //失败
$秘密['1'] = 'ABCDEFG2IJK3MNO4'; //有效
$秘密['1'] = 'ABCD123456789012'; //失败
$秘密['1'] = 'ABCDE23456789012'; //失败
$秘密['1'] = 'ABCDEF3456789012'; //失败
$秘密['1'] = 'ABCDEFG456789012'; //失败
$秘密['1'] = 'ABCDEFGH56789012'; //失败
$秘密['1'] = 'ABCDEFGHIJBKCLMD'; //有效
$秘密['1'] = 'AAAAAAAAAAAAAAAA'; //有效
$secret['1'] = '1111111111111111'; //失败
$秘密['1'] = 'A1A1A1A1A1A1A1A1'; //失败
$秘密['1'] = 'AAA1AAA1AAA1AAAA'; //失败
$秘密['1'] = 'AAAAAAA1AAAAAAAA'; //失败
$秘密['1'] = 'AAAA1AAAAAAAAAAA'; //失败
$秘密['1'] = 'AAA1AAAAAAAAAAAA'; //失败
$秘密['1'] = 'AAAAAAAAAAAAAAAA'; //仍然有效
$秘密['1'] = 'AAAAAAA5AAAAAAAA'; //有效
$秘密['1'] = 'AAAAAAA1AAAAAAAA'; //失败
$秘密['1'] = 'A5A5A5A5A5A5A5A5'; //有效
$秘密['1'] = 'A2A2A2A2A2A2A2A2'; //有效
$秘密['1'] = 'A0A2A2A2A2A2A2A2'; //失败
$秘密['1'] = 'A3A2A2A2A2A2A2A2'; //假设任何 1 或 0 都会导致失败
$秘密['1'] = 'XVQ2UIG17SXRUKJO'; //失败 1
$秘密['1'] = 'XVQ2UIG27SXRUKJO'; //没有 1
也能工作$秘密['1'] = 'XVQ2UIG07SXRUKJO'; //失败0
$秘密['1'] = 'XVQ2UIG37SXRUKJO'; //适用于 3
$秘密['1'] = 'XV02UIG37SXRUKJO'; //失败添加了一个0