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

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

  •  1
     
  •   ethusdt ·
    FaiChou · 1 天前 · 3297 次点击

    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 条附言  ·  12 小时 4 分钟前
    ## 最简单的策略(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)。
    34 条回复    2025-11-29 18:26:57 +08:00
    dzdh
        1
    dzdh  
       1 天前   ❤️ 4
    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  
       1 天前
    改 80 443 默认端口为其他非常规端口+ ip 直接访问返回空页面 能挡掉大量机器人扫描。
    如果还是不行就只能订阅 waf 的 ip 列表然后只给 waf 开白名单了
    ethusdt
        3
    ethusdt  
    OP
       1 天前
    @pckillers 返回空白页没用呀,还是被扫。现在 80 端口返回的就是空白页。
    evill
        4
    evill  
       1 天前   ❤️ 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  
       1 天前
    fail2ban 也可以用来拦截扫描异常 HTTP 路径访问的 IP ,是一个通用工具,不限于 SSH
    spritecn
        6
    spritecn  
       1 天前
    我也有 1 楼的想法,既然走了 cf 就直接点
    importmeta
        7
    importmeta  
       1 天前
    # . files
    location ~ /\.(?!well-known) {
    deny all;
    }
    songco
        8
    songco  
       1 天前 via Android
    返回 zip 炸弹之内的可行吗?哈哈
    WoneFrank
        10
    WoneFrank  
       1 天前
    不对外服务的可以加个 basic auth
    julyclyde
        11
    julyclyde  
       1 天前
    你已经用了 cdn 还用 iptables ?
    benjaminliangcom
        12
    benjaminliangcom  
       1 天前
    有 CF WAF 也够了
    ethusdt
        13
    ethusdt  
    OP
       1 天前
    @benjaminliangcom 只能防域名爬虫,但防不了对 vps ip 直连的请求。
    hero0earth
        14
    hero0earth  
       1 天前
    过 2 周,我就把 WAF 开源了,相信能解决你的问题👍
    studyingss
        15
    studyingss  
       1 天前   ❤️ 1
    1 楼的回复基本就是标准答案了,op 自动无视是有什么顾虑么
    scegg
        16
    scegg  
       1 天前
    既然用了 cf ,可以不要给 nginx 开 80/443 给公网了。用 cf tunnel 吧。
    sslyxhz
        17
    sslyxhz  
       1 天前
    加一层 waf ,雷池, samwaf 之类的
    anjing01
        18
    anjing01  
       1 天前
    梳理日志,用 ipset 过滤 ip 地址
    ethusdt
        19
    ethusdt  
    OP
       1 天前
    @studyingss 我找到方案了。用 1 楼给的 CF ip 列表(set_real_ip_from+X-Forwarded-For),然后搭配 fail2ban 规则(4 楼)。
    lizhenda
        20
    lizhenda  
       1 天前
    @scegg 好想法,大多数人都是只用 CF 的 DNS + 开启代理,cf tunnel 生产环境用靠谱么?
    abc0123xyz
        21
    abc0123xyz  
       1 天前
    只允许 cf ip 和自己的 ip 访问。
    其他 ip 直接断掉请求,啥都不返回。甚至可以用 cf 给的哪个 15 年证书。
    abc0123xyz
        22
    abc0123xyz  
       1 天前
    @lizhenda #20

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

    经验:
    ( 1 )由于 tunnel 直接负责连接了,所以本地 nginx 都不用 https 的边缘证书,直接走 http 就行。
    ( 2 )如果你的服务端本身有 http 服务,那 nginx 都不用了。
    ( 3 )如果用了 docker ,可以把 tunnel 也放在同一个网络内,直接在 cf 通过 container 的名字去访问服务就好。
    malusama
        24
    malusama  
       1 天前   ❤️ 1
    我遇到奇怪的爬虫是我挂了一大堆的 jav 直接暴露在公网我想让 115 同步过去, 然后 google 给我视频全部下了, 他一个搜索引擎下我视频文件干嘛? 他想索引啥。。你索引你收录地址得了,你下载干嘛
    unclemcz
        25
    unclemcz  
       1 天前 via Android
    @malusama 可能是其他爬虫伪造的 Google ua
    malusama
        26
    malusama  
       1 天前
    @unclemcz 当时他遵守 robots.txt 我添加他就不爬了。。。
    nvksie
        27
    nvksie  
       1 天前 via iPhone
    如果不想让别人访问,cf 用 tunnel 和 access 。
    效果就是需要用邮箱验证码登陆才能访问,ssh 也可以走这个 access 从 web 登陆,vps 就可以 drop 所有公网入站 tcp 了,有问题从云厂商的串口或者预备一个 wireguard 隧道登陆救援
    xyz5378
        28
    xyz5378  
       1 天前
    @hero0earth 为什么不是今天
    ryd994
        29
    ryd994  
       18 小时 37 分钟前 via Android   ❤️ 1
    添加一个 default_server ,return 444
    会直接关闭连接。
    @malusama #24 生成略缩图啊
    ethusdt
        30
    ethusdt  
    OP
       13 小时 56 分钟前 via iPhone
    @ryd994 这个牛
    cj323
        31
    cj323  
       10 小时 21 分钟前
    我是在云提供商上仅在本机( 0.0.0.0/32 )放行 443/22 ,然后通过高位端口的代理口后入本机。这样只有走后门才可以访问 443 。这样达到 L3 级别的隔离,绝对清净,access log ,last 不管什么时候看都只有自己(别人能进那就真后门了哈哈)。
    Explr
        32
    Explr  
       7 小时 50 分钟前 via Android
    cf 回源可以带客户端证书,在 nginx 上配置双向 TLS 认证,只允许 cf 回源就好了
    lemonda
        33
    lemonda  
       3 小时 49 分钟前
    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  
       3 小时 28 分钟前
    @xyz5378 那是因为还没有开源版,今天剥离出来一个版本 https://github.com/Flmelody/open-website-defender.git 😂
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2632 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 13:55 · PVG 21:55 · LAX 05:55 · JFK 08:55
    ♥ Do have faith in what you're doing.