我有许多MySQL表描述诸如“User”,“Business”等模型。这些表的主键是否应该暴露给客户端?我主要是从安全角度提问,但还有其他一些我没有想过的考虑因素吗?
答案 0 :(得分:16)
公开主键(特别是如果它们是可预测的)是一个名为“不安全的直接对象引用”的漏洞。
通过这样的URL(或任何其他客户提供的参数):
http://www.domain.com/myaccount?userid=12
您可以让最终用户有机会搞乱这些变量并传递他们喜欢的任何数据。缓解此漏洞的对策是创建间接对象引用。这可能听起来像是一个很大的变化,但并不一定如此。您不必重新键入所有表格或任何内容,只需通过使用间接参考地图聪明地处理数据即可。
请考虑一下:您的用户正在您的网站上进行购买。当需要支付时,他们会向您提供他们“存档”的信用卡号码。如果您查看下拉列表的代码,您会看到信用卡号码与密钥8055,9044和10099相关联。
用户可能会看到这一点并认为它们看起来很像自动递增主键(用户可能是正确的)。所以他开始尝试其他钥匙,看看他是否可以用别人的卡付款。
从技术上讲,您应该在服务器端拥有代码,以确保所选卡是用户帐户的一部分,并且可以使用它。这是一个人为的例子。现在我们假设情况并非如此,或者这是另一种可能没有这种服务器端控制的形式。
那么我们如何阻止最终用户选择一个他们不应该使用的密钥呢?
不是向DB显示对记录的直接引用,而是给它们一个间接引用。
我们将在服务器上创建一个数组,并将其填入用户的会话中,而不是将数据库键放入下拉列表中。
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
答案 1 :(得分:3)
通常,可以将任何数据发布到浏览器。但不要忘记:
传递给客户端并传回服务器的任何数据都可能是 以任何方式妥协。不要相信客户返回的数据!
不幸的是,如果您发布密钥 - 不知何故 - 用户更改密钥并且您无法正确验证密钥,如果从客户端发回服务器,则可能会发生有害的事情。
因此,您应该编写关于客户端到服务器发布/获取密钥的非常防御性代码。实际上,您不应该信任从客户端发回服务器的任何数据。
我的question也可能会引起人们的兴趣。
如我的问题所述,我的最新应用程序从未向客户发布识别数据。更一般地说,甚至通常用作GET / POST参数中的参数的东西都不会被写入客户端。
控制应用程序流的所有键或以某种方式实体相关的属性都是严格的服务器端。
我在question中提出的替代D)提供了以下内容:
答案 2 :(得分:1)
在表格中有一列UUID以及自动增量主键。在客户端中公开UUID,并使用自动增量键进行连接和后端处理。
答案 3 :(得分:1)
如果您的用户不介意混淆的网址,那么一个可以使用散列来寻找另一层安全性。像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
}
答案 4 :(得分:0)
通常认为暴露自动增量键是个坏主意。域主键很好。
答案 5 :(得分:0)
一般来说暴露它并没有太大的危害。人们可以通过拥有更高的ID来猜测之后创建了哪些记录。
但是你需要有任何必要的安全措施。如果用户更改了ID,您是否已经运行检查以查看是否允许他们查看该数据?如果您有其他查找数据的方法,则可能更难猜测正确的值。但你必须确保它们是独特的,你使用的数据也可能是可猜测的。