Google身份验证器代码与服务器生成的代码不匹配

波诺

背景


我目前正在使用两因素身份验证系统,在该系统中,用户可以使用智能手机进行身份验证。用户必须先对其进行验证,然后才能使用他们的设备。为此,他们需要扫描我给他们的QR码,然后输入随后显示的代码。

问题


QR码的扫描工作正常,并且可以通过Google Authenticator应用正确读取。但是,生成的代码与我在服务器上生成的代码不匹配。

我尝试了什么


我尝试了几件事,希望能找到我的问题。

  1. 我尝试在Google Authenticator应用程序中直接插入默认secret:'thiswasmysecretkeyused'secretbase64.b32encode()编码版本'ORUGS43XMFZW26LTMVRXEZLUNNSXS5LTMVSA====',但是它们都生成与服务器不同的代码。

  2. 我读到,====从密钥开始的尾随可能会导致它不起作用,因此我也尝试添加不带这些字符的密钥。仍然没有良好的结果(它们生成相同的代码)

  3. 我尝试使用另一种算法生成TOTP代码,因为在极少数情况下,我正在使用的算法(django-otp)不正确。所用的不同算法,我从拍摄这个答案。当使用相同的密钥时,两种算法都生成相同的代码。

  4. 我检查了系统时间。我看到操作系统显示的15:03就像我的智能手机一样。在将时间与python一起转储之后time.time()datetime.datetime.now()我看到返回的时间比操作系统时间晚了一个小时;显示14:03我尝试3600在用于代码生成的时间戳中增加几秒钟,但无济于事。

  5. 我尝试了其他几件事,但还不太记得它们是什么。

  6. 我在Google Authenticator中查找了接受密钥的代码,并验证了它是否需要base32字符串。据我所知,我对密钥的编码是正确的。从代码(EnterKeyActivity.java,第78行)中:

    验证输入字段包含有效的base32字符串


生成密钥;

def generate_shared_key(self):
    # create hash etc.
    return base64.b32encode(hasher.hexdigest())

生成QR码;

key = authenticator.generate_shared_key()
qrcode = pyqrcode.create('otpauth://totp/someurl.nl?secret=' + key)

生成TOTP代码;

def generate_code(self, drift_steps=0, creation_interval=30, digits=6, t0=0):
    code = str(totp(self.generate_shared_key(), creation_interval, timestamp, digits, drift_steps))
    return code.zfill(digits)

如果您还需要其他代码,例如django-otp实际的totp生成代码,请告诉我。

失误


没有错误。

腰腿


我的直觉是,在生成密钥或将密钥传递给Google Authenticator的某个地方,我一定会出错。由于即使手动将密钥放入Google Authenticator也无法生成正确的代码。保存密钥后,Google Authenticator会对密钥进行更多操作吗(例如添加用户)?

我还注意到,在我使用的其他算法中,那里的秘密首先被解码。

key = base64.b32decode(secret, True) 

我的原始密钥(SHA512哈希)不正确吗?我应该还是不应该使用base64.b32encode()如果我尝试扫描生成的QR码而不对哈希值进行编码,则Google Authenticator表示无法将其识别为(有效)密钥。

波诺

好吧,在研究了Google Authenticator的代码之后,我终于发现我做错了。

密钥编码

显而易见,Google Authenticator确实希望将base32编码后的字符串作为秘密。因此,无论您是手动输入还是通过QR码输入,base32将其提供给Google Authenticator时,都必须确保您的机密是经过编码的字符串。

EnterKeyActivity

/*
 * Verify that the input field contains a valid base32 string,
 * and meets minimum key requirements.
 */
private boolean validateKeyAndUpdateStatus(boolean submitting) {
    //...
}

储存

Google身份验证器会将您提供的密钥原样存储在数据库中。因此,这意味着它将base32您的机密字符串直接存储在数据库中。

EnterKeyActivity

private String getEnteredKey() {
    String enteredKey = mKeyEntryField.getText().toString();
    return enteredKey.replace('1', 'I').replace('0', 'O');
}

protected void onRightButtonPressed() {
    //...
    if (validateKeyAndUpdateStatus(true)) {
        AuthenticatorActivity.saveSecret(this, mAccountName.getText().toString(), getEnteredKey(), null, mode, AccountDb.DEFAULT_HOTP_COUNTER);
        exitWizard();
    }
    //...
}

来自AuthenticatorActivity

static boolean saveSecret(Context context, String user, String secret, String originalUser, OtpType type, Integer counter) {
    //...
    if (secret != null) {
          AccountDb accountDb = DependencyInjector.getAccountDb();
          accountDb.update(user, secret, originalUser, type, counter);

          //...
    }
}

恢复

当Google Authenticator从数据库中检索到机密时,它将对base32字符串进行解码,以便可以使用真正的机密。

OtpProvider

private String computePin(String secret, long otp_state, byte[] challenge) throws OtpSourceException {
    //...

    try {
        Signer signer = AccountDb.getSigningOracle(secret);
        //...
    }
}

AccountDb

static Signer getSigningOracle(String secret) {
    try {
        byte[] keyBytes = decodeKey(secret);
        //...
    }
}

private static byte[] decodeKey(String secret) throws DecodingException {
  return Base32String.decode(secret);
}

错误

我的错误是,在服务器端,我使用了base32编码密钥来生成TOTP代码,因为我认为Google Authenticator也使用了该密钥。事后看来,这当然是很合逻辑的,但是我找不到太多有关此的信息。希望这会在将来帮助更多的人。

TL; DR

确保您传递给Google Authenticator的秘密/密钥是base32编码字符串。确保在服务器端您没有使用base32编码的字符串,而是解码的字符串。在Python中,您可以按以下方式对秘密/密钥进行编码和解码:

import base64

base64.b32encode(self.key)
base64.b32decode(self.key)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Firestore服务器身份验证:重叠文档

.NET Core身份服务器4身份验证VS身份身份验证

后端服务器的Google身份验证所需的范围

跨客户端Google OAuth:在iOS上获取身份验证代码并在服务器上访问令牌

如何在节点JS的服务器端验证Google身份验证令牌?

使用Google Apps脚本API可执行服务器到服务器的身份验证

身份服务器4 Windows身份验证

在无服务器环境中存储和重新生成身份验证令牌

如何使用passport-2fa-totp验证Google身份验证器代码?

Keycloak服务器身份验证

使用OAuth 2.0和Google API进行服务器到服务器身份验证的示例

Google Calendar API v3-不创建事件(服务器到服务器身份验证)

从Flask服务器生成HTML代码

使用密钥的扭曲的ssh服务器身份验证

Web服务器上的身份验证

保存打印服务器身份验证凭据

为什么客户端Websocket关闭代码与服务器代码不匹配?

使用Google身份验证在SSH服务器上进行两因素身份验证

服务器提供的Mocha Testing身份验证API

Google登录后端服务器身份验证

客户端-服务器应用程序中的Google Play服务身份验证

Linux服务器未启用MongoDB身份验证

身份服务器 4 身份验证

如何对 Google Drive Api 进行服务器到服务器身份验证?

EEException:服务器返回 HTTP 代码:404 — 使用 Google App Engine 和 Google Earth Engine 测试应用程序并具有经过身份验证的访问

使用 Ajax 和 Postman 代码进行身份验证,但服务器无法识别变量。

服务器级别的 MongoDB 身份验证

FHIR:多服务器身份验证

Google API 身份验证服务器端