【问题标题】:Should primary keys of MySQL tables be exposed?MySQL表的主键是否应该暴露?
【发布时间】:2015-02-07 02:38:00
【问题描述】:

我有许多描述模型的 MySQL 表,例如“用户”、“业务”等。这些表的主键是否应该暴露给客户端?我主要是从安全角度询问,但还有其他我没有想到的考虑因素吗?

【问题讨论】:

  • 嗯...所以如果您想更新表中的特定行,如果没有密钥,您将如何引用它?
  • 对于用户,我可能会使用更公开的“用户名”,它也保证是唯一的。但并不是我所有的模型都必须有这样的替代唯一标识符。
  • 我倾向于让它们可用,但不突出

标签: mysql security


【解决方案1】:

暴露您的主键(特别是如果它们是可预测的)是一个称为不安全直接对象引用的漏洞。

通过这样的 URL(或任何其他客户端提供的参数):

http://www.domain.com/myaccount?userid=12

您让最终用户有机会处理这些变量并传递他们喜欢的任何数据。缓解此漏洞的对策是创建间接对象引用。这听起来像是一个很大的变化,但不一定非得如此。您不必重新设置所有表或任何东西的密钥,只需通过使用间接引用映射巧妙地处理数据即可。

考虑一下:您有一个用户正在您的网站上进行购买。当需要付款时,他们会看到您“存档”的他们的信用卡号码的下拉列表。如果您查看下拉菜单的代码,您会看到信用卡号与密钥 8055、9044 和 10099 相关联。

用户可能会看到这个并认为它们看起来很像自动递增的主键(用户可能是对的)。于是他开始尝试其他钥匙,看看他是否可以用别人的卡支付。

现在从技术上讲,您应该在服务器端有代码,以确保所选卡是用户帐户的一部分并且他们可以使用它。这是一个人为的例子。现在我们假设情况并非如此,或者这是另一种可能没有这种服务器端控件的表单。

那么我们如何防止最终用户选择他们不应该使用的密钥呢?

不要向他们显示对数据库中记录的直接引用,而是给他们一个间接引用。

我们将在服务器上创建一个数组并将其填充到用户的会话中,而不是将数据库密钥放入下拉列表中。

Array cards = new Array(3);
cards[0] = 8055;
cards[1] = 9044;
cards[2] = 10099;

在下拉列表中,我们现在提供对存储卡的数组索引的引用。因此,如果最终用户查看源代码,他们将看到值 0、1 和 2,而不是看到实际的键。

提交表单时,将传递其中一个值。然后我们从用户会话中取出数组,并使用索引来获取值。实际密钥从未离开服务器。

如果用户愿意,他可以全天传递不同的值,但他永远不会得到除了他自己的卡片以外的结果,不管服务器端的访问控制如何。

请记住,当使用传入的索引来获取值时,如果用户确实弄乱了它,您可能会遇到一些异常(ArrayOutOfBounds、InvalidIndex 等)。因此,将这些东西包装在 try/catch 中,这样您就可以抑制这些错误并记录失败以查找破解尝试。

希望这会有所帮助。

要了解有关不安全直接对象引用的更多信息,请查看 OWASP Top 10。风险编号为 A4。 https://www.owasp.org/index.php/Top_10_2010-A4-Insecure_Direct_Object_References

【讨论】:

  • 感谢您的详细回答。综上所述,要么不使用主键,要么在使用时进行检查。所以只是想知道:在 Facebook API 中,他们向开发人员公开了用户 ID(一个大数字)。您认为他们每次都使用直接对象引用并执行检查,还是仍然是间接对象引用?
  • 在一个安全的系统中,你应该两者都做。纵深防御。如果进行身份验证检查的服务器端逻辑有漏洞怎么办?以合理的成本提供双重保护。
【解决方案2】:

通常,可以将任何数据发布到浏览器。但不要忘记:

任何传递给客户端并传回服务器的数据都可能是 以任何方式妥协。不要相信客户端返回的数据!

不幸的是,如果您发布密钥 - 不知何故 - 用户更改了它并且您未能正确验证密钥,如果从客户端发布回服务器,可能会发生有害的事情。

因此,您应该编写有关客户端到服务器发布/获取密钥的防御性代码。事实上,您不应该信任从客户端回传到服务器的任何数据。

我的question 可能也会感兴趣。

如我的问题所述,我最新的应用程序从未向客户端发布识别数据。更一般地说,甚至那些通常用作 GET/POST 参数中的参数的东西也不会被写入客户端

控制应用程序流程的所有键或某种与实体相关的属性都严格仅限于服务器端。

我在question 中提出的备选方案 D) 提供了以下内容:

  • 服务器收到的任何请求都是默认有效,因为所有有效链接都是在页面呈现时间而不是在请求时间。
  • 用户无法使用电子邮件转发链接或发布它们,因为它们仅在其会话处于活动状态时存在。
  • 用户只能请求网页内的这些链接,因为只有这些链接在服务器端注册。
  • 所有状态相关数据都保存在服务器端,因此客户端无法更改。

【讨论】:

    【解决方案3】:

    在表中拥有一列 UUID 以及自增主键。在客户端中公开 UUID,并为连接和后端进程使用自动增量键。

    【讨论】:

      【解决方案4】:

      如果您的用户不介意经过混淆的 URL,如果要寻找另一层安全性,可以使用散列。类似于 PHP 中的以下内容。

      $id = 1234;
      $url = 'http://domain.com?id='. $id .'&v='. sha1('SALT1'. $id. 'SALT2');
      

      服务器端的数据可以进行如下双重检查。

      if ( $_GET['v'] == sha1('SALT1'. $_GET['id']. 'SALT2') ){
      //run further checks here
      }
      

      【讨论】:

      • 哦,至于问题,其实我是来找答案的;我认为只要有适当的检查,这没什么大不了的。您知道他们如何通过默默无闻来谈论安全性。
      【解决方案5】:

      公开自动增量键通常被认为是一个坏主意。域主键没问题。

      【讨论】:

      • 不想争论,只是好奇。为什么它被认为是一个坏主意?
      • @itsmatt 我一直认为向用户公开 PK 是个坏主意,因为在某些情况下,他们会希望出于任何原因更改密钥,而这并不是一件容易的事去做。如果您需要实体的 ID,最好创建一个单独的字段,用户可以在不破坏任何内容的情况下进行更改。
      • @matthewh - 是的,我不会向用户展示 PK,因为它们通常对他们没有任何意义。我按照你的建议去做 - 给他们一个他们可以重命名的字段。
      • @matthewh:ON UPDATE CASCADE 是你的朋友。
      【解决方案6】:

      一般来说暴露它不会有太大的危害。人们可以通过拥有更高的 id 来猜测哪些记录是后来创建的。

      但您需要具备任何必要的安全措施。如果用户更改了 id,您是否已经在运行检查以查看是否允许他们查看该数据?如果您有其他查找数据的方法,则可能更难猜出正确的值。但是您必须确保它们是不同的,并且您使用的数据也可能是可猜测的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-29
        • 2010-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        相关资源
        最近更新 更多