安全的许可证系统

信息安全 密码学 drm 许可证执行
2021-08-26 16:27:12

所以我试图想出一个好方法来检查用户为程序提供的许可证密钥是否合法。

让我们假设这是他们获得许可证的方式

  1. 他们进行某种类型的购买。
  2. 服务器根据其用户名生成许可证
  3. 服务器给用户许可
  4. 当用户第一次尝试使用程序时,他们使用所述许可证密钥
  5. 服务器检查许可证是否合法。

我有一个问题是什么是仅使用用户名生成“安全”许可证的好方法。

目前我在 Java 服务器上有类似的东西。

username += "a long random string literal here";
        username = username.toUpperCase();
        md = MessageDigest.getInstance("SHA-256");
           md.update(username.getBytes());
            byte[] mdbytes = md.digest();

如您所见,我在用户名中添加了一个长的随机字符串,然后通过将其更改为大写来使其不区分大小写。

我在编辑的用户名上使用 SHA。

那么这其中的弱点究竟是什么?他们是检查许可证的更好方法吗?

显然,客户永远不会看到生成方法,所以我看不到他们如何生成自己的许可证,除非他们获得了我添加到用户名的字符串。

3个回答

许可证生成方法并不那么重要,只要它不是微不足道的。诀窍是您的客户如何验证许可证是否正确。

假设您执行以下操作:

BOOL verifyLicense (char* licenseKey)
{
    BOOL result = false;

    if(strlen(licenseKey) > 128)
        return false;

    char* url = (char*)malloc(1024);
    sprintf(url, "%s%s", LicenseServerBaseURL, licenseKey);

    char* response = http_get(url);

    if (strcmp(response, "OK") == 0)
        result = true;

    free(result);
    free(url);
}

这非常简单——我们对许可证密钥进行一些基本检查,创建一个包含许可证密钥的 URL,然后对服务器执行 HTTP GET 以检查许可证。如果它返回“OK”,我们接受它。

这样做的问题是任何人都可以反汇编客户端:

 ...
 push 06f2011c        ; address of url string
 call http_get        ; http_get(url)
 mov ebx, eax         ; store a copy of the result address
 push 04830040        ; ASCII "OK"
 push eax             ; address of response string
 call msvcrt.strcmp   ; strcmp(response, "OK")
 test eax, eax        ; if( ^ == 0 )
 jnz exit             ; skip if branch if non-zero
 mov eax, 1           ; result = true
exit:
 push ebx
 call msvcrt.free     ; free(result)
 push 06f2011c
 call msvcrt.free     ; free(url)
 ret

我所要做的就是将 更改jnz exit为一系列nop指令,因此永远不会发生跳转并且结果始终为真。这样,任何许可证密钥都被接受。更好的是,我可以简单地将方法的开头修改为立即设置eax为 1 并返回,因此它甚至不必费心要求您的服务器进行许可证验证。

那么,我们如何解决这个问题呢?不幸的是,我们不能。您遇到了 DRM 问题,该问题实质上表明,如果您将一些数据交给某人,他们总是可以更改它。无论您对代码进行了多少混淆,用户最终都有可能对其进行逆向工程并弄清楚如何进行更改或提取数据。如果您加密内容,您最终必须在系统上拥有密钥才能对其进行解密,因此用户总有办法提取该密钥并永久解密内容。

你能做的最好的就是让它变得困难,这需要大量的时间和精力。最终,您的软件将被破解并最终出现在某处的 torrent 站点上。对于使用这种商业模式的开发人员来说,这很糟糕,但这是我们生活的世界。我的建议是你走以下两条路线之一:

  1. 创建一个有效的基本许可系统,并接受某些人会盗版它的事实。通过花费您的开发时间实际改进软件,而不是实施和维护严苛的 DRM 系统,为您的真正客户服务。

  2. 改变您的整个开发和业务范式,使您的产品成为服务而不是软件应用程序。SaaS非常成功,因为盗版服务(实际上)是不可能的,并且该模型允许您对代码库进行实时更改,并对使用情况进行分析。

如果您选择选项 1,请不要使用服务器进行验证。它增加了复杂性,没有提供额外的安全性,带来一些隐私问题,并且如果您的许可服务器关闭,您的用户将无法使用您的软件。只需坚持一些简单的东西,例如用户名的哈希值和客户端的秘密值。打破它是微不足道的,但它为盗版提供了最低限度的障碍。

如果您要做的只是生成一个难以复制的许可证密钥,那么通过使用用户名作为哈希输入的一部分,您实际上会使许可证密钥更弱,因此更容易猜测。用户名是非随机的,因此会降低生成的许可证密钥的熵,并且攻击者可以猜测用户名,然后攻击者可以对您的方法进行一些假设,从而更容易地暴力破解您的密钥。

如果您要向用户名添加随机字符串,那么根本没有充分的理由使用用户名,只需使用所有随机数据。您使用用户名生成许可证密钥的唯一原因是使其可逆。

或者,您可以向用户发送一封带有验证链接的电子邮件。一旦他们单击链接,您的服务器就知道该软件是合法的。该应用程序会定期“打电话回家”以查看其是否已通过验证,如果收到否定响应,则该应用程序将变得功能受限。

这也可以防止过多的多次激活,因为服务器会知道用户拥有多少许可证,以及已经使用了多少。显然,应用程序和服务器之间的所有通信都是通过 ssl 进行的。