散列数值的最佳算法?

时间:2009-08-31 22:17:56

标签: algorithm delphi hash numbers

当处理一系列数字,并且出于安全原因想要使用哈希结果时,从给定的一系列数字生成哈希值的最佳方法是什么?输入的示例是信用卡号或银行帐号。首选输出将是单个无符号整数,以帮助进行匹配。

我的感觉是,大多数字符串实现在针对如此短的字符范围运行时似乎具有低熵,因此,碰撞率可能高于针对较大样本运行时的碰撞率。

目标语言是Delphi,但是如果能够提供可以导致最佳解决方案的数学基础,那么欢迎来自其他语言的答案。

此例程的目的是确定先前收到的卡/帐户是否先前已处理过。输入文件可能有多条记录针对多个记录的数据库,因此性能是一个因素。

8 个答案:

答案 0 :(得分:12)

对于安全问题,所有答案都是从最安全最方便连续体。我会给你两个答案,一个非常安全,一个非常方便。鉴于此以及每个解释,您可以为您的系统选择最佳解决方案。

您声明您的目标是存储此值以代替实际信用卡,以便您稍后可以知道是否再次使用相同的信用卡号。这意味着它必须只包含信用卡号码,并且可能包含均匀的盐。包含CCV,到期日期,名称等将使其无用,因为它可能与相同的信用卡号不同。因此,我们假设您使用相同的盐值填充所有信用卡号码,这些盐值对于所有条目都保持一致。

方便的解决方案是使用FNV(如Zebrabox和Nick建议的那样)。这将产生一个32位数字,可以快速索引搜索。当然,缺点是它只允许最多40亿个不同的数字,并且在实践中会产生更快的碰撞。因为它具有如此高的碰撞率,所以蛮力攻击可能会产生足够的无效结果,使其几乎没有用处。

安全解决方案是依赖SHA哈希函数(越大越好),但需要多次迭代。我会建议大约10,000的地方。是的,我知道,10,000次迭代很多,而且需要一段时间,但是当谈到强大对抗蛮力时,攻击速度就是敌人。如果你想要安全,那么你希望它是缓慢的。 SHA旨在不会出现任何大小的输入冲突。如果发现冲突,则认为散列不再可行。 AFAIK SHA-2系列仍然可行。

现在,如果您想要一个安全且快速的解决方案来搜索数据库,那么我建议使用安全解决方案(SHA-2 x 10K),然后将完整哈希存储在一个列,然后取前32位并将其存储在不同的列中,索引位于第二列。首先对32位值进行查找。如果没有产生匹配则没有匹配。如果它确实产生匹配,那么您可以比较完整的SHA值并查看它是否相同。这意味着您正在执行完整的二进制比较(哈希实际上是二进制,但仅表示为字符串,以便于人类阅读和基于文本的协议中的传输)在更小的集合上。

如果你真的关心速度,那么你可以减少迭代次数。坦率地说,即使进行1000次迭代,它仍然会很快。您将需要对您期望数据库获得的大小以及可能影响持续时间的其他因素(通信速度,硬件响应,负载等)做出一些现实的判断调用。您可能会发现在流程中优化最快点,这几乎没有实际影响。

另外,我建议您基准查看完整哈希与32位子集的查找。大多数现代数据库系统都相当快,并且包含许多优化,并且经常针对我们以 easy 的方式进行优化。当我们试图变得聪明时,我们有时会放慢速度。什么是关于过早优化的引用。 。 。 ?

答案 1 :(得分:6)

这似乎是key derivation functions的情况。看看PBKDF2

仅使用加密哈希函数(如SHA系列)将为您提供所需的分布,但对于非常有限的输入空间(如信用卡号),它们可以使用强力攻击轻松攻击,因为此哈希算法通常设计为尽可能快。

<强>更新

好的,安全性与您的任务无关。因为您已经有了数字输入,所以您可以使用这个(帐户)数量模拟您的哈希表大小。如果您将其作为字符串处理,您可能确实会遇到错误的分布,因为十个数字只构成所有可能字符的一小部分。

另一个问题可能是数字形成了大的已分配(帐户)数字群集,它们之间有大量未分配的数字区域。在这种情况下,我建议尝试高度非线性哈希函数来传播这个集群。这将我们带回到加密哈希函数。也许好老MD5。只需将128位散列分成四组32位,使用XOR组合它们,并将结果解释为32位整数。

虽然没有直接关联,但您也可以查看Benford's law - 它提供了一些有关数字通常不均匀分布的信息。

答案 2 :(得分:3)

如果您需要安全性,请使用加密安全散列,例如SHA-256。

答案 3 :(得分:2)

如果表现是一个因素,我建议你看一下Peter CodeCentral entry。它对于大量物品表现非常好。

默认使用P.J. Weinberger ELF hashing function。但也提供了其他人。

答案 4 :(得分:2)

几个月前我需要深入研究哈希函数。以下是我发现的一些事情。

您希望散列在整个目标空间中均匀且随机地分布命中(通常为32位,但可以是16位或64位。)您希望输入的每个字符对输出具有同等大的影响。

所有简单的哈希(如ELF或PJW)只需循环遍历字符串,并且每个字节中的xor都带有shift或mod将失败该条件,原因很简单:添加的最后一个字符效果最好。

但Delphi和asm中有一些非常好的算法可用。以下是一些参考文献:

见burtleburtle.net/bob/hash/doobs.html 1997年的Dobbs博士文章 代码:burtleburtle.net/bob/c/lookup3.c

Paul Hsieh(AKA HsiehHash)的SuperFastHash功能c2004-2008 www.azillionmonkeys.com/qed/hash.html

您可以在此参考文献中找到Delphi(带有可选的asm)源代码:
http://landman-code.blogspot.com/2008/06/superfasthash-from-paul-hsieh.html
2008年7月13日
“一年多以前,Juhani Suhonen要求快速哈希用于他的 哈希表。我提出了旧的但表现良好的精灵哈希,但也注意到了 我最近发现了一个更好的哈希函数。它被称为SuperFastHash(SFH) 由Paul Hsieh创建,用哈希函数克服他的“问题” 来自鲍勃詹金斯。 Juhani询问是否有人可以在basm中编写SFH函数。 一些人致力于实施并发布了它。“

哈希传奇继续:
2007-03-13安德鲁:当坏哈希意味着好缓存
  www.team5150.com/~andrew/blog/2007/03/hash_algorithm_attacks.html
2007-03-29安德鲁:打破SuperFastHash
  floodyberry.wordpress.com/2007/03/29/breaking-superfasthash/
2008-03-03 Austin Appleby:MurmurHash 2.0
  murmurhash.googlepages.com/
  SuperFastHash - 985.335173 mb / sec
  lookup3 - 988.080652 mb / sec
  MurmurHash 2.0 - 2056.885653 mb / sec
  提供c ++代码MurmurrHash2.cpp和对齐只读实现 -
MurmurHashAligned2.cpp
// ================================================ ========================
//这是Landman的MurmurHash2 in C#
// 2009-02-25 Davy Landman做了SuperFashHash和MurmurHash2的C#实施 //landman-code.blogspot.com/search?updated-min=2009-01-01T00%3A00%3A00%2B01%3A00&updated-max=2010-01-01T00%3A00%3A00%2B01%3A00&max-结果= 2
//
// Landman在C#中使用SuperFastHash和MurmurHash2四种方式: // 1:托管代码2:内联位转换器3:Int Hack 4:不安全指针
// SuperFastHash 1:281 2:780 3:1204 4:1308 MB / s
// MurmurHash2 1:486 2:759 3:1430 4:2196

对不起,如果以上结果看起来像一团糟。我不得不切割并粘贴它。

上面至少有一个参考文献为您提供了一个64位哈希的选项,它肯定不会在信用卡号码空间中发生冲突,并且可以很容易地存储在MySQL的bigint字段中。 / p>

您不需要加密哈希。它们的CPU密集程度更高。而“加密”的目的是阻止黑客攻击,而不是避免冲突。

答案 5 :(得分:1)

根据定义,加密哈希将完美适用于您的用例。即使字符很接近,散列也应该很好地分配。

所以我建议你使用任何加密哈希(例如SHA-256)和盐。

答案 6 :(得分:1)

对于非加密方法,您可以快速查看FNV hash,并且碰撞率较低。

作为一种非常快速的替代方案,我也使用了这个算法几年并且几乎没有碰撞问题,但我不能给你一个数学分析它固有的健全性,但是它的价值在于它是

=编辑 - 我的代码示例不正确 - 现在已修复=

在c / c ++中

unsigned int Hash(const char *s)
{
    int hash = 0;

    while (*s != 0)
    {
        hash *= 37;
            hash += *s;
        s++;
    }

    return hash;
}

请注意,'37'是一个幻数,所以选择它是因为它是素数

答案 7 :(得分:1)

自然数的最佳哈希函数

 f(n)=n

没有冲突;)