V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
这是一个专门讨论 idea 的地方。

每个人的时间,资源是有限的,有的时候你或许能够想到很多 idea,但是由于现实的限制,却并不是所有的 idea 都能够成为现实。

那这个时候,不妨可以把那些 idea 分享出来,启发别人。
est
V2EX  ›  奇思妙想

想到一个自增 id 但是同时防止遍历的办法

  •  2
     
  •   est · 2015-11-12 10:56:13 +08:00 · 14677 次点击
    这是一个创建于 3078 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如会员, uid 从 1 开始,那么一些人就可以从 1 开始爬虫。。。

    于是我在脑洞一些办法防止这个事。有的用uuid,甚至hash,感觉都不是很友好。在保持id自增的前提下,我想了一些办法:

    第一个手段当然是限频率和屏蔽手段。。。

    第二个手段,就是把自增 id 弄成稀疏的。 比如统统跳过 id 个位数为 7 的。。那么如果有人故意访问 uid=7, uid=17, uid=27 等等,就直接判定为爬虫,封锁之。

    当然,判断个位数,这个很容易被发现规律猜到。

    那么,有没有比较好的算法,让人猜不到这个 “间隙” id 的规律,但是服务器又非常容易判断为不可用 id 呢?
    85 条回复    2015-11-20 10:31:59 +08:00
    pubby
        1
    pubby  
       2015-11-12 11:07:29 +08:00
    偶尔我用 /user/{uid}-{md5(uid+key)} 的形式防止遍历
    est
        2
    est  
    OP
       2015-11-12 11:08:04 +08:00
    @pubby 这个也太复杂了。。。 id 太长。。
    wy315700
        3
    wy315700  
       2015-11-12 11:08:43 +08:00
    short_uuid
    undeflife
        4
    undeflife  
       2015-11-12 11:12:31 +08:00
    不需要算法 id 随便什么规律都好 没规律也行
    fail2ban 解析 access.log 多次符合规则的 404 ban 掉就行了
    pubby
        5
    pubby  
       2015-11-12 11:14:51 +08:00
    @est 嗯,所以偶尔用用。
    ts
        6
    ts  
       2015-11-12 11:16:11 +08:00
    @est 大神你还经常在哪些站里出没或者潜水 除了 v2
    est
        7
    est  
    OP
       2015-11-12 11:18:38 +08:00
    @undeflife 这个有一个问题就是别人可以新注册一个用户,看 uid 可以大致判断出你总用户数是多少。。。
    @wy315700 也可行。但是无序了。 id 自增感觉要好点。。
    xujif
        8
    xujif  
       2015-11-12 11:18:43 +08:00
    弄几个魔术 id ,访问就 ban
    cyr1l
        9
    cyr1l  
       2015-11-12 11:18:59 +08:00   ❤️ 1
    比如判断是否是质数或者是否能被某个质数(比如 17 ) 整除?
    akira
        10
    akira  
       2015-11-12 11:19:18 +08:00
    自增的 id 只对内部使用,对外是一个 uuid 或者和 id 无表面关系的字符串
    lanlanlan
        11
    lanlanlan  
       2015-11-12 11:21:13 +08:00
    只要爬虫 IP 资源够 都是浮云嘛→_→
    est
        12
    est  
    OP
       2015-11-12 11:21:25 +08:00
    @wy315700 而且如果 mysql 里用的话,还有可能冲突。。虽然很小概率。但是为了防止冲突还得加个唯一索引。这是要让人纠结死。。。而如果自增的话,直接根据上一个 id ,按照某一个算法,跳过一些 id ,就可以得到下一个 id 了。。。现在就是在想有没有这个比较好的算法。不容易猜出来,服务器计算和判断速度也比较快的。
    est
        13
    est  
    OP
       2015-11-12 11:24:22 +08:00
    @lanlanlan 如果加上运营商判断, ip 没那么好找哦。比如只允许 ADSL 的 IP 段访问,代理 ip 绝大多数是机房 ip 段。很容易判断出来。我见过很多国外论坛都只允许 comcast verizon at&t 之类的民用宽带 ip 访问。我换了 n 个代理,但是因为出口都是机房,都被判断出来了。
    est
        14
    est  
    OP
       2015-11-12 11:25:07 +08:00
    @xujif 我也是这样想的,但是从代码维护的角度来说,每人工增加一个 magic id 就要改代码,部署流程,或者改 config ,感觉略蛋痛。
    est
        15
    est  
    OP
       2015-11-12 11:25:43 +08:00
    @ts reddit hacker news. 我现在也想找别的地方混。
    est
        16
    est  
    OP
       2015-11-12 11:26:04 +08:00
    @cyr1l 差不多是这个方向。。。不知道大家有没有灵光一现更加 NB 的。。
    Elethom
        17
    Elethom  
       2015-11-12 11:26:42 +08:00 via iPhone
    那自增就沒有意義了。還不如 hash 。
    zhujinliang
        18
    zhujinliang  
       2015-11-12 11:29:23 +08:00
    之前有个类似的需求,当时的做法很 2b
    把 id 以十进制表示,然后每位随机做一个的对照表,然后再拼起来
    RitianZhao1988
        19
    RitianZhao1988  
       2015-11-12 11:35:11 +08:00
    内部一张表,外部一张表
    外部增加时有几率增加 rand 个魔术 id ,然后在内部表里再写一份没有 magic id 的怎么样?
    undeflife
        20
    undeflife  
       2015-11-12 11:42:36 +08:00
    @est 你的"间隙" id 同样不能解决这个问题
    如果 /users/xxxx 这样一个 url 并不存在分享的需求的话 无所谓友好不友好.
    如果存在分享的需求的话 /users/username 这样不是更好吗
    所以我认为完全可以从设计上弱化 uid 在用户这的存在
    wy315700
        21
    wy315700  
       2015-11-12 11:47:31 +08:00   ❤️ 1
    @est short_uuid 是 MySQL 的一个 64 位增长型 ID 啊,就是满足你的要求的,
    18000rpm
        22
    18000rpm  
       2015-11-12 11:53:37 +08:00
    跟着思路走只能想到多挖几个坑 233
    一个一位数的坑
    两个两位数的坑
    三个三位数的坑
    能整除的都不能踩,不知道这个在没遍历前还好不好猜规律
    lwbjing
        23
    lwbjing  
       2015-11-12 11:58:34 +08:00
    mongodb _id
    oott123
        24
    oott123  
       2015-11-12 12:00:57 +08:00   ❤️ 1
    lincanbin
        25
    lincanbin  
       2015-11-12 12:06:11 +08:00
    用 GUID+截断的时间戳作为主键,可以获得更高的插入效率,同时也可以一定程度防止爬虫。
    muzuiget
        26
    muzuiget  
       2015-11-12 12:11:43 +08:00
    自增 id 只在服务器器使用,对外就 hash 不就行了么, hash 同表也是唯一,允许所以?不让客户端通过 id 访问到资源。
    imn1
        27
    imn1  
       2015-11-12 12:15:54 +08:00
    虽然会增加我将来爬东西的难度,但还是要说一句话:
    外显有序 id 是低智商

    说个故事:
    上世纪末,要抓日本某站点一批数据,当时只知道 max(id)>=17000 ,步长 1 自增
    还不会写爬虫,于是开网络蚂蚁批量,直接下
    大约抓了 5000 条左右,那站点停了几小时,然后页面浏览器访问顶部出现了“巡回禁止”的横条,哈哈
    然后发现大约下 1000 条左右后面就会全部 404
    老子 proxy 多,当年还没有 qiang 的概念, ssl proxy 都是稀有物,但 http proxy 还是不少,因为原生网路就不畅,非人为原因……
    然后就每 800 条换一个 proxy ,爬完(换了多个确认是真的没有数据而不是 404 ),总数 26000+条
    这是当年不为爬 qiang 而使用梯子的典型例子
    凭这 2w 条信息,虽然没有全部发布,并且是重新组织和翻译,在小圈子也有点名气
    但也属盗版了,后来还是怕担责(即使日本追究不到我这来),撤了,自此之后虽然爬数据,但再也没批量公开发布了
    反正从那时开始我就禁止后台程序员使用外显有序 id 了

    @akira 说的是对的,其实不要想什么算法,因为读取的次数比写入多得多,在写入时产生一个唯一用于外显的 uid 则可,读取时用算法判定会严重增加机器负担
    est
        28
    est  
    OP
       2015-11-12 12:30:26 +08:00   ❤️ 1
    @undeflife 用户 id 只是举个例子。有很多时候会用到 id 的。
    @imn1 其实还有一招。就是不封,也不返回任何错误,直接随机产生垃圾内容。。。让你区分不出来内容是否真实的。。。我抓 bing 就遇到这问题了。。。。。。。嗯。。。。。。。。

    即便是外显 uid ,也是需要防止爬虫呀。。。这个时候除非做 hash ,否则依然也是有问题的。
    est
        29
    est  
    OP
       2015-11-12 12:32:33 +08:00
    @wy315700

    仔细看了下

    The UUID_SHORT() return value is constructed this way:

    (server_id & 255) << 56
    + (server_startup_time_in_seconds << 24)
    + incremented_variable++;

    感觉也算一个不明显的自增 id 的思路吧。。。。
    est
        30
    est  
    OP
       2015-11-12 12:35:50 +08:00
    @oott123 好东西。不过 ruby 有个更好的,基于 Perfect hash function 的 https://github.com/namick/obfuscate_id
    imn1
        31
    imn1  
       2015-11-12 12:37:45 +08:00
    @est
    我没说不防啊,只是你别只穿着内衣上地铁,然后举个牌子“我可以骚,你不可扰”,这算什么?
    livelazily
        32
    livelazily  
       2015-11-12 12:42:25 +08:00
    跳过所有质数
    oott123
        33
    oott123  
       2015-11-12 12:46:59 +08:00
    @est 恕我愚昧,没发现哪里“更好”了。
    lerry
        34
    lerry  
       2015-11-12 12:52:25 +08:00
    twor2
        35
    twor2  
       2015-11-12 13:04:53 +08:00
    uuid 只是视觉上的长,其实还好,网址都是点进去的,就算是 1234 ,也不会有人敲进去吧
    liboyue
        36
    liboyue  
       2015-11-12 13:05:51 +08:00
    用一个表呗。把顺序的内部 ID 映射到随机 ID 上,多一次查表的操作
    XiaoxiaoPu
        37
    XiaoxiaoPu  
       2015-11-12 13:10:58 +08:00
    把自增 ID 用 RSA 加密(或其他加密算法),加密后的结果给用户,用户传过来的 ID 解密,不知道是否可行
    est
        38
    est  
    OP
       2015-11-12 13:19:56 +08:00
    @livelazily 2333 好办法!
    ts
        39
    ts  
       2015-11-12 13:20:24 +08:00
    @est 有中文类别的坛子或者网站吗 求大神的 bookmark .....
    millken
        40
    millken  
       2015-11-12 13:36:02 +08:00   ❤️ 2
    est
        41
    est  
    OP
       2015-11-12 13:40:32 +08:00
    @ts 网址我都背熟了。 newsmth.net/mainpage.html 纯手打。
    wingoo
        42
    wingoo  
       2015-11-12 13:41:51 +08:00
    base62
    映射规则自己指定下
    hooopo
        43
    hooopo  
       2015-11-12 13:45:21 +08:00
    内部设置一个私有函数产生黑洞 ID...
    ooh
        44
    ooh  
       2015-11-12 13:47:50 +08:00
    没有任何实际意义
    windows98
        45
    windows98  
       2015-11-12 13:58:15 +08:00   ❤️ 1
    看帖子的同时,瞟了一眼这个帖子的 url...
    ibireme
        46
    ibireme  
       2015-11-12 14:02:16 +08:00
    用 MongoDB 的 ID 生成办法就不错啊~
    est
        47
    est  
    OP
       2015-11-12 14:10:01 +08:00
    @hooopo 就是这个意思。。。那么,大家有没有什么好的私有函数建议呢。要无状态,速度快,很难逆向出的。。。这个帖子 /t/233004 被 @binux 弄一个一元四次方程就给破了。。。。。。。。。。
    dong3580
        48
    dong3580  
       2015-11-12 14:40:21 +08:00
    @est
    没用,我不信你作出站无目录列表,

    @XiaoxiaoPu
    这样变化的 id ,你让搜索引擎怎么收录?
    est
        49
    est  
    OP
       2015-11-12 15:06:10 +08:00
    @dong3580 没看懂。什么没用?
    Felldeadbird
        50
    Felldeadbird  
       2015-11-12 15:23:40 +08:00
    如果是闭源的话,直接 参数+自增 ID+参数。 混淆进去就行了。
    如: order_id =1 ;那么访问的 URL 地址为:
    /xxx.io?order=6544678198786782 拆分为三部分: 6544678 、 1 、 98786782 。
    fork3rt
        51
    fork3rt  
       2015-11-12 15:58:22 +08:00
    好无聊的方法。。。
    zdhxiong
        52
    zdhxiong  
       2015-11-12 16:03:33 +08:00
    @est 不管你的网址弄得多复杂,一个 wget 命令都能把你的整个站镜像下来。
    xierch
        53
    xierch  
       2015-11-12 16:10:06 +08:00
    next_id = hmac(last_id, key) % MAX_GAP + 1
    xierch
        54
    xierch  
       2015-11-12 16:10:46 +08:00
    哦,不是 =,是 +=
    xierch
        55
    xierch  
       2015-11-12 16:11:44 +08:00
    next_id = last_id + 1 + hmac(last_id, key) % MAX_GAP
    est
        56
    est  
    OP
       2015-11-12 16:25:05 +08:00
    @zdhxiong 你再想想呢?比如我 margin-left: -10000px 一个 div 里弄一个陷阱链接,你访问就直接给你永久返回垃圾数据。镜像下来然后呢?
    huobazi
        57
    huobazi  
       2015-11-12 16:27:56 +08:00
    snowflake
    est
        58
    est  
    OP
       2015-11-12 16:31:50 +08:00
    iambic
        59
    iambic  
       2015-11-12 16:32:44 +08:00
    感觉是不是就是一个 integer hashing 问题啊

    这个链接 https://gist.github.com/badboy/6267743 应该有用。另外 redis 源码里, dict.c 用的 Thomas Wang 的 hashing function 应该也可以参考下
    clino
        60
    clino  
       2015-11-12 16:34:11 +08:00
    取当前时间
    typcn
        61
    typcn  
       2015-11-12 16:37:51 +08:00
    @est 搜索引擎蜘蛛也会这样的。。
    est
        62
    est  
    OP
       2015-11-12 16:39:14 +08:00
    @iambic 这也是个好思路。
    est
        63
    est  
    OP
       2015-11-12 16:40:03 +08:00
    @typcn 增加一行 UA+IP 地址段判断,不拉黑就是了。
    hooopo
        64
    hooopo  
       2015-11-12 16:50:03 +08:00
    @est 无规律的随机点就可以,加一个 hole 表...里面随机填一些 id 就好吧
    est
        65
    est  
    OP
       2015-11-12 16:53:28 +08:00
    @hooopo 感觉通过算法算出来的好好玩一点。。
    jsq2627
        66
    jsq2627  
       2015-11-12 17:20:24 +08:00
    UUID/GUID 当然是最好的。只不过长的比较丑,对用户不友好。不过还是可以避免,我们最近在做的一个系统,用户注册后就必须得输入一个五位代码做 URL 定位部分。
    FrankFang128
        67
    FrankFang128  
       2015-11-12 17:32:09 +08:00
    ban 有什么用,很快就发现你的规律了。
    haython
        68
    haython  
       2015-11-12 17:35:41 +08:00
    用进制转换啊, 10 进制转换成 36 进制, 10 数字加 26 个字母,把顺序打乱
    xierch
        69
    xierch  
       2015-11-12 18:23:39 +08:00
    不如 ID 还是按顺序自增,但是提供给用户的时候,在末尾加一位数用作校验?
    效果应该类似于,每两个 ID 之间平均间隔 10 个黑洞
    校验值可以用任意算法加密 ID ,然后 mod 10 得到..

    推广一下,可以用 id * N + (encrypt(id) % N) 得到带校验码的 id_v
    服务器收到 id_v 以后,用 id_v / N 得到原 ID ,
    然后用 id_v % N == encrypt(id, key) % N 判断是否正确。
    ( N >= 2 ,越大掺的洞越多(
    dallaslu
        70
    dallaslu  
       2015-11-12 20:06:53 +08:00
    1. 使用自增 ID ,记为 A ;
    2. 生成 UUID ,去掉前面的 0 ,再取前 X 位按 16 进制转为 10 进制的值,记为 B ;
    3. 计算 X 位 16 进制数字的 10 进制值的数字个数(如 4 位 16 进制数字 FFFF 10 进制值为 65535 ,数字个数为 5 ),记为 C ;
    4. 在 B 的前面补 0 ,以使长度等于 C ,将其记为 D ;
    5. 把 A 和 D 拼接起来,用做外显 ID 。

    比如设 X 为 4 , A 为 235554 , UUID 为 3FE4C8B33C ...,则:

    B = 0x3FE4 = 16356 ;
    C = 5 ;
    D = 16356 ;
    ID = 23555416356

    遇到 404 就可以判为爬虫了。话又说回来,自增 ID 后面拼上定长的随机数不就好了么!
    dallaslu
        71
    dallaslu  
       2015-11-12 20:10:40 +08:00
    当然了,如果还有校验的需求,可以用 md5(id+salt) 来代替 UUID ,参与 12345 运算……
    hansnow
        72
    hansnow  
       2015-11-12 20:17:47 +08:00
    @est 其实我想说,你是唯一一个我在 D 版和 v2 同时看到的活跃 id
    ctftemp
        73
    ctftemp  
       2015-11-12 20:17:51 +08:00 via Android
    id 加盐 hash 一下,拿这个 hash 值作为判断依据就比较难预测了吧?
    pepsin
        74
    pepsin  
       2015-11-12 20:20:48 +08:00
    @hansnow 你可能看漏我了
    hansnow
        75
    hansnow  
       2015-11-12 20:22:19 +08:00
    @pepsin 哈哈哈,看你头像就想起来了
    Hipponensis
        76
    Hipponensis  
       2015-11-12 20:59:42 +08:00
    LS 许多思路学习了,但总觉得防遍历有点舍本逐末,不如 ID 还是按顺序自增,然后下陷阱,某些 id 对应的页面用户无法访问,让爬虫进去,然后封禁对应 ip 。以上爬虫新手瞎想的。
    varrily
        77
    varrily  
       2015-11-12 23:47:16 +08:00
    我在用 base58(uuid) 长度 22 位左右。比直接 uuid 短不少。
    msg7086
        78
    msg7086  
       2015-11-12 23:48:51 +08:00
    @haython http://ra.gg/!iMD4o 以前还做过类似的东西其实。

    @xierch 其实信用卡、身份证,都是基于这个机制。
    abscon
        79
    abscon  
       2015-11-13 08:41:50 +08:00 via iPhone
    @twor2 我就敲网址。有时候这是最方便的
    holyghost
        80
    holyghost  
       2015-11-13 09:26:47 +08:00 via iPhone   ❤️ 1
    有开源的 hashids 不用。。。
    xiaogui
        81
    xiaogui  
       2015-11-13 12:18:22 +08:00
    @holyghost hashids 看着好棒,哈哈。
    xiaogui
        82
    xiaogui  
       2015-11-13 12:24:37 +08:00
    不过非连续 ID 并不能完全阻止被爬虫。
    est
        83
    est  
    OP
       2015-11-13 13:00:13 +08:00
    @xiaogui 为什么?
    xiaogui
        84
    xiaogui  
       2015-11-13 13:38:37 +08:00
    @est 只要你把内容显示给用户,就不能保证爬虫不会抓取,只不过可能会让爬虫麻烦点而已。
    un
        85
    un  
       2015-11-20 10:31:59 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1340 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 17:34 · PVG 01:34 · LAX 10:34 · JFK 13:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.