V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
ethusdt
0.01D
V2EX  ›  程序员

需要防护一下 nginx 被人爬么?有哪些工具推荐?

  •  1
     
  •   ethusdt ·
    FaiChou · 12 天前 · 5171 次点击

    nginx 反代了很多服务,域名在 cf 上托管这,可以用 cf 的 WAF 规则来屏蔽机器人或者限制国家地区的访问,以及可以限制 10 秒内访问次数( ratelimit )。

    但是你的服务器还会开放 80/443 端口,看 nginx 的 access.log 日志发现仍然有一些爬虫去扫目录,基本上是扫你 .env 或者一些敏感数据,99% 的访问都是 404 ,因为没有这些文件。虽然不怕被扫,但还是有点担心万一哪天忽略了什么东西放进里面,很容易被扫到。

    对于 ssh 可以用 fail2ban-client 这种工具防止登录,会拉黑(使用 iptables)失败登录的 ip 。

    但 nginx 呢?判断 UA 么?但伪造浏览器 UA 太容易了,甚至发现有人扫 nginx 时候竟然使用 UA 打广告(类似于在你日志里留下 xxx 到此一游)。

    第 1 条附言  ·  11 天前
    ## 最简单的策略(29L ryd994)

    ```
    server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 default_server;
    listen [::]:443 default_server;
    ssl_certificate /etc/nginx/ssl/fake1.crt;
    ssl_certificate_key /etc/nginx/ssl/fake1.key;
    server_name _;
    return 444;
    }
    ```

    这样通过 ip 直接访问的 nginx 服务都会被关闭连接。

    ## 最严格的策略(16L scegg)

    服务器不开放 80/443 端口,使用 cf tunnel 来反代你的服务。

    ## 白名单模式(1L dzdh)

    开放白名单模式,nginx 只允许 cf ip 进来。

    ## 折腾一点的模式(4L evill)

    使用 fail2ban 的策略正则匹配一些爬虫,封禁其 ip 。由于黑客也可以篡改 header X-Forwarded-For ,所以也需要搭配白名单模式,仅信任来自 cf ip 的 header(set_real_ip_from)。
    39 条回复    2025-12-01 18:20:20 +08:00
    dzdh
        1
    dzdh  
       12 天前   ❤️ 5
    https://www.cloudflare.com/zh-cn/ips/
    https://api.cloudflare.com/client/v4/ips
    https://github.com/cevin/cloudflare-ip-sync

    nginx 只允许这些 ip 访问就行了。

    还有一种类似 cf 的 5 秒盾 https://anubis.techaro.lol/docs/
    git.kernel.org 都在用
    pckillers
        2
    pckillers  
       12 天前
    改 80 443 默认端口为其他非常规端口+ ip 直接访问返回空页面 能挡掉大量机器人扫描。
    如果还是不行就只能订阅 waf 的 ip 列表然后只给 waf 开白名单了
    ethusdt
        3
    ethusdt  
    OP
       12 天前
    @pckillers 返回空白页没用呀,还是被扫。现在 80 端口返回的就是空白页。
    evill
        4
    evill  
       12 天前   ❤️ 3
    fail2ban-client 可以根据 nginx 日志拉黑
    这是我的一些配置
    # nginx-sensitive.conf
    [Definition]
    failregex = <HOST> -.*"(GET|POST).*\.env(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*/(_|\.)?env(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*/\.git/(HEAD|config|index|objects|info)[^ ]*(\?|$)? HTTP/.*"
    <HOST> -.*"(GET|POST).*/\.ht(access|passwd)(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*(config\.ya?ml|config\.json)(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*(composer\.(json|lock))(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*wp-config\.php(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*(database|dump)\.sql(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*(backup|db)\.(zip|tar|gz)(\?|$) HTTP/.*"
    <HOST> -.*"(GET|POST).*\.(bak|old|swp|~)(\?|$) HTTP/.*"
    # nginx-404.conf
    [Definition]
    failregex = ^<HOST> -.*"(GET|POST).*" 404


    # nginx-404.conf
    [Definition]
    failregex = ^<HOST> -.*"(GET|POST).*" 404
    ignoreregex =root@tx-beijing:/etc/fail2ban/filter.d# cat nginx-bad-request.conf
    # Fail2Ban filter to match bad requests to nginx

    [Definition]
    # The request often doesn't contain a method, only some encoded garbage
    # This will also match requests that are entirely empty
    failregex = ^<HOST> - \S+ \[\] "[^"]*" 400

    datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
    ^[^\[]*\[({DATE})
    {^LN-BEG}

    journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx


    基本规则就是访问敏感文件直接永封,连续 404 或 400 关 7 天
    killva4624
        5
    killva4624  
       12 天前
    fail2ban 也可以用来拦截扫描异常 HTTP 路径访问的 IP ,是一个通用工具,不限于 SSH
    spritecn
        6
    spritecn  
       12 天前
    我也有 1 楼的想法,既然走了 cf 就直接点
    importmeta
        7
    importmeta  
       12 天前
    # . files
    location ~ /\.(?!well-known) {
    deny all;
    }
    songco
        8
    songco  
       12 天前 via Android
    返回 zip 炸弹之内的可行吗?哈哈
    WoneFrank
        10
    WoneFrank  
       12 天前
    不对外服务的可以加个 basic auth
    julyclyde
        11
    julyclyde  
       12 天前
    你已经用了 cdn 还用 iptables ?
    benjaminliangcom
        12
    benjaminliangcom  
       12 天前
    有 CF WAF 也够了
    ethusdt
        13
    ethusdt  
    OP
       12 天前
    @benjaminliangcom 只能防域名爬虫,但防不了对 vps ip 直连的请求。
    hero0earth
        14
    hero0earth  
       12 天前
    过 2 周,我就把 WAF 开源了,相信能解决你的问题👍
    studyingss
        15
    studyingss  
       12 天前   ❤️ 1
    1 楼的回复基本就是标准答案了,op 自动无视是有什么顾虑么
    scegg
        16
    scegg  
       12 天前
    既然用了 cf ,可以不要给 nginx 开 80/443 给公网了。用 cf tunnel 吧。
    sslyxhz
        17
    sslyxhz  
       12 天前
    加一层 waf ,雷池, samwaf 之类的
    anjing01
        18
    anjing01  
       12 天前
    梳理日志,用 ipset 过滤 ip 地址
    ethusdt
        19
    ethusdt  
    OP
       12 天前
    @studyingss 我找到方案了。用 1 楼给的 CF ip 列表(set_real_ip_from+X-Forwarded-For),然后搭配 fail2ban 规则(4 楼)。
    lizhenda
        20
    lizhenda  
       12 天前
    @scegg 好想法,大多数人都是只用 CF 的 DNS + 开启代理,cf tunnel 生产环境用靠谱么?
    abc0123xyz
        21
    abc0123xyz  
       12 天前
    只允许 cf ip 和自己的 ip 访问。
    其他 ip 直接断掉请求,啥都不返回。甚至可以用 cf 给的哪个 15 年证书。
    abc0123xyz
        22
    abc0123xyz  
       12 天前
    @lizhenda #20

    要考虑 cpu 占用
    scegg
        23
    scegg  
       12 天前
    @lizhenda 至少我用了一段时间还没遇到问题。

    经验:
    ( 1 )由于 tunnel 直接负责连接了,所以本地 nginx 都不用 https 的边缘证书,直接走 http 就行。
    ( 2 )如果你的服务端本身有 http 服务,那 nginx 都不用了。
    ( 3 )如果用了 docker ,可以把 tunnel 也放在同一个网络内,直接在 cf 通过 container 的名字去访问服务就好。
    malusama
        24
    malusama  
       12 天前   ❤️ 1
    我遇到奇怪的爬虫是我挂了一大堆的 jav 直接暴露在公网我想让 115 同步过去, 然后 google 给我视频全部下了, 他一个搜索引擎下我视频文件干嘛? 他想索引啥。。你索引你收录地址得了,你下载干嘛
    unclemcz
        25
    unclemcz  
       12 天前 via Android
    @malusama 可能是其他爬虫伪造的 Google ua
    malusama
        26
    malusama  
       12 天前
    @unclemcz 当时他遵守 robots.txt 我添加他就不爬了。。。
    nvksie
        27
    nvksie  
       12 天前 via iPhone
    如果不想让别人访问,cf 用 tunnel 和 access 。
    效果就是需要用邮箱验证码登陆才能访问,ssh 也可以走这个 access 从 web 登陆,vps 就可以 drop 所有公网入站 tcp 了,有问题从云厂商的串口或者预备一个 wireguard 隧道登陆救援
    xyz5378
        28
    xyz5378  
       12 天前
    @hero0earth 为什么不是今天
    ryd994
        29
    ryd994  
       12 天前 via Android   ❤️ 1
    添加一个 default_server ,return 444
    会直接关闭连接。
    @malusama #24 生成略缩图啊
    ethusdt
        30
    ethusdt  
    OP
       12 天前 via iPhone
    @ryd994 这个牛
    cj323
        31
    cj323  
       11 天前
    我是在云提供商上仅在本机( 0.0.0.0/32 )放行 443/22 ,然后通过高位端口的代理口后入本机。这样只有走后门才可以访问 443 。这样达到 L3 级别的隔离,绝对清净,access log ,last 不管什么时候看都只有自己(别人能进那就真后门了哈哈)。
    Explr
        32
    Explr  
       11 天前 via Android
    cf 回源可以带客户端证书,在 nginx 上配置双向 TLS 认证,只允许 cf 回源就好了
    lemonda
        33
    lemonda  
       11 天前
    Nginx 设置频率限制
    超过限制 Fail2ban 把 IP 加入 Cloudflare IP list
    对于 List 中的 IP ,Cloudflare 设定 Managed Challenge
    过了半年多,我已经完全想不起来这套的缺陷在哪儿了,好像是不能动态加载白名单,有但是在一个小项目上工作良好我就没管了。
    ```
    # 1.1.1.1 测试版
    #!/bin/bash
    IP=$1
    TIME=$(date '+%Y-%m-%d %H:%M:%S')

    if [ "$IP" = "1.1.1.1" ]; then
    # 测试用数据
    INFO='{
    "ip": "1.1.1.1",
    "country_name": "Test Country",
    "asn": "AS13335",
    "org": "Test Organization"
    }'
    else
    INFO=$(curl -s "https://ipapi.co/$IP/json/")
    fi

    COUNTRY=$(echo $INFO | jq -r '.country_name')
    ORG=$(echo $INFO | jq -r '.org')
    ASN=$(echo $INFO | jq -r '.asn')

    # Log and mail separately
    {
    echo "$TIME | IP: $IP | Country: $COUNTRY | ASN: $ASN | Org: $ORG" >> /var/log/fail2ban-ip.log
    echo "IP: $IP has been banned at $TIME
    Country: $COUNTRY
    ASN: $ASN
    Organization: $ORG" | mail -s "IP Banned Alert" [email protected]
    } &> /dev/null

    # Output clean JSON for jq processing
    echo "{\"ip\":\"$IP\",\"time\":\"$TIME\",\"country\":\"$COUNTRY\",\"asn\":\"$ASN\",\"org\":\"$ORG\"}"



    # 创建 Fail2ban 动作配置文件 /etc/fail2ban/action.d/cloudflare-ip-list.conf
    [Definition]
    actionstart =
    actionstop =
    actioncheck =

    actionban = IP="<ip>"; \
    INFO=$(/usr/local/bin/ip-info.sh <ip>); \
    TIME=$(echo $INFO | jq -r '.time'); \
    COUNTRY=$(echo $INFO | jq -r '.country'); \
    ASN=$(echo $INFO | jq -r '.asn'); \
    ORG=$(echo $INFO | jq -r '.org'); \
    curl -X POST "https://api.cloudflare.com/client/v4/accounts/<your_account_id>/rules/lists/<your_list_id>/items" \
    -H "Authorization: Bearer <your_api_token>" \
    -H "Content-Type: application/json" \
    --data "[{\"ip\": \"$IP\", \"comment\": \"Banned at $TIME | Country: $COUNTRY | ASN: $ASN | Org: $ORG\"}]"

    # 移除 actionunban ,这样 IP 就不会被自动解封
    actionunban = IP="<ip>"; \
    ITEM_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/<your_account_id>/rules/lists/<your_list_id>/items" \
    -H "Authorization: Bearer <your_api_token>" \
    -H "Content-Type: application/json" | jq -r --arg IP "$IP" '.result[] | select(.ip == $IP) | .id'); \
    if [ ! -z "$ITEM_ID" ]; then \
    curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/<your_account_id>/rules/lists/<your_list_id>/items" \
    -H "Authorization: Bearer <your_api_token>" \
    -H "Content-Type: application/json" \
    --data "{\"items\":[{\"id\":\"$ITEM_ID\"}]}"; \
    fi

    [Init]
    ```
    hero0earth
        34
    hero0earth  
       11 天前
    @xyz5378 那是因为还没有开源版,今天剥离出来一个版本 https://github.com/Flmelody/open-website-defender.git 😂
    beck8
        35
    beck8  
       11 天前
    https://miniodocs.cc/ 我直接关闭了所有的访问,cf 的 reboot 也没有启用,有很多 ai bot 在爬,现在这个快被 AI 学习成官方网站了
    0x676e67
        36
    0x676e67  
       10 天前
    检测是否使用代理也是一种防御手段吧,原理就是利用 RTT 延迟,这里有个例子: https://github.com/YCCDSZXH/proxy-checker-rs
    AkinoKaedeChan
        37
    AkinoKaedeChan  
       10 天前 via iPhone
    配置回源 mTLS ,源服务器强制 mTLS 校验就好了
    jarao
        38
    jarao  
       9 天前
    ssl_reject_handshake on;
    server_tokens off;
    默认服务配上这两个,连证书信息、nginx 版本都别暴露
    FrankAdler
        39
    FrankAdler  
       9 天前 via Android
    我写了个脚本,从 404 日志里过滤 ip 然后 iptable 封禁,这种 404 都是大量尝试不存在的资源,一定有问题
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   4429 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 01:06 · PVG 09:06 · LAX 17:06 · JFK 20:06
    ♥ Do have faith in what you're doing.