代码之家  ›  专栏  ›  技术社区  ›  uriDium

对于db id,需要一个比guid更小的选项,但是对于url,仍然是唯一的和随机的。

  •  32
  • uriDium  · 技术社区  · 16 年前

    我找遍了所有的地方都找不到答案。所以如果stackoverflow上已经有了答案,那么我提前道歉。

    我想要一个唯一的随机ID,这样我的网站上的用户就不能猜到下一个号码,只需跳到其他人的信息。我计划为主键坚持一个递增的ID,但也要在数据库中为该行存储一个随机的唯一ID(散列类型),并在上面放置一个索引。

    通过我的搜索,我意识到我想避免碰撞,我读过一些关于sha1的文章。

    我的基本要求是

    • 比guid小的东西。(在URL中看起来很可怕)
    • 必须是唯一的
    • 避免碰撞
    • 没有一长串不可读的奇怪字符。

    我要找的一个例子是www.somesite.com/page.aspx?ID= AF78FEB

    我不确定是否应该在数据库(我使用的是SQL Server 2005)或代码(我使用的是C ASP.NET)中实现此功能。

    编辑:

    从我所做的所有阅读中,我意识到这是通过默默无闻的安全。我确实打算拥有访问页面的适当授权和身份验证。我将使用.NET的身份验证和授权框架。但是一旦一个合法用户登录并访问一个合法的(但动态创建的)页面,该页面会充满指向他所属项目的链接。例如,链接可能是www.site.com/page.aspx?ItEthyID=123。是什么阻止他点击那个链接,然后改变上面的网址去www.site.com/page.aspx?物品编号=456,不属于他?我知道一些Struts的技术(Struts)被修正了,它存储了会话中的所有内容,并不知何故从中解脱出来,但我不知道这是怎么做到的。

    11 回复  |  直到 8 年前
        1
  •  8
  •   Greg    16 年前

    [回应编辑]
    您应该将查询字符串视为“邪恶的输入”。您需要以编程方式检查是否允许经过身份验证的用户查看请求的项目。

    if( !item456.BelongsTo(user123) )
    {
      // Either show them one of their items or a show an error message.
    }
    
        2
  •  16
  •   Zhaph - Ben Duguid    9 年前

    Raymond Chen 有一篇关于为什么不应该使用“半个guid”的好文章,并提供了一个合适的解决方案来生成自己的“不完全guid,但足够好”类型值:

    GUIDs are globally unique, but substrings of GUIDs aren't

    他的战略(没有具体实施)基于:

    • 四位编码计算机号码,
    • 56位表示时间戳,以及
    • 四位作为uniquifier。

    由于集群中的计算机数量是有界的,因此我们可以减少位的数量以使计算机具有唯一性,并且我们可以通过假设程序从现在起200年后不会运行来减少时间戳中的位的数量。

    您可以通过假设时钟不会偏离一个多小时的歪斜(比如说),并且时钟不会每小时重置超过16次来摆脱四位uniquifier。

        3
  •  15
  •   CraigTP    8 年前

    更新(2017年2月4日):
    Walter Stabosz 在原始代码中发现了一个错误。经过调查,我们发现了更多的错误,但是,原始作者本人对代码进行了大量的测试和修改。( CraigTP )现在已经解决了所有这些问题。我已经用正确的工作版本更新了这里的代码,您也可以 download a Visual Studio 2015 solution here 它包含“shortcode”生成代码和一个相当全面的测试套件来证明其正确性。

    我过去使用的一个有趣的机制是,在内部只使用一个递增的整数/长,但要将该整数“映射”为字母数字“代码”。

    例子

    Console.WriteLine($"1371 as a shortcode is: {ShortCodes.LongToShortCode(1371)}");
    Console.WriteLine($"12345 as a shortcode is: {ShortCodes.LongToShortCode(12345)}");
    Console.WriteLine($"7422822196733609484 as a shortcode is: {ShortCodes.LongToShortCode(7422822196733609484)}");
    
    Console.WriteLine($"abc as a long is: {ShortCodes.ShortCodeToLong("abc")}");
    Console.WriteLine($"ir6 as a long is: {ShortCodes.ShortCodeToLong("ir6")}");
    Console.WriteLine($"atnhb4evqqcyx as a long is: {ShortCodes.ShortCodeToLong("atnhb4evqqcyx")}");    
    
    // PLh7lX5fsEKqLgMrI9zCIA   
    Console.WriteLine(GuidToShortGuid( Guid.Parse("957bb83c-5f7e-42b0-aa2e-032b23dcc220") ) );      
    

    代码

    下面的代码显示了一个简单的类,它将把long更改为“code”(然后再返回!):

    public static class ShortCodes
    {
        // You may change the "shortcode_Keyspace" variable to contain as many or as few characters as you
        // please.  The more characters that are included in the "shortcode_Keyspace" constant, the shorter
        // the codes you can produce for a given long.
        private static string shortcodeKeyspace = "abcdefghijklmnopqrstuvwxyz0123456789";
    
        public static string LongToShortCode(long number)
        {
            // Guard clause.  If passed 0 as input
            // we always return empty string.
            if (number == 0)
            {
                return string.Empty;
            }
    
            var keyspaceLength = shortcodeKeyspace.Length;
            var shortcodeResult = "";
            var numberToEncode = number;
            var i = 0;
            do
            {
                i++;
                var characterValue = numberToEncode % keyspaceLength == 0 ? keyspaceLength : numberToEncode % keyspaceLength;
                var indexer = (int) characterValue - 1;
                shortcodeResult = shortcodeKeyspace[indexer] + shortcodeResult;
                numberToEncode = ((numberToEncode - characterValue) / keyspaceLength);
            }
            while (numberToEncode != 0);
            return shortcodeResult;
        }
    
        public static long ShortCodeToLong(string shortcode)
        {
            var keyspaceLength = shortcodeKeyspace.Length;
            long shortcodeResult = 0;
            var shortcodeLength = shortcode.Length;
            var codeToDecode = shortcode;
            foreach (var character in codeToDecode)
            {
                shortcodeLength--;
                var codeChar = character;
                var codeCharIndex = shortcodeKeyspace.IndexOf(codeChar);
                if (codeCharIndex < 0)
                {
                    // The character is not part of the keyspace and so entire shortcode is invalid.
                    return 0;
                }
                try
                {
                    checked
                    {
                        shortcodeResult += (codeCharIndex + 1) * (long) (Math.Pow(keyspaceLength, shortcodeLength));
                    }
                }
                catch(OverflowException)
                {
                    // We've overflowed the maximum size for a long (possibly the shortcode is invalid or too long).
                    return 0;
                }
            }
            return shortcodeResult;
        }
    }
    

    }

    这基本上是您自己的basex编号系统(其中x是shortcode_keyspace常量中唯一字符的数目)。

    要使事情变得不可预测,请从1或0以外的位置开始内部递增编号(即从184723开始),并更改短代码\u keyspace常量中字符的顺序(即使用字母A-Z和数字0-9,但将它们的顺序散放在常量字符串中)。这将有助于使每个代码都有点不可预知。

    如果你用这个来“保护”任何东西,这仍然是模糊的安全,如果一个给定的用户能够观察到足够多的这些生成的代码,他们就可以预测一段给定时间内的相关代码。这其中的“安全性”(如果你可以这么叫的话)就是短代码的keyspace常量被置乱了,并且仍然是秘密的。

    编辑: 如果您只想生成一个guid,并将其转换为一些仍然是唯一的,但包含较少字符的内容,那么这个小函数将完成以下操作:

    public static string GuidToShortGuid(Guid gooid)
    {
        string encoded = Convert.ToBase64String(gooid.ToByteArray());
        encoded = encoded.Replace("/", "_").Replace("+", "-");
        return encoded.Substring(0, 22);
    }
    
        4
  •  14
  •   David Basarab    16 年前

    如果您不希望其他用户看到用户信息,为什么不保护您使用的ID页面?

    如果这样做,那么使用递增的ID就不重要了。

        5
  •  3
  •   webjunkie    16 年前

    你可以随机生成一个数字。检查这个数字是否已经存在于数据库中并使用它。如果您希望它显示为一个随机字符串,您可以将其转换为十六进制,这样您就可以在其中得到a-f,就像在示例中一样。

        6
  •  2
  •   Gumbo    16 年前

    一个guid是128位。如果取这些位,不要使用只有16个字符的字符集来表示它们(16=2^4和128/4=32个字符),而使用64个字符的字符集(如以64为基数),则最终只会得到22个字符(64=2^6和128/6=21.333,即22个字符)。

        7
  •  2
  •   Emil Sit    16 年前

    带上你的自动增值ID,然后HMAC-SHA1它和一个只有你知道的秘密。这将生成一个随机查找的160位来隐藏真正的增量ID。然后,取一个长度的前缀,使应用程序不太可能发生冲突,比如64位,可以用8个字符编码。用这个做你的绳子。

    HMAC将保证没有人可以从显示的位映射回基础数字。通过散列一个自动递增的ID,您可以非常确定它是唯一的。所以发生碰撞的风险来自于sha1中64位部分碰撞的可能性。使用此方法,您可以通过预先生成此方法生成的所有随机字符串(例如,最多可达预期的行数)并进行检查来预先确定是否会发生冲突。

    当然,如果您愿意在数据库列上指定一个唯一的条件,那么简单地生成一个完全随机数也可以。你只需要注意随机性的来源。

        8
  •  0
  •   Jeremy Boyd    16 年前

    你能做的就是当我想要你想要的东西的时候我做的事情。

    1. 创建你的guid。

    2. 去掉破折号,得到 子字符串显示您希望 身份证件

    3. 检查数据库的ID,如果 存在转到步骤1。

    4. 插入记录。

    这是最简单的方法来确保它是模糊和独特的。

        9
  •  0
  •   uriDium    16 年前

    我刚刚有了一个想法,我看到格雷格也指出了这一点。我在会话中使用用户ID存储了该用户。创建查询时,我将使用该用户ID加入到用户表中,如果结果集为空,那么我们知道他正在入侵URL,我可以重定向到错误页。

        10
  •  0
  •   Luis Perez    8 年前

    guid只是一个数字

    最新一代的guid(版本4)基本上是一个大的随机数*

    因为它是一个很大的随机数,所以发生碰撞的可能性很小。

    使用guid可以获得的最大数量已超过:

    5,000,000,000,000,000,000,000,000,000,000,000,000
    

    因此,如果生成两个guid,则第二个guid与第一个guid相同的可能性是:

    1 in 5,000,000,000,000,000,000,000,000,000,000,000,000
    

    如果你生成1000亿个guid。

    你的机会 100第十亿 guid与其他9999999999 guid冲突为:

    1 in 50,000,000,000,000,000,000,000,000
    

    为什么是128位?

    一个原因是计算机喜欢使用8位的倍数。

    8、16、32、64、128等

    另一个原因是,提出guid的人觉得64不够,256太多了。

    您需要128位吗?

    不,您需要多少位取决于您希望生成多少个数字,以及您希望它们不会发生碰撞。

    64位示例

    那么第二个数字与第一个数字碰撞的可能性是:

    1 in 18,000,000,000,000,000,000 (64 bit)
    

    而不是:

    1 in 5,000,000,000,000,000,000,000,000,000,000,000,000 (128 bit)
    

    那呢? 100第十亿 数字?

    您的第100亿个数字与另一个9999999999发生碰撞的可能性是:

    1 in 180,000,000 (64 bit)
    

    而不是:

    1 in 50,000,000,000,000,000,000,000,000 (128 bit)
    

    那么应该使用64位吗?

    取决于你是否产生了1000亿个数字?即使你当时是,180000000会让你不舒服吗?

    关于guid的更多细节

    我专门讲的是第四版。

    版本4并没有将所有128位都用于随机数部分,而是使用122位。其他6位用于指示是guid标准的版本4。

    此答案中的数字基于122位。

    是的,因为它只是一个随机数,你可以从中取你想要的位数。(只需确保您不使用6个永不更改的版本控制位中的任何一个—见上文)。

    尽管您可以使用相同的随机数生成器,而不是从guid中获取位,guid从中获取位。

    可能 使用操作系统附带的随机数生成器。

        11
  •  -1
  •   Kibbee    16 年前

    多长时间太长?您可以将guid转换为base 64,这会使它变得更短一些。

    推荐文章