模糊数据库ID

8

我有一张带自增主键的表。我想将图片与主键关联起来,但不想暴露主键。我是否可以将图片命名为:

$filename = md5($primarykey + $secret_string) . '.jpg';

这是一个好的解决方案吗?

我担心可能会出现冲突并覆盖文件。

当然,另一种选择是生成一个随机字符串,检查它是否存在于文件中并将其存储在数据库中......但如果不必要的话,我更喜欢不存储额外的数据。

另一种选择是逻辑转换YouTube网址样式,例如1 = a 2 = b,但顺序是随机的,例如1 = x 2 = m ......但那么有可能被解码......加上md5可能比任何youtube url函数更轻量级。

我猜我正在处理超过两百万条记录,那么发生冲突的可能性有多大?你会选择哪个选项或者你能想到更好的方法吗?


4
如果图像记录的主键被发现,会有什么危险? - jball
如果id列是主键,它不会轻易被覆盖。您的数据库会捕获它,然后您可以相应地处理它。 - Sinan
不涉及安全问题,但很重要,因为它允许用户创建机器人并作弊系统。 - JimBo
10个回答

2
使用线性同余生成器。如果你选择的值合适,那么你将得到一个具有非常大周期的伪随机序列。没有碰撞,但需要注意的是,这只是一种混淆方法,不会提供任何真正的安全保障(但我认为这不是你所寻找的)。

请注意,如果您想获得更好的安全性,那么可以将LCG的输出与具有密码学强度的随机数串联起来。基本上就像 $lcg_string . $rand_string; 这样LCG负责独特性,而随机字符串则使其无法预测。 (了解情况的攻击者仍然可以发现PK) - hrnt

2

我猜我正在处理超过两百万条记录,那么发生碰撞的可能性有多大?

根据维基百科的说明,你需要超过2*10^19个记录才能获得50%的概率至少有一个碰撞,所以我认为你不必担心。


1

您提供的代码非常适合完成这项工作。您不必担心碰撞,您数据库使用的任何整数都不会产生与其他任何整数相同的md5哈希值。

如果您想要更加确定:这里有一个小测试脚本 ;)

<?php
for($i = 0; $i < 1000000; ++$i) {
    $hash = md5($i);
    if(isset($x[$hash])) { die("COLLISION!"); }
    $x[$hash] = true;
}        }
echo "All is well";

一个更复杂的解决方案似乎会浪费你的时间,但如果你想要更短的ID,你可以为每张图片分配一个短的随机生成的ID(并在生成时检查它是否已经被使用,如果是,则再次生成另一个ID)。


1
通常为了在URL中隐藏数据库ID,您实际上会加密ID,因为服务器需要可逆地查找来自模糊ID的数据库记录。对于将数据库ID映射到文件名的情况,可逆性可能并不那么重要,但这是一个简单的模式,也可以消除冲突的可能性。
您需要使用base64或十六进制编码将加密数据转换为ASCII,例如:
base64_encode(encrypt(id, secret_key))

以及解码:

decrypt(base64_decode(id), secret_key)

(上面是伪代码,你需要在PHP中找到适当的函数)

加密并不需要太复杂。像DES这样的简单块密码就足够了。


0
如果您想使用ID,但不想透露ID,并且希望冲突的风险较低,则可以使用ID的哈希值。哈希值是一致的、不可逆的,并且(除了大型数据集-取决于所使用的哈希值)对于每个照片都是唯一的。您似乎已经在问题中有了基本的想法:
$filename = md5($primarykey + $secret_string) . '.jpg';

你可以用更好的哈希算法替换MD5。最好选择一些输出更大的算法。根据对评论的回应,浏览了各种文章后,似乎SHA512或类似算法更适合。


“而且不想有任何冲突;一个选项是使用ID的哈希值。”我认为你应该更仔细地研究哈希。哈希确实会产生冲突。这就是为什么你必须检查它们的原因。 - Dan McGrath
哈希值不应该发生碰撞,但是一些哈希算法如SHA-0、MD4和MD5已经被证明会出现碰撞。 - TWA
3
@Dana:哈希值是有可能发生冲突的。它们最基本的要求是将一个无限大的输入空间映射到一个有限的输出空间,这就不可避免地导致了冲突。而密码学哈希函数绝不能有故意引发冲突的方法。 - Michael Borgwardt
2
"哈希不应该冲突" - 错误。否则,就意味着有人发现了完美的压缩方法。 - ta.speot.is
1
密码哈希具有非常低的碰撞风险。 - Alex Jasmin
在我开始胡言乱语之前,先稍微了解一下。虽然哈希算法不应该发生碰撞,但有时确实会发生。有些算法比其他算法更具弹性 - MD5 算法很糟糕,我知道它很糟糕,从我点击提交按钮的那一刻起,我就一直在想我应该更直接地指示使用更好的哈希算法。 - AnonJr

0

你有两个选择:

  • 生成一个东西并验证没有冲突
  • 生成一个东西并希望没有冲突

通常可以使用以下选项: - 哈希 - 随机生成的字符串 - UUID

哈希 如果你选择哈希,请选择一个冲突率低的哈希。此外,在进行哈希时考虑为什么要隐藏数据库ID。如果你对纯数字进行哈希,很快就会有人发现你的哈希,你绝对需要加盐。加盐哈希的优点是生成速度快,碰撞的可能性小(在小规模情况下绝对不需要验证,因此插入更快)。缺点是任何正确的实现都将是SHA256或更好的算法,这意味着它很长。如果你想节省DB/Index空间,可以进行一些十六进制转换,但这可能超出你的需求。

随机字符串 你可以生成任何长度、任何字符集或数字a-Z0-9的随机字符串。这也意味着在URI、请求数据等中使用较短的字符串中包含了“更多”数据。缺点是你必须检查它是否在数据库中。

一个 UUID 像哈希一样,生成速度快,碰撞概率相当低,并且可以修改为比纯输出“更美观”的形式。
我的建议是:不要这样做。我曾经在一个非常大的实现中遇到过这个问题,而这个实现从最初的很小规模逐渐扩大。最终,你开始做一些“聪明”的事情,比如创建完全唯一的标识符(例如内容类型+你的标识符),并开始看到其中的价值,但随后你必须处理规模问题。垂直扩展这个问题非常困难。数据库被优化为使用 ID 作为主键,如果你想让它垂直扩展,你需要考虑的事情还有很多。如果必须使用,则只能用于外部客户端交互。

0

我同意使用Base64。你也可以使用Guid。或者只需在表中存储文件名,这是我所做的。

此外,请注意避免孤立的文件。


0

像MD5这样的哈希算法并不是解决此问题的好方法,因为哈希本质上不能保证唯一性(它们创建具有有限值的哈希)。

你需要的是加密。请查看javax.crypto.Cipher。


0
如果您可以添加一列,请在表中添加一个GUID作为UNIQUE列,并使用<GUID>.jpg作为键名。 GUID算法不应在可预见的未来产生重复,但以防万一,UNIQUE约束将捕获此类情况。

-1

只需使用主键的哈希值。发生冲突的概率非常低。


编写通常正确但可能会失败的代码(并且随着样本数量的增加,保证会失败)并不是一个好主意,特别是当问题有简单的解决方案时。 - ThisGuy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接