V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ChristopherWu
V2EX  ›  推广

从限流谈到伪造 IP(nginx remote_addr)

  •  
  •   ChristopherWu · 2019-03-11 14:01:30 +08:00 · 7589 次点击
    这是一个创建于 1866 天前的主题,其中的信息可能已经有所发展或是发生改变。

    来自我的公众号 『 YongHao 写东西的 Cache 』 打个小广告,还是希望写的东西有人看🙊

    分享一下见解,权当抛砖引玉


    从限流谈到伪造 IP ( nginx remote_addr )

    remote_addr

    很多流量大的网站会限流,比如一秒 1000 次访问即视为非法,会阻止 10 分钟的访问。

    通常简单的做法,就是通过 nginx 时,nginx 设置

        proxy_set_header X-Real-IP $remote_addr;
    

    nginx 的 $remote_addr代表客户端的访问 ip,把它设到 http 请求的头部 X-Real-IP ;然后程序取出并存入数据库,统计访问次数。

    remote_addr 基本上不能被伪造,因为是直接从 TCP 连接信息中获取的,也就是 netstatForeign Address那栏。

    你想想, 客户端 A 与 B 服务器建立 TCP 连接,是不是 B 肯定知道 A 的公网地址是什么呢,除非客户端 A 是经过了一个代理服务器 Z, 那么就是 A -> Z -> B, 服务器 B 拿到的只能是 Z 的 ip 地址了,但这不意味就是伪造 ip,限流依然有效。

    nginx 转发

    上述应对外网访问,没有任何问题。假如公司内部需要测试,不停的访问服务器上的程序时,并且经过负载均衡或者 nginx 转发时,也就是 client -> nginx1 -> nginx2 -> server, remote_addr 就变成了 nginx2 的内网地址了。

    因此,需要在 nginx1处,e client 的 remote_addr, 再传给 nginx2,server 再取出。

    示例:

       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    

    $proxy_add_x_forwarded_for$remote_addr 加到 X-Forwarded-For 头部后面;最后设在 my_ips

    如果是需要做 ip 统计,地理信息获取,天气定位等,需要常用的另一个 http 头部, X-Forwarded-For 来做处理。通过名字就知道,X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1 ( RFC 2616 )协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239( Forwarded HTTP Extension )标准之中。1

    然后 my_ips 就代表了请求从 client 到 server 的完整 ip 路径, 只要由后往前推,直到 找到 外网的 ip,就证明这就是 client 的真正 ip。

         bb_real_ip = request.environ.get('my_ips')
         bb_real_ip = bb_real_ip.replace(" ", "").split(',')
         for ip in reversed(bb_real_ip):
             if not is_private(ip):
                 return ip
    
         # 不可能出现这种情况, 除非 LBS/nginx 没有设$proxy_add_x_forwarded_for
         return request.environ.get('REMOTE_ADDR')
    

    这样取到的也是真实的 ip 地址,但有一个问题,假如 client 跟其他很多 client 通过同一个出口出来,共享一个外网 ip,那么如何获取它的 ip 呢?

    这个情况下,限流一样可以生效,最多就是稍微误杀下无辜,影响不大。

    伪造 ip (也就是 remote_addr )

    那么,有办法伪造 remote_addr吗? 其实本质就是,TCP 连接中,有办法伪造 ip 信息吗?

    请看如何用 hping3 工具发出伪装 ip 的包到 google.com

    $ sudo apt-get install hping3
    $ sudo hping3 --icmp --spoof 6.6.6.6 baidu.com
    HPING baidu.com (eth0 220.181.57.216): icmp mode set, 28 headers + 0 data bytes
    

    另一个控制台抓 icmp 包:

    $ sudo tcpdump -i eth0 'icmp'
    21:24:58.562844 IP 6.6.6.6 > 220.181.57.216: ICMP echo request, id 11035, seq 5120, length 8
    

    可以看到, 我们成功的伪装成 6.6.6.6 并向 baidu.com 发出了 ping (也就是 ICMP 包),不过由于我们的 IP 实质上并不是6.6.6.6,所以收不到 baidu.com 发往 它的 ICMP 包。

    模拟的原理是,自己重新实现系统的 tcp ( ICMP )协议栈,然后 自己改变自己的 ip。

    值得一提的八卦是,hping 的作者是 Salvatore Sanfilippo,同时他也是 redis 的作者。

    试想一下,可以通过这个办法来做借刀杀人——伪造一个 ip (如4.4.4.4),大量发包给第三方(如 Google ),然后第三方返回 TCP reset 或者 ICMP unreachable 给 你伪造的 ip(4.4.4.4), 这样就可以借 Google 来 ddos 4.4.4.4了。2

    但是现在运营商的路由器都部署了 uRPF,可以根据你发过来的源 ip 检测是否在路由表中,不在就拒绝掉此请求。

    30 条回复    2019-03-12 09:38:03 +08:00
    hxsf
        1
    hxsf  
       2019-03-11 14:07:30 +08:00
    1. TCP 有握手
    2. 不能放大的攻击有啥用? 第三方 D 对面的流量 === 你 D 第三方的流量。
    tabris17
        2
    tabris17  
       2019-03-11 14:09:12 +08:00   ❤️ 1
    9012 年了,还有这种伪科学文章啊
    rochek
        3
    rochek  
       2019-03-11 14:10:41 +08:00 via Android   ❤️ 1
    过去,一般打机器的时候是做跳板的。
    后来,跳板容易被反追,有人用伪造 ip,再中途拿包转发这个妖路。

    可以了解一下。
    ChristopherWu
        4
    ChristopherWu  
    OP
       2019-03-11 14:14:40 +08:00
    yexm0
        5
    yexm0  
       2019-03-11 14:16:59 +08:00 via Android
    @Livid 公众号推广
    ChristopherWu
        6
    ChristopherWu  
    OP
       2019-03-11 14:22:10 +08:00
    @rochek 已感谢。 自己中途拿包转发,是自己容许伪造 ip 的请求过来对吧?那么效果如何,可行吗?
    ChristopherWu
        7
    ChristopherWu  
    OP
       2019-03-11 14:23:31 +08:00
    @yexm0 v 站里不允许宣传公众号吗,还是不允许宣传推广?

    我还见过每个帖子都带上自己博客的。。还有一堆嗮博客的帖子,那些属于博客推广吗?
    ChristopherWu
        8
    ChristopherWu  
    OP
       2019-03-11 14:24:32 +08:00
    @tabris17 还是很不爽你的说法,请您说出哪里伪科学了。。。
    rochek
        9
    rochek  
       2019-03-11 14:25:55 +08:00 via Android
    @ChristopherWu 特定环境下的小聪明
    tabris17
        10
    tabris17  
       2019-03-11 14:31:34 +08:00
    @ChristopherWu

    1. 伪造 IP 源的数据包会被 ISP 的路由器丢弃
    2. 想要实现 TCP 的三次盲握手必须能猜解出对方的 IP 数据包的序列号,那你还不如直接猜出对方数据库密码呢

    标题搞个噱头,通篇文章毫无意义
    oott123
        11
    oott123  
       2019-03-11 14:54:16 +08:00
    这种文章不提一下 nginx 的 set_real_ip_from 指令吗?
    j0hnj
        12
    j0hnj  
       2019-03-11 15:03:20 +08:00
    @tabris17 #2 我也想知道怎么就伪科学了
    ChristopherWu
        13
    ChristopherWu  
    OP
       2019-03-11 15:03:36 +08:00   ❤️ 1
    @tabris17 你再看看我的标题? 注意是『谈到』 。。。不是做到,而且我文章也提及是做不到以及说明了原因。

    所以你的『标题搞个噱头』从何而来?接下来的『通篇文章毫无意义』更是毫无根据了。

    对你没什么用的,但是在我的角度来看,比起配置啥啥,如何安装,算法题解等重复的文章有用了一点。
    www5070504
        14
    www5070504  
       2019-03-11 15:10:15 +08:00
    抛砖引玉 这个真的是砖 感觉真没啥用。。。这个连握手第一步都过不去的吧。。
    gstqc
        15
    gstqc  
       2019-03-11 15:15:36 +08:00 via Android
    楼主是真的抛砖
    1134506391
        16
    1134506391  
       2019-03-11 15:28:47 +08:00
    <script>alert("aa")</script>
    goodryb
        17
    goodryb  
       2019-03-11 15:51:37 +08:00
    估计楼主也就是顺手一抄,楼上不要太较真
    VoidChen
        18
    VoidChen  
       2019-03-11 15:52:44 +08:00
    对我我这种外行来说还是能受到一点启发的
    ChristopherWu
        19
    ChristopherWu  
    OP
       2019-03-11 16:00:40 +08:00
    @www5070504

    注意文中最后一句,『但是现在运营商的路由器都部署了 uRPF,可以根据你发过来的源 ip 检测是否在路由表中,不在就拒绝掉此请求。』。

    回复前,尤其是负面性评论时,能否先认真看看呢?。。
    ryd994
        20
    ryd994  
       2019-03-11 16:07:50 +08:00 via Android
    要是真听了你的就出事了,连猜 TCP 都不用,直接伪造 x-forwarded-for。
    所以必须设置 set_real_ip_from,同时前端反代必须覆盖这个 header 而不是按照默认配置拼接。

    然后你这个攻击思路一看就是拍脑袋。1.没有放大系数,其实完全没有必要反射。直接带宽上去打死就行了。没必要纠结协议。只要能路由到对方端口,都是一样的。
    2. RST 怎么就能 DDoS 了?且不说人家前面套 CDN,CDN 都是硬件清洗。就算靠自己软防火墙抗,一条 conntrack 规则就可以把这些全部 drop 掉了,不会对 CPU 构成压力。要么你占满带宽打死,那还用什么 RST ?见 1,直接暴力打就是了。
    ChristopherWu
        21
    ChristopherWu  
    OP
       2019-03-11 16:20:14 +08:00
    @ryd994

    关于`proxy_add_x_forwarded_for`, 你怎么伪造都好啊,与我无关,我取的只是最后的 `$remote_addr`,你与前端建立连接时,`$remote_addr`就几乎不可能伪造。

    > $proxy_add_x_forwarded_for
    > the “ X-Forwarded-For ” client request header field with the $remote_addr variable appended to it, separated by a comma.
    请看 http://nginx.org/en/docs/http/ngx_http_proxy_module.html


    关于攻击思路,请看 https://www.wikiwand.com/en/Denial-of-service_attack#/Backscatter,写的时候忘记放进去文章了
    ChristopherWu
        22
    ChristopherWu  
    OP
       2019-03-11 16:21:55 +08:00
    ryd994
        23
    ryd994  
       2019-03-11 17:31:03 +08:00   ❤️ 4
    @ChristopherWu
    “ the “ X-Forwarded-For ” client request header field with the $remote_addr variable appended to it, separated by a comma.“
    这就是我说的拼接。正确做法是在前端 proxy_set_header X-Forwarded-For $remote_addr; 在后端 set_real_ip_from。
    按你的做法,到应用内再处理,不仅开销大,而且 nginx 的 access_log 就完全没意义了。Nginx 明明做好的你不用,还要自己造轮子。

    "backscatter is a side-effect of a spoofed denial-of-service attack ”
    你英文不及格。这是副作用而不是攻击手段和主要目的。用你的例子来讲,被攻击的是 Google 的服务器,只不过 Google 会以为是另一个 IP 在 syn flood 它。但是实际上都是有硬件清洗的。硬件先和你握手,三次握手成功之后再去和服务器握手。syn flood 很难完成三次握手。除非你破解了对方的 seq 生成随机算法。对于没有足够熵源的服务器来说,这种攻击从协议上来讲是有机会的。后来的 CPU 都有熵发生器了,这种攻击也就彻底不可行了。

    写安全类文章前,不如自己搭个服务器,去 hostloc 挑衅一下,看看自己是怎么死的,再教别人。恕我直言,我认为你没有运维过一个公开服务的 nginx http 反代,很可能连服务器管理的经验也没有。
    ChristopherWu
        24
    ChristopherWu  
    OP
       2019-03-11 18:03:49 +08:00
    @ryd994 谢谢你的回复,非常有帮助。
    我确实之前不知道`set_real_ip_from`这个模块,我稍候再想想怎么改对目前的程序。

    > 恕我直言,我认为你没有运维过一个公开服务的 nginx http 反代,很可能连服务器管理的经验也没有。
    实不相瞒,我有。。而且就是这样子做的。。

    另外,写文章就是总结下自己学到的东西啊,我不可能知道很多对的事情应该怎么做,身边说不定也没有人知道,所以像现在贴上来,不就收到了你的建议了吗 - = -
    zeraba
        25
    zeraba  
       2019-03-11 19:04:20 +08:00 via Android   ❤️ 1
    就和 ryd994 说的一样,多层其实设置好 header 后用 realip 模块可以搞定,另外,针对同一个出口的策略也需要调整了,可以自己生成 id 作为用户识别,毕竟移动网络下,这种情况很常见,还有可以深入写下 ng 多层代理后的 header 情况
    ChristopherWu
        26
    ChristopherWu  
    OP
       2019-03-11 19:24:58 +08:00
    @zeraba
    已感谢。 请问『针对同一个出口的策略也需要调整了,可以自己生成 id 作为用户识别』可以细讲一下吗?

    『有可以深入写下 ng 多层代理后的 header 情况』这个是指啥意思呢?有哪些 header 需要关注?
    zeraba
        27
    zeraba  
       2019-03-11 20:51:31 +08:00 via Android
    @ChristopherWu 你现在限流是针对 ip 其实可以通过 cookie lua 可以 set cookie 如果项目不是基于 cookie 比如小程序之类的 可以在 url 设置某个参数标识用户 再限流,至于多层,举个例子,X-Forwarded-For 这个变量就可以展开,一层是什么情况,内网多层是什么情况,如何排除中间的 ip 其他变量当然也可以深入,可以拓宽一点理解整个 proxy 模块
    msg7086
        28
    msg7086  
       2019-03-12 01:04:56 +08:00
    我刚打开的时候就觉得这写得都什么玩意儿,谈 nginx 不谈 real_ip 那还谈什么。9012 年了还谈 forwarded-for 伪造。
    翻了下回复,看来我不是第一个这么觉得的。

    > 实不相瞒,我有。。而且就是这样子做的。。
    建议找个懂的来做运维。
    抱歉,我说话比较冲。

    如果自己不确定,那么就开贴直接问。把错误的知识写成科普性文章是很不好的行为。
    freaking
        29
    freaking  
       2019-03-12 09:07:54 +08:00 via Android
    好喷
    Paradisiaercy
        30
    Paradisiaercy  
       2019-03-12 09:38:03 +08:00 via iPhone
    以网络安全角度来看,不知道说了什么。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2855 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 11:21 · PVG 19:21 · LAX 04:21 · JFK 07:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.