版本更新后继续支持旧票

Keep supporting old tickets after version-update

大多数基本的票务平台都是这样工作的:有一个网站,用户可以在该网站上购买特定活动的门票。在他们的信息连同唯一 ID 或哈希值一起存储在数据库中以识别门票后,他们会收到一封带有二维码的电子邮件。进入活动时,二维码(包含唯一 ID/hash)会被扫描,后端会检查对应于该 ID 或哈希值的门票是否存在、是否有效且尚未使用。

在入口处扫描门票的保安会有一个专用但非常基本的应用程序:该应用程序扫描门票上二维码的内容(二维码仅包含唯一的 ID/hash票),并将唯一的 ID/hash 发送到后端。如果后端检查得出票证有效,则应用程序会向警卫显示绿色屏幕,否则显示红色屏幕。所以扫描应用程序非常 'dumb'、简单和基本。

我的问题是:如果后端逻辑发生变化怎么办?

示例:2017 年,我们编写了一个票务平台,并决定使用 MD5 哈希作为所有票的唯一标识符。所以数据库中的每张票都保存了它的信息(用户名、用户电子邮件、什么事件……)以及它自己唯一的 MD5 哈希。门票所有者通过邮件收到的二维码也只包含该 MD5 哈希。这样,当使用我们专用的扫描应用程序时,我们可以 link 在我们的数据库中使用正确数据的实体票。

但到了 2021 年,我们认为 MD5 不再足够安全,从现在开始,我们将使用 SHA256 哈希为门票提供唯一 ID。 因此,从现在开始,数据库中的 hash_id 列包含 SHA256 哈希,我们通过邮件发送给买家的所有 QR 码都包含与他们购买的机票匹配的 SHA256 哈希。

然后我们更改(伪代码):

if ($_POST['action_check_hash']) {
    if (md5_hash_is_valid_ticket($_POST['hash_on_ticket'])) {
        return GREEN_SCREEN;
    } else {
        return RED_SCREEN;
    }
}

if ($_POST['action_check_hash']) {
    if (sha256_hash_is_valid_ticket($_POST['hash_on_ticket'])) {
        return GREEN_SCREEN;
    } else {
        return RED_SCREEN;
    }
}

考虑到给定的解释,我们也可以只比较字符串,使问题无效。但是为了这个问题,让我们假设我们正在使用依赖于哈希算法的函数来检查是否存在具有给定哈希值的有效票证。

门票没有有效期。

所以现在昨天刚买票的用户 Foo 来到活动入口,保安扫描了 Foo 的包含 SHA256 哈希的二维码。 sha256_hash_is_valid_ticket($_POST['hash_on_ticket']) returns 绿屏,Foo 可以进入。

现在用户 Gux 在 2018 年购买了他的票,他带着他的——实际上是有效的票——在它的 QR 码中包含一个 MD5 哈希(因为在我们平台的旧版本中我们使用 MD5)但是安全守卫得到红屏,因为 sha256_hash_is_valid_ticket($_POST['hash_on_ticket']) returns 红屏。显然是因为依赖于 sha256 的函数无法解释 MD5 哈希。所以 Gux 确实有一张有效的票,但是无法进入活动,因为后端无法再解释他的旧票。

我们如何解决这个问题?我们如何确保在更新我们的后端或平台时,已经购买的门票仍然有效?

我在想这样的事情: 我们在票证上放了一个特定的版本号,这样我们就可以确定我们应该使用哪个版本的后端。

例如: 2017 年门票的二维码将包含: V1.0;098f6bcd4621d373cade4e832627b4f6 (V1.0;MD5_HASH) 2021年或以后购买的门票将包含 V2.0;9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 (V2.0;SHA256_HASH)

后端看起来像这样:

if ($_POST['action_check_hash']) {
    valid = false;

    // Checking what version number the QR-code contains
    if ($_POST['version_on_ticket'] == "V1.0") {
        valid = md5_hash_is_valid_ticket($_POST['hash_on_ticket']);
    } 
    else if ($_POST['version_on_ticket'] == "V2.0") {
        valid = sha256_hash_is_valid_ticket($_POST['hash_on_ticket']);
    } else {
        valid = false;
    }

    if (valid) {
        return GREEN_SCREEN;
    }

    return RED_SCREEN;
}

每次更新我们都会确保 QR 码中的散列前缀有新的版本号。在后端,我们通过检查版本号知道要使用什么算法或逻辑。这是解决问题的好方法吗?

我想到的几个解决方案:

TL;DR: 如何使用我们后端工作的最新版本不支持的 format/logic 保留旧票证。以及如何让后端知道它需要切换到旧逻辑才能正确解释传入的数据?

非常期待您的回答!

是的,为此使用版本号是个好主意。通常,您应该假设您向用户发送的每种数据格式(令牌、标识符等)都会更改并包含一个版本号。然后,当(而不是如果)您需要进行更改时,您可以修改版本号。此更改可以是哈希算法、您需要从标识符中解析出的数据,或者您需要进行的任何其他数据格式更改。

我个人喜欢这样做的方式是为此使用一个 BER 编码的整数,然后将它添加到数据中,用十六进制或 base64 或任何你使用的编码。这是一种编码,每个字节的低七位代表数据,如果整数继续到下一个字节,则最高位为 1,如果这是最后一个字节,则最高位为 0。这是可变长度的,但它针对较小的值(通常更频繁)进行了优化并且易于解析。当然,你可以选择任何你喜欢的合理方案。

请注意,如果您预先选择了版本号,则可以简单地将标识符存储在数据库中,并在其前面加上版本号,这样就不需要额外的字段来指定类型。当然,后见之明总是20/20。