Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
基础设施与运维阅读约 12 分钟

Web3 安全:保护链上系统的分层模型

一种实用的 Web3 安全分层方法,涵盖协议、基础设施、智能合约、钱包和人为因素,并提供可运行的诊断和可复现的测量方法。

TL;DR

Web3 安全不是单一产品或检查清单;它是整个系统的分层属性,从共识和节点基础设施到智能合约、钱包和用户行为。本文为您提供这些层次的心智模型、可适配您自身技术栈的可运行诊断流程,以及可复现的方法来衡量您的控制措施是否真正有效。它将有据可查的协议行为、工程建议以及您必须自行运行的测量区分开来。使用它来构建威胁模型、加固您控制的基础设施,并决定下一步投资方向。

范围与直接答案

Web3 安全是降低去中心化系统中未经授权状态变更的概率和影响的做法。由于这些系统跨越共识协议、节点和 RPC 基础设施、智能合约、桥、钱包和人,任何单一控制措施都不足够。实用的答案是分层模型:假设每一层都可能失败,验证您控制的层,并测量结果,而不是信任供应商的声明。

本文涵盖该模型、针对基础设施和合约层的可运行诊断流程、预期的验证信号、故障排除以及每种方法的权衡。它不发布任何提供商的基准数字;在适合测量的地方,它为您提供流程和表格,供您填入自己的结果。

  • 有据可查的行为:共识规则、EVM 语义以及各自规范定义的 RPC 方法契约。
  • 建议:工程实践,如最小权限、独立 RPC 验证和纵深防御。
  • 您必须运行的测量:延迟、错误率、端点间一致性以及您自己环境中的检测覆盖率。

假设与前提条件

以下流程假设您至少可以访问一个 EVM 兼容的 RPC 端点、一个装有 curl 和 Python 3 的 shell,并且有权在您测试的网络上读取合约状态。只读诊断不需要有资金的账户。对于写入路径测试,请使用测试网和一次性密钥。

将机密信息排除在源代码管理之外。使用 RPC_URL 和 PRIVATE_KEY 等环境变量,切勿将生产密钥粘贴到共享终端历史记录中。如果您需要托管端点而不是运行自己的节点,请在做出决定前通过 RPC Assistant 端点指南和网络目录比较选项。

  • 一个主 RPC_URL,最好还有一个独立的 RPC_URL_SECONDARY 用于交叉检查。
  • Python 3.9+ 及 requests 库,或者如果您偏好 JavaScript,则使用 Node.js 18+。
  • 一个测试网账户,余额较少,用于任何交易级别的检查。
  • 对您的合约源代码和部署地址的读取权限。

分层心智模型

将 Web3 安全视为一个堆栈,每一层都有自己的信任假设和故障模式。协议层依赖于共识和密码学。基础设施层依赖于节点、RPC 提供商以及它们之间的网络路径。合约层依赖于代码正确性和经济设计。应用和钱包层依赖于意图如何构建和签名。人为层依赖于操作纪律。

一个有用的规则是,任何一层的妥协都可能使上层保证失效。如果 RPC 端点返回过时或伪造的数据,正确的合约仍然不安全;如果用户签署了恶意负载,加固的 RPC 仍然不安全。这就是为什么单层审计很少能产生持久的安全。

  • 协议:共识、最终性和密码学假设。
  • 基础设施:节点、RPC 端点、DNS、TLS 和托管。
  • 合约:逻辑、访问控制、升级和依赖项。
  • 应用和钱包:交易构建、签名和显示。
  • 人和流程:密钥管理、事件响应和变更控制。

工具之前的威胁建模

在选择工具之前,写下您要保护什么、谁可能影响它以及成功攻击的样子。对于 dApp,资产是用户资金和协议状态;参与者包括用户、运营商、RPC 提供商和外部攻击者。对于基础设施,资产是验证者密钥、RPC 可用性和数据完整性。

轻量级威胁模型足以开始:列出资产、列出信任边界,并针对每个边界记录如果另一方恶意或不可用会发生什么。这项练习通常揭示 RPC 边界和签名边界比团队最初假设的更值得关注。

  • 资产:如果被破坏会失去价值的东西。
  • 信任边界:数据或控制权在各方之间跨越的地方。
  • 故障模式:恶意、不可用或仅仅是不正确。
  • 检测:您如何在几分钟而不是几天内注意到故障。

使用 curl 的可运行基础设施诊断

首先验证您的端点返回一致的链状态并响应标准方法。以下命令使用占位符;将 RPC_URL 替换为您的端点。多次运行每个命令,并比较主端点和辅助端点的结果。

第一个检查确认链身份。第二个确认端点可以读取最近的区块。第三个比较两个端点的区块号以检测延迟。

export RPC_URL="https://your-primary-endpoint"
export RPC_URL_SECONDARY="https://your-secondary-endpoint"

# 1. Chain identity
curl -s -X POST "$RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'

# 2. Latest block
curl -s -X POST "$RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"eth_blockNumber","params":[]}'

# 3. Compare lag between endpoints
for url in "$RPC_URL" "$RPC_URL_SECONDARY"; do
  echo -n "$url -> "
  curl -s -X POST "$url" \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":3,"method":"eth_blockNumber","params":[]}' \
    | python3 -c 'import sys,json; print(int(json.load(sys.stdin)["result"],16))'
done

验证合约状态和字节码

一旦端点对于读取足够可信,验证部署的字节码与您期望的匹配,并且关键状态变量可读。下面的 Python 脚本获取字节码并调用一个简单的视图函数。它仅使用标准 JSON-RPC 方法,因此适用于任何兼容端点。

将 CONTRACT_ADDRESS 和函数选择器替换为您自己部署中的值。预期字节码与返回字节码之间的不匹配是停止并在签署任何交易之前进行调查的强烈信号。

import os, json, requests

RPC_URL = os.environ["RPC_URL"]
CONTRACT = os.environ.get("CONTRACT_ADDRESS", "0xYourContractAddress")

def rpc(method, params):
    r = requests.post(RPC_URL, json={"jsonrpc":"2.0","id":1,"method":method,"params":params}, timeout=10)
    r.raise_for_status()
    body = r.json()
    if "error" in body:
        raise RuntimeError(body["error"])
    return body["result"]

code = rpc("eth_getCode", [CONTRACT, "latest"])
print("bytecode length:", len(code))
print("bytecode prefix:", code[:42])

# Example: call a no-argument view function by selector
selector = "0x18160ddd"  # totalSupply()
result = rpc("eth_call", [{"to": CONTRACT, "data": selector}, "latest"])
print("raw result:", result)
print("decoded uint256:", int(result, 16) if result != "0x" else None)

可复现的测量方法

关于端点可靠性或检测覆盖率的声明只有在您能在自己的环境中复现时才有用。定义固定的测试窗口、固定的方法集和固定的样本量。对每个候选端点或控制措施运行相同的脚本,并将结果记录在您自己填写的表格中。不要复制营销页面上的数字。

一个实用的流程:对于每个端点,以稳定速率对每个方法发出 N 个请求,记录成功、错误代码和延迟,然后在一天中的不同时间重复。对于合约控制,重放一组固定的已知不良输入,并记录每个是否被阻止。下表是一个模板;值必须来自您自己的运行。

  • 方法:eth_chainId、eth_blockNumber、eth_getCode、eth_call、eth_getLogs。
  • 样本量:每个端点每个方法至少 100 个请求。
  • 记录:成功率、中位数和 p95 延迟、错误代码以及与参考端点的区块延迟。
  • 重复:在不同日期至少两个窗口,以暴露一天中不同时间的影响。

结果表模板

用您自己的测量结果填写此表。在获得数据之前将单元格留空;不要估计。| 端点 | 方法 | 请求数 | 成功率 | 中位延迟 (ms) | p95 延迟 (ms) | 最大区块延迟 | 备注 | |---|---|---|---|---|---|---|---| | 主 | eth_blockNumber | | | | | | | | 辅助 | eth_blockNumber | | | | | | | | 主 | eth_call | | | | | | | | 辅助 | eth_call | | | | | | | | 主 | eth_getLogs | | | | | | | | 辅助 | eth_getLogs | | | | | | |

表格的价值不在于单个数字,而在于比较。主端点和辅助端点在区块延迟或错误率上的巨大差距是一个信号,表明需要在它成为事件之前调查路由、速率限制或提供商行为。

预期的验证信号

运行诊断后,您应该看到一小组预期信号。链身份应与您打算使用的网络匹配。区块号应在调用之间递增。字节码应与您的部署记录匹配。视图调用应返回与您自己的索引器或第二个端点一致的值。

如果这些信号中的任何一个缺失,将其视为发现而不是麻烦。链 ID 不匹配意味着您指向了错误的网络。区块号冻结意味着端点过时或请求被错误缓存。字节码不匹配意味着您读取的地址不是您认为的合约。

  • 链 ID 与文档化的网络 ID 匹配。
  • 区块号在重复调用中增加。
  • 字节码前缀与您的部署工件匹配。
  • 视图调用结果在两个独立端点上一致。
  • 错误响应包含 JSON-RPC 错误对象,而不是 HTML 页面。

常见故障排除

大多数基础设施故障属于几类:身份验证和速率限制、网络和 TLS 问题、过时或不一致的数据以及格式错误的请求。首先检查 HTTP 状态和 JSON-RPC 错误对象;许多问题在任何更深入的调试之前就可以在那里看到。

如果您看到 401 或 403,请检查您的 API 密钥是否存在且作用域正确。如果您看到 429,您被速率限制;退避并考虑专用端点。如果您看到超时,请从不同的网络路径测试。如果端点之间的结果不一致,请先比较区块号,然后比较确切的请求负载。

  • 401/403:凭据缺失或无效,或 IP 允许列表不匹配。
  • 429:速率限制;降低并发或迁移到专用节点。
  • 超时:DNS、TLS 或路由问题;使用 curl -v 测试。
  • 数据不一致:区块延迟或缓存;与第二个端点交叉检查。
  • 格式错误的请求:验证 JSON-RPC 版本、方法名称和参数类型。

局限性与权衡

分层安全提高了覆盖率,但增加了运营成本。运行自己的节点减少了对第三方的信任,但增加了维护负担。对每次读取与两个端点进行交叉检查提高了完整性,但使延迟和成本翻倍。审计和形式化验证能捕获测试遗漏的 bug 类别,但它们不覆盖基础设施或人为错误。

正确的平衡取决于风险价值。只读仪表板可以容忍单个托管端点;桥或托管系统则不应如此。明确记录权衡,以便未来的更改不会悄悄削弱出于某种原因选择的控制措施。

  • 自托管节点:最大控制,最高运营开销。
  • 托管 RPC:较低开销,但您继承了提供商的可用性和数据完整性属性。
  • 双端点验证:更强的完整性,更高的成本和复杂性。
  • 审计和形式化方法:对合约逻辑强大,对基础设施和签名视而不见。

后续步骤

将诊断转变为定期检查。安排 curl 和 Python 脚本以固定节奏运行,将结果存储在时间序列表中,并在偏离基线时发出警报。通过审查交易如何构建和向用户显示,将威胁模型扩展到签名路径。

如果您在下一阶段需要托管端点,请从 RPC 端点指南开始,在网络目录中比较网络,并在扩展前查看定价。对于运行生产工作负载的团队,专用节点消除了嘈杂邻居效应,并为您提供了更清晰的测量基线。

  • 自动化测量流程并保持滚动基线。
  • 将签名路径审查添加到您的威胁模型。
  • 使用 RPC Assistant 指南和网络目录有意识地选择端点。
  • 在任何基础设施变更后重新运行完整诊断。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始