入职 Web3 运维日记 · 第 14 日:铸造无形钥匙 —— OIDC 与 CI/CD 施工实录

时间:入职第 14 天,上午 10:00 天气:多云,代码审查室里的气氛有些焦灼 事件:发现开发团队使用个人电脑直连主网部署合约,并深度剖析 Web3 的“草台班子”现状

上午 10 点,智能合约开发组长在 Slack 核心群里发了一条消息:“新版 Vault (资金库) 合约本地测试完毕,10 分钟后我准备把它发到主网 (Mainnet)。”

作为一个 Web2 摸爬滚打出来的老运维,我对“发主网(生产环境)”这三个字有着天然的敬畏。我立刻端着咖啡走到他工位旁,随口问了一句:“咱们发主网的流程是啥?你用的哪个平台的流水线?”

组长头也没抬,切到了他的 VS Code 终端:“流水线?不用那么麻烦。我在我的 Mac 上直接跑 forge script script/Deploy.s.sol --rpc-url $MAINNET_RPC --broadcast 命令就行了。秒上链。”

我眼皮一跳:“那你部署用的私钥 (Private Key) 放在哪?”

他指着编辑器左侧的目录树,理直气壮地说:“写死在本地的 .env 文件里啊。里面这个地址有 5 个 ETH 当手续费,部署完剩下的还能用。你放心,我已经把 .env 加进 .gitignore 了,绝对不会传到 GitHub 上,很安全!”

我感觉血压瞬间飙升。这不叫安全,这叫“裸奔”。

但我忍住了直接拔网线的冲动。我看着他,问出了那个直击灵魂的问题: “兄弟,如果你以前在美团或者阿里,敢把生产环境数据库的 Root 密码写在你这台 Mac 的本地文本里,然后坐在星巴克连着公共 Wi-Fi 敲回车发版,你今天下午就会被安全部和 HR 架出大楼。为什么到了 Web3,你觉得这就理所当然了?

组长愣了一下,挠了挠头:“可是……大家都是这么干的啊。教程里也是这么教的。”

🕵️‍♂️ 为什么 Web2 的“死罪”,变成了 Web3 的“常态”?

我拉了把椅子坐下,跟他(也是跟我自己)彻底理清了这个荒谬现状背后的三大原因:

原因一:物理边界的消亡(公网无墙)

  • Web2 时代:生产环境的数据库藏在 VPC(虚拟私有云)深处,外面有防火墙、WAF、堡垒机。你在星巴克是物理上无法连接生产库的,除非你经过层层 VPN 认证。
  • Web3 时代:以太坊主网是 Permissionless(无许可) 的公共网络。没有任何防火墙。对区块链来说,你坐在公司内网发出的交易,和你坐在星巴克公共 Wi-Fi 下发出的交易,没有任何区别。只要你的签名对得上,节点就会处理。这种“极度开放”的网络环境,给了开发者一种“随时随地都能发版”的错觉和便利。

原因二:“极客英雄主义”的开发工具链

  • Web3 起源于极客和密码朋克文化。早期大部分项目都是两三个人的草台班子在黑客松上搞出来的。
  • 他们没有专业的 DevOps,所以早期的开发框架(Truffle, Hardhat,甚至现在的 Foundry)在设计时,默认的使用场景就是单兵作战
  • 你去翻看官方文档的“部署教程”,第一步永远是:“新建一个 .env 文件,粘贴你的 MetaMask 私钥”。工具层的设计导向,硬生生把整个行业的开发者培养出了极其糟糕的安全习惯。

原因三:身份与资产的高度绑定(Deployer = God)

  • 在 Web2,发版的账号(比如 Jenkins Service Account)和管钱的账号是分开的。
  • 但在 Web3,那个放在 .env 里的私钥,不仅是用来付 Gas 费的,它通常还会被智能合约自动识别为 Owner(超级管理员)
  • 这意味着,这把躺在 Mac 硬盘上的私钥,未来可能直接拥有铸造代币、暂停合约、甚至提取用户几亿美金的最高权限!

🛑 刺破谎言:叫停部署

听我分析完,组长的表情终于变了。但他还在辩解:“但我 Mac 上装了杀毒软件,而且我不乱点链接的。”

我直接按住了他的键盘:“停!这笔部署立刻取消。

“你的 .env 谎言到此为止。”我给他下了最后通牒。

  1. 单点故障 (SPOF):只要你的电脑中了一次钓鱼木马,那个装着 5 个 ETH 和最高权限的私钥瞬间被盗,公司直接上新闻头条。
  2. 代码投毒防不住:你从本地编译部署,我怎么向审计公司证明,你部署的机器码 (Bytecode) 就是 GitHub 上的那份源码?万一你私自在本地改了一行代码,留了个后门呢?
  3. 环境差异:你的 Node.js 版本、甚至 Mac 的 M系列芯片,都可能导致编译出来的 Hash 值和线上验证不一致。

我站起身,走向 CTO 办公室,留下一句话: “从今天起,彻底废除任何形式的本地主网部署。 下午 5 点前,我会交付一套零信任的 CI/CD 流水线。以后所有上主网的合约,必须通过机器自动化部署,私钥必须锁在硬件保险柜(KMS)里!”


🏗️ 施工第一步:在 AWS 建立“门卫大爷” (OIDC 信任策略)

我登录到 AWS IAM 控制台。我要教 AWS 认识 GitHub 这个“外来机构”,并定下死规矩。

1. 添加身份提供商 (IdP) 我先在 AWS 里添加了 GitHub 作为可信的 OpenID Connect (OIDC) 实体:

  • Provider URL: https://token.actions.githubusercontent.com
  • Audience: sts.amazonaws.com

2. 编写信任策略 (Trust Policy - 最核心的防线) 接着,我创建了一个 IAM 角色,命名为 Bybit-GitHub-Deploy-Role。这个角色就是给流水线准备的临时“工牌”。 但这块工牌不是谁都能领的,我给门卫大爷(AWS STS)写了一段极其严苛的 JSON 过滤规则:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::1234567890:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { // 【绝杀卡点】:必须是 Bybit 仓库的 main 分支! // 其他仓库、其他分支(比如 dev)来请求,直接 Access Denied! "token.actions.githubusercontent.com:sub": "repo:Bybit/smart-contracts:ref:refs/heads/main" } } } ] } 

3. 限制行为范围 (Permissions Policy) 领到工牌进门后,流水线能干啥?我只给了它两个极其可怜的权限:看公钥,和请求签名。

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "kms:Sign", // 允许请求签名 "kms:GetPublicKey" // 允许获取公钥地址 ], // 只能碰这一把指定的冷钱包部署私钥,别的资源看都看不见 "Resource": "arn:aws:kms:ap-southeast-1:1234567890:key/mrk-abc123deploykey" } ] } 

📜 施工第二步:编写流水线大脑 (deployment.yml)

AWS 那边的门卫调教好了。现在我回到 GitHub 仓库,在 .github/workflows/ 目录下新建了 deployment.yml

这份 YAML 文件,就是彻底终结那个危险的 .env 文件的最终武器。我把它分成了三个严密的阶段。

name: Compliant Mainnet Deployment # 1. 触发条件:绝对禁止自动发版 on: workflow_dispatch: inputs: network: description: 'Target Network (mainnet / sepolia)' required: true default: 'mainnet' # 2. 赋予流水线申请 OIDC Token 的权限 (极其重要的一句!) permissions: id-token: write # 必须有这个,GitHub 才会生成用来骗过 AWS 门卫的 JWT 证件 contents: read jobs: # 阶段一:无状态编译 build: name: 🏗️ Compile with Foundry runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - name: Build Smart Contracts run: forge build # 阶段二:Slither 死亡安检 (防线) security-audit: name: 🛡️ Slither Security Scan needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run Slither Analyzer uses: crytic/[email protected] with: target: 'src/' fail-on: high # 只要查出高危漏洞(如重入攻击),立刻中断! # 阶段三:无私钥签名部署 deploy: name: 🚀 Sign & Broadcast via KMS needs: security-audit # 只有安全审计全绿,才能走到这里 runs-on: ubuntu-latest environment: production # 触发 GitHub 页面上的审批按钮 (Approve) steps: - uses: actions/checkout@v3 # 魔法时刻:向 AWS 出示 OIDC 证件,换取 15 分钟临时凭证 - name: Authenticate to AWS KMS (No Passwords!) uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: arn:aws:iam::1234567890:role/Bybit-GitHub-Deploy-Role aws-region: ap-southeast-1 # 执行部署脚本 (此时环境里已经有了 AWS 临时凭证) - name: Execute KMS Deployment run: | echo "Connecting to AWS KMS..." # 这里通常是一个封装好的 TS/Python 脚本,调用 KMS API 签名并向 RPC 发送交易 node scripts/deploy_kms.js --network ${{ github.event.inputs.network }} 

最后三步:在 KMS 内部“孕育”私钥

我们不导入私钥,我们命令 AWS KMS 的底层硬件(HSM 防弹芯片)原生生成一把私钥。它从出生的那一刻起,就被焊死在芯片里。

操作步骤如下:

  1. 选择算法: 在 AWS KMS 点击“创建密钥”。选择 非对称 (Asymmetric),用途选 签名和验证 (Sign and verify)
  2. 选择以太坊专用的“灵魂曲线”: 这一步极其关键。以太坊使用的密码学曲线叫 secp256k1。你在 AWS KMS 的下拉菜单里,必须精准地选中 ECC_SECG_P256K1。如果选错了,生成的签名以太坊根本不认。
  3. 完成创建: 点击确定。就这么简单,私钥诞生了。
第四步:发起“权力移交”交易

你需要用你原来的本地旧私钥(就是那个 .env 里的)执行一次合约调用。你可以用 Foundry 的命令行工具 cast 快速完成:

# 命令解释: # cast send [合约地址] "transferOwnership(address)" [新KMS地址] --private-key [旧私钥] --rpc-url [主网RPC] cast send 0xVaultContractAddress "transferOwnership(address)" 0xNew_KMS_Address \ --private-key $OLD_PRIVATE_KEY \ --rpc-url $MAINNET_RPC 
第五步:验证权力移交

去 Etherscan 上查看你的合约,点击 "Read Contract",查看 owner 变量。

  • 如果显示的地址已经是 0xNew_KMS_Address,那么恭喜你,移交成功!

下午 4 点,我把这段 YAML 代码推到了 main 分支。

这时候的架构已经完美了:

  1. 没有密码:无论是本地电脑还是 GitHub 仓库,都没有明文的 Access Key 或私钥。
  2. 双重锁死:开发人员想发主网,必须推代码到 main 分支;推上来之后,必须通过 Slither 的安全扫描;全绿之后,还要等我和 CTO 在网页上点击 Approve。


🚨 16:15 PM:触雷!Slither 的无情绞杀

组长点击了 Run workflow。 我们俩并排站在屏幕前,盯着 GitHub Actions 的运行日志。

  • 第一步:🏗️ Compile with Foundry。用时 12 秒,绿灯 ✅。 组长得意地说:“看吧,我本地编译没问题,云端肯定也没问题。”
  • 第二步:🛡️ Slither Security Scan。日志开始疯狂滚动。

就在这时,页面突然一闪,一个刺眼的红色叉号 (❌) 弹了出来。构建失败!流水线被强制熔断。 第三步的 🚀 Sign & Broadcast via KMS 直接变成了灰色的 Skipped(跳过)。

组长急了:“Alen,你这什么破流水线!我本地跑 forge test 测试用例明明全过了(100% Pass),怎么到你这就挂了?”

我没有说话,点开了 Slither 阶段的红色报错日志。屏幕上赫然印着几行冰冷的英文:

INFO:Detectors: High Risk Vulnerability Detected: Reentrancy Contract: Vault.sol Function: Vault.withdraw(uint256) Details: External call made before state variable update. - msg.sender.call{value: amount}("") (Vault.sol#42) - balances[msg.sender] -= amount (Vault.sol#44) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities Error: Slither found 1 High severity issues! Failing build. 

🧠 16:20 PM:为什么本地测试抓不出这个 Bug?

看到 Reentrancy(重入攻击)这个词,组长的脸色瞬间煞白。 这是以太坊历史上最臭名昭著的漏洞。当年 The DAO 就是因为这个漏洞被黑了 5000 万美金,直接导致了以太坊主网硬分叉出 ETC。

他的代码是这么写的:

function withdraw(uint256 amount) public { require(balances[msg.sender] >= amount, "Not enough balance"); // 【致命错误】:先给钱,把执行权交给了外部! (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); // 然后才扣减数据库里的余额 balances[msg.sender] -= amount; } 

我拍了拍他的肩膀:“兄弟,本地的单元测试(Unit Test)往往只测正常的业务逻辑。你测了存 100 块,取 50 块,余额剩 50 块,当然是绿的。 但黑客不会按套路出牌。黑客会写一个恶意合约,在收到你这笔钱的瞬间(触发 fallback 函数),再次回头调用你的 withdraw 函数。 因为你还没执行到 balances -= amount 这一步,系统认为黑客账户里还有钱,于是又给他发了一笔……无限循环,直到把你资金库里的几千万美金抽干。”

组长擦了擦头上的冷汗:“如果上午我用本地电脑直连主网部署了……明天公司可能就破产了。”

🔧 17:00 PM:修复与二次闯关

组长飞奔回工位,老老实实地重写了逻辑。 他遵循了智能合约最核心的安全铁律:CEI 模式 (Checks-Effects-Interactions)。 先把余额扣掉(Effects),再去给用户转钱(Interactions)。并且引入了 OpenZeppelin 的 nonReentrant 互斥锁。

代码重新 Push,流水线再次触发。 这次,Slither 扫描:绿灯 ✅!

🚀 17:30 PM:核按钮与完美点火 (Environment Approval)

流水线走到了最后一步:🚀 Sign & Broadcast via KMS。 但这步并没有直接运行,而是变成了黄色的等待状态 (Waiting for Review)

CTO 的手机和我电脑上的 Slack 同时响了。

🤖 GitHub Actions 提醒Compliant Mainnet Deployment 正在请求部署到 production 环境。 请管理员审核。

这是我在 YAML 里配置的最后一道防线:环境审批。 我走到 CTO 办公室,他看了一眼 Slither 的全绿报告,我们在各自的 GitHub 页面上郑重地点击了 "Approve and Deploy"

接下来,就是见证 OIDC 与 KMS 密码学魔法的时刻了。

我们在日志里清晰地看到了这一切的发生,而黑客在网线另一端只能绝望地看着:

  1. Authenticating via OIDC... Success. (拿到了只活 15 分钟的 AWS 临时凭证)
  2. Connecting to AWS KMS HSM... (连接防弹保险箱)
  3. Sending bytecode hash to KMS for signing... (把编译好的机器码送进保险箱)
  4. Received signature (v, r, s) from KMS. (印章盖好,拿到了谁也看不懂但全网都认的数字签名)
  5. Broadcasting transaction to Ethereum Mainnet...
  6. Transaction confirmed at Block #18452901! Contract Address: 0x...

部署成功。

至此,那台完成了历史使命的 GitHub 临时 Ubuntu 虚拟机,像完成了刺杀任务的特工一样,瞬间销毁,不留一丝痕迹。

Read more

GLM-4.6V-Flash-WEB Web界面使用指南,拖图就出结果

GLM-4.6V-Flash-WEB Web界面使用指南,拖图就出结果 你不需要配置环境、不用写一行推理代码、甚至不用打开终端——只要把一张截图拖进浏览器窗口,几秒钟后,它就能告诉你图里写了什么、画了什么、哪里有问题。这不是未来预告,而是你现在就能在本地跑起来的真实体验。 GLM-4.6V-Flash-WEB 是智谱AI最新开源的轻量级视觉语言模型,专为Web端实时交互而生。它不像某些“实验室模型”那样只存在于论文和Benchmark表格里,而是真正做到了:部署快、启动快、响应快、上手更快。一块RTX 3090,一个浏览器,一次拖拽,结果即刻呈现。 本文不讲训练原理,不列参数表格,不堆技术术语。我们只聚焦一件事:怎么用好它的Web界面?从零开始,到稳定产出,每一步都清晰可操作。 1. 为什么说“拖图就出结果”不是宣传话术? 很多多模态模型标榜“支持图文理解”,但实际用起来才发现:要装依赖、改路径、调精度、修CUDA版本、

前端防范 XSS(跨站脚本攻击)

目录 一、防范措施 1.layui util  核心转义的特殊字符 示例 2.js-xss.js库 安装 1. Node.js 环境(npm/yarn) 2. 浏览器环境 核心 API 基础使用 1. 基础过滤(默认规则) 2. 自定义过滤规则 (1)允许特定标签 (2)允许特定属性 (3)自定义标签处理 (4)自定义属性处理 (5)转义特定字符 常见场景示例 1. 过滤用户输入的评论内容 2. 允许特定富文本标签(如富文本编辑器内容) 注意事项 更多配置 XSS(跨站脚本攻击)是一种常见的网络攻击手段,它允许攻击者将恶意脚本注入到其他用户的浏览器中。

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

目录 1. 打开浏览器开发者工具 2. 使用 Network 面板 3. 查看具体的API请求 a. Headers b. Payload c. Response d. Preview e. Timing 4. 实际操作步骤 5. 常见问题及解决方法 a. 无法看到API请求 b. 请求失败 c. 跨域问题(CORS) 作为一名后端工程师,理解前端如何调用接口、传递参数以及接收返回值是非常重要的。下面将详细介绍如何通过浏览器开发者工具(F12)查看和分析这些信息,并附带图片案例帮助你更好地理解。 1. 打开浏览器开发者工具 按下 F12 或右键点击页面选择“检查”可以打开浏览器的开发者工具。常用的浏览器如Chrome、Firefox等都内置了开发者工具。下面是我选择我的一篇文章,打开开发者工具进行演示。 2. 使用

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例)

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例) 前端开发中最令人头疼的莫过于那些难以定位的UI问题——元素错位、样式冲突、响应式失效...传统调试方式往往需要反复修改代码、刷新页面、检查元素。现在,通过Cursor编辑器集成的Codex功能,你可以直接用截图交互快速定位和修复这些问题。本文将带你从零开始,掌握这套革命性的调试工作流。 1. 环境准备与基础配置 在开始之前,确保你已经具备以下环境: * Cursor编辑器最新版(v2.5+) * Node.js 18.x及以上版本 * React 18项目(本文以Chakra UI 2.x为例) 首先在Cursor中安装Codex插件: 1. 点击左侧扩展图标 2. 搜索"Codex"并安装 3. 登录你的OpenAI账户(需要ChatGPT Plus订阅) 关键配置项: // 在项目根目录创建.