26.8 万个暴露在公网的 AI 系统:Censys 九个月里看到了什么

·9 分钟阅读·Evergreen Tools Team

Censys 于 2026 年 10 月 14 日发布第五份年度《互联网现状报告》,最扎眼的数字不是某起入侵事件,而是一个增速:面向公网的 AI 工具在九个月里增长 63%,达到近 26.8 万个系统。这个统计口径比聊天机器人宽得多,它把「把模型接进数据、应用和用户账号」的那一层也算了进去——而这恰恰是多数团队上线最快、文档最少的一层。下面讲清这份报告到底测了什么、哪几条发现会改变你正要做的决定,以及哪些部分你这周就能在自己的基础设施上验证一遍。

机房里的机架服务器

面向公网的 AI 工具在九个月里增长了 63%,达到近 26.8 万个系统

这份报告到底测了什么

Censys 高级研究集体(ARC)分析了来自 Censys Internet Map 的历史数据与实时数据,该图谱持续监测全球互联网基础设施,报告于 10 月 14 日发布。被引用最多的结论是:面向公网的 AI 工具在九个月里增长 63%,总量接近 26.8 万个系统。Censys 明确说明,这不仅是聊天界面,而是把 AI 与数据、应用、用户账号连接起来的后台工具。CEO Zakir Durumeric 的表述是:AI 这类新技术带来了新的基础设施层,而持续监测正是让你能用上它、又不背上「无人管理的风险」的前提。真正有用的读法是:这种增速跑赢了文档,而不是某个具体产品坏掉了。

# Run this from OUTSIDE your network, and only against hosts you own.
import urllib.request

MY_HOSTS = ["flow.example.com", "agent.example.com"]

SIGNATURES = {
    "n8n editor login": "/rest/login",
    "n8n public API": "/api/v1/workflows",
    "agent console": "/__console",
}

def probe(host, path, timeout=5):
    url = "https://" + host + path
    try:
        with urllib.request.urlopen(url, timeout=timeout) as resp:
            return resp.status, resp.read(200).decode("utf-8", "ignore")[:80]
    except Exception as exc:
        return None, type(exc).__name__

for host in MY_HOSTS:
    for label, path in SIGNATURES.items():
        status, info = probe(host, path)
        if status and status < 400:
            print(host, "|", label, "answers", status, "from the public internet")

# Censys is scanning these hosts whether or not you are. The only question
# is whether your inventory agrees with what an outsider can see.

n8n 是全球暴露最多的 AI 产品

近 11.4 万台运行 n8n 的主机可以从公网访问,这让这个工作流自动化平台在数据集中成为暴露最多的 AI 产品,且差距很大。面向 Agent 的控制台 OpenClaw 以约 5.4 万台排在后面。三个国家占据了全部暴露 AI 基础设施的略超一半:美国 62,687 台、中国 42,511 台、德国 31,222 台。n8n 占主导的原因是结构性的,而不是谁粗心:它默认自建部署、自带 Web 编辑器、还要接收 webhook,所以总有人为了让外部服务调用它而主动把它暴露出去。之后很少被重新评估的,是这条工作流能触及的一切——每个已连接服务的凭证,以及这条工作流被允许写入的任何东西。

# The automation tool is the one to check first: nearly 114,000 hosts in the report.
# Read the running container's effective config instead of trusting the .env file.
import json, subprocess

def n8n_env(container="n8n"):
    raw = subprocess.run(["docker", "inspect", container, "--format", "{{json .Config.Env}}"],
                         capture_output=True, text=True, check=True).stdout
    return dict(kv.split("=", 1) for kv in json.loads(raw) if "=" in kv)

env = n8n_env()
CHECKS = {
    "bind to loopback or an internal interface": env.get("N8N_LISTEN_ADDRESS", "0.0.0.0") != "0.0.0.0",
    "public API disabled unless used": env.get("N8N_PUBLIC_API_DISABLED", "false") == "true",
    "owner account set up": bool(env.get("N8N_USER_MANAGEMENT_DISABLED", "") == "false"),
    "encryption key pinned, not generated": bool(env.get("N8N_ENCRYPTION_KEY")),
    "external URL reflects the real host": bool(env.get("WEBHOOK_URL")),
}
for name, ok in CHECKS.items():
    print(("PASS  " if ok else "CHECK ") + name)

# Every failed line is a host an attacker can reach and you cannot explain.
屏幕上的安全代码流

暴露的系统里,最容易被忽略的是工作流自动化平台

13.4 万个暴露的工业主机,多数在消费级网络上

报告识别出约 13.4 万台暴露在公网、运行工业控制系统的主机,其中约 70% 出现在消费级与移动网络上。正是这个分布让这个数字难以落地:一台挂在家庭或移动宽带后面的工控设备,既对应不到清晰的负责人,也对应不到维护窗口或安全团队。区域上,北美约占暴露工控资产的 38%,欧洲 32%,亚洲 25%。对于任何拥有 OT 环境的人来说,真正要问的问题比标题窄得多:到底有没有一部分能从公网摸到,以及这条网络路径是不是你以为的那一条。责任归属被打碎,正是这些暴露长期存在的原因,也是「扫一次就够」不成立的原因。

# An exposure ledger is only useful if it is a file with owners, not a wiki page.
import yaml

LEDGER = (
    "services:\n"
    "  - name: n8n-flow\n"
    "    host: flow.example.com\n"
    "    public: true\n"
    "    auth: owner-account + 2FA\n"
    "    network_path: cloudflare-tunnel\n"
    "    update_channel: monthly\n"
    "    owner: platform-team\n"
    "    reviewed: 2026-10-16\n"
    "  - name: agent-console\n"
    "    host: agent.example.com\n"
    "    public: true\n"
    "    auth: null\n"
    "    network_path: cloudflare-tunnel\n"
    "    update_channel: null\n"
    "    owner: null\n"
    "    reviewed: 2026-09-02\n"
)

def audit(entry, today="2026-10-16"):
    problems = []
    if entry["public"] and not entry["auth"]:
        problems.append("public with no auth")
    if entry["public"] and entry["network_path"] == "direct":
        problems.append("public, directly addressed")
    if not entry["owner"]:
        problems.append("no owner")
    if not entry["update_channel"]:
        problems.append("no update channel")
    return problems

doc = yaml.safe_load(LEDGER)
for svc in doc["services"]:
    issues = audit(svc)
    print(svc["name"], "->", issues or "clean")

# A service with no owner and no update channel is the shape of every long-lived exposure.

威胁基础设施的分布并不均匀

Censys 把观测到的威胁基础设施拆成「主机层」和「web 层」两类,而地理位置在这两类之间是翻转的。主机层恶意基础设施(如 C2 服务器与远程访问工具)有 36.6% 落在中国,美国是 16.8%。而 web 层威胁(如钓鱼与欺骗页面)有 45.5% 落在美国,中国是 20.9%。这个切分之所以重要,是因为两半需要完全不同的防御。主机层基础设施会出现在你的出网流量和 DNS 里,过滤与监测在这里有效;web 层威胁直接抵达人,控件是 URL 信誉、邮件过滤和面向用户的告警。把其中一个数字当成全貌,就会保护错方向。

# Industrial protocols have no business being reachable from a consumer network.
# Check your own ranges only: Modbus 502, S7comm 102, EtherNet/IP 44818.
import ipaddress, socket

MY_RANGES = ["10.20.0.0/24"]
INDUSTRIAL = {502: "Modbus", 102: "S7comm", 44818: "EtherNet/IP"}

def reachable(ip, port, timeout=1.5):
    with socket.socket() as sock:
        sock.settimeout(timeout)
        return sock.connect_ex((str(ip), port)) == 0

for cidr in MY_RANGES:
    for ip in ipaddress.ip_network(cidr).hosts():
        for port, name in INDUSTRIAL.items():
            if reachable(ip, port):
                print("exposed upstream:", ip, name, port)

# The report counted roughly 134,000 internet-facing ICS hosts.
# About 70% of them sit on consumer and mobile networks, which is why nobody can name an owner.
相互连接的网络节点

先有外部视角的清单,再谈加固

中位修补水平在提升,最老的版本却原地不动

报告还追踪了四个产品的版本采纳情况:Progress Telerik Report Server、Jenkins、Cleo MFT 与 Microsoft SharePoint Server。在研究周期内,组织确实迁移到了更新的版本,但每个群体里最老的那批版本基本没有变化。这种组合很常见,也很容易误读:中位数在改善,而制造大多数事件的那一小撮根本没动——因为升级大多数很便宜,升级尾部很贵,总得有人做测试,而总有一个理由先放一放。如果你只报平均值,尾部恰恰会在那个本该暴露它的报表里隐身。

# Median patch level improves. The oldest version in the fleet does not.
# Track the tail, because that is the one that gets exploited.
from collections import Counter

INVENTORY = [
    {"product": "jenkins", "host": "ci-1", "version": "2.492"},
    {"product": "jenkins", "host": "ci-2", "version": "2.375"},
    {"product": "sharepoint", "host": "intranet", "version": "16.0.10417"},
    {"product": "telerik-report-server", "host": "reports", "version": "10.1.24.514"},
]

FLOOR = {"jenkins": "2.440", "sharepoint": "16.0.10400", "telerik-report-server": "10.1.24.514"}

def parse(v):
    return tuple(int(p) for p in v.replace("-", ".").split(".") if p.isdigit())

def drift(rows):
    out = []
    for row in rows:
        below = parse(row["version"]) < parse(FLOOR[row["product"]])
        out.append((row["host"], row["product"], row["version"], "BELOW FLOOR" if below else "ok"))
    return out

for line in drift(INVENTORY):
    print(line)

print("version spread:", Counter(r["version"] for r in INVENTORY))

# Censys studied Telerik Report Server, Jenkins, Cleo MFT and SharePoint:
# organisations moved to newer releases, the oldest ones stayed put.

这周可以先做的四步

第一步从外部视角开始清点,而不是从你的服务目录开始——因为目录正是被检验的对象,它一定会和现实对不上。第二步给每一个真正对公网开放的服务加上两样东西:有明确归属人的认证,以及一条经过认证的隧道之类的网络路径,而不是直接暴露地址。第三步为每个产品钉一个受支持的版本下限,并且按「相对下限的偏离」来汇报,而不是平均版本。第四步在清单上写上复查日期,并且每月重跑一遍整套检查,因为这些数字是会动的:报告里的 AI 层九个月涨了 63%,而你的清单不会涨得比它慢。只要清单够短、负责人写清了,上面这些事一次评审会就能过完。

📌 常见问题 FAQ

Censys 2026 年度《互联网现状报告》发现了什么?

面向公网的 AI 工具在九个月里增长 63%,达到近 26.8 万个系统。暴露最多的 AI 产品是 n8n,近 11.4 万台主机;其次是 OpenClaw,约 5.4 万台。报告还统计出约 13.4 万台暴露在公网的工业控制主机,其中约 70% 位于消费级与移动网络上。

暴露了 n8n 是不是说明我已被入侵?

不是。暴露不等于入侵,它意味着编辑器或 API 可以从公网访问,于是认证与网络路径就成了外部访问者和你工作流里的凭证之间唯一的屏障。先把它当成一份清单与访问控制问题来对待。

暴露的 AI 基础设施主要分布在哪些国家?

按 Censys 的数据,美国 62,687 台、中国 42,511 台、德国 31,222 台,三者合计略超过全部暴露 AI 基础设施的一半。

为什么暴露的工控主机值得关注?

因为其中约 70% 落在消费级与移动网络上,责任归属很难确定、整改也找不到明确负责人。区域上,北美约占暴露总量的 38%,欧洲 32%,亚洲 25%。

我应该多久复查一次自己的暴露面?

每月一次是合理的下限;此外,每次新增自建服务、改动隧道或 webhook 路由、轮换凭证时都应复查。九个月增长 63% 这件事的教训是:环境的变化速度比年度评审周期快得多。