惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
Secure Thoughts
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
AWS News Blog
AWS News Blog
美团技术团队
G
Google Developers Blog
宝玉的分享
宝玉的分享
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
InfoQ
小众软件
小众软件
Google DeepMind News
Google DeepMind News
P
Privacy & Cybersecurity Law Blog
Stack Overflow Blog
Stack Overflow Blog
Webroot Blog
Webroot Blog
D
DataBreaches.Net
IT之家
IT之家
PCI Perspectives
PCI Perspectives
人人都是产品经理
人人都是产品经理
Hacker News: Ask HN
Hacker News: Ask HN
L
LangChain Blog
SecWiki News
SecWiki News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cisco Blogs
T
Threatpost
P
Proofpoint News Feed
Y
Y Combinator Blog
Cloudbric
Cloudbric
T
Tor Project blog
量子位
博客园_首页
B
Blog
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
D
Darknet – Hacking Tools, Hacker News & Cyber Security

博客园 - 若-飞

企业AI Agent落地的核心逻辑与路径 基于langchain,Function Call的成功率怎么解决? LangChain Checkpoint(检查点)是什么?—— Agent 的"存档机制" RAG 设计:Embedding 如何切分 AI 客服系统设计:RAG 知识库设计 Go 百万连接服务器设计:从网卡到业务的全链路解析 深度解析 sync.Pool:从设计哲学到生产实践 Goroutine 泄漏:原因、检测与防范 Go Channel 关闭与超时机制完全指南 Go Map 无限增长问题解决方案 LangChain 聊天记录压缩:原理、机制与实战 揭开 sklearn 文本分类的核心原理:从词袋到逻辑回归 大模型“胡说八道”怎么办?一张图读懂检测、评估与修复全方案 企业级AI知识库权限隔离设计:让AI“懂规矩”比“懂知识”更重要 RAG系统设计全解析:从架构到多模态的核心知识图谱 RAG召回率提升全攻略:7大核心方法让检索更精准 RAG召回率提升秘籍:Metadata过滤的底层原理与实践 构建更好的RAG系统:深入理解混合搜索 一文搞懂 RAG 中 Retriever 和 Reranker 的区别 一文搞懂 RAG 的召回率(Recall)是什么? LangChain / LangGraph、MCP、Harness Engineer 与 Claude Code 的对应关系 Agent Harness 技术笔记:从 Trajectory 到 Function Calling Loop BLEU 是什么?——从原理到工程实践 一文讲清:Approve / Permit / Permit2 的本质区别 分库分表后跨分页查询的完整方案 ai如何处理私有数据 ai幻觉是啥,以及如何解决 别再让大模型“凭空瞎猜”了!带你认识AI最强外挂:ChromaDB 用 useQuery 管请求:TanStack React Query 入门小结 HD钱包--BIP44 TRON 四种 API 面怎么选:从节点协议到 JSON-RPC 再到 TronGrid 以太坊节点存储与共识机制全解析 BSC节点发现协议全解析:UDP发现、Bootnode引导与Gossip交易广播 以太坊节点发现背后的分布式哈希表(DHT)与 Kademlia 原理解析 Solidity中的bytes与string:深入理解这两种特殊的动态数组 TDengine CLI (taos) 使用指南 —— Docker 本地开发实战 在 macOS 上用 DBeaver 连接 TDengine:踩坑总结与最终配置指南 Solidity Storage Slot 深度解析 Geth Snapshot Export/Import 深度解析: 不是备份工具,而是数据分析利器 基于BSC 公链的数据备份与 Snapshot 机制深度解析 Docker 共享内存完全指南:从原理到实践,避免常见的理解误区 Docker容器"僵尸状态"问题排查与自动重启方案 SSE协议深度解析:被低估的HTTP服务器推送标准 TDengine vs MySQL:时序数据处理的时代之选 Proxmox 启用 QEMU Guest Agent 实战指南 解决 Blockscout "batch too large" 错误的完整指南 一文讲清楚什么是基准测试(Benchmark) Rust中的宏(Macro):编译时的代码生成魔法 Docker优雅关闭的艺术:为什么stop_grace_period能防止数据丢失 为什么 Go 没有依赖注入和 Bean 机制?语言设计哲学对比
智能合约自毁:当资产还在,合约死了 —— 深度解析 selfdestruct 导致的资产锁定风险
若-飞 · 2026-02-12 · via 博客园 - 若-飞

最残酷的安全漏洞不是偷走你的钱,而是把钱锁在你永远打不开的保险柜里,让你每天看着它,却无能为力。


一、颠覆认知:自毁 ≠ 消失

1.1 绝大多数人的误解

当我第一次接触 selfdestruct 时,我以为它意味着:

✅ ~~合约从区块链上彻底删除~~
✅ ~~所有资金自动退还给用户~~
✅ ~~合约地址变成空地址~~

全错。

1.2 自毁的真相

contract SelfDestructDemo {
    uint256 public value = 100;
    address public owner;
    
    constructor() {
        owner = msg.sender;
    }
    
    function kill() external {
        require(msg.sender == owner);
        selfdestruct(payable(owner));  // 💀 自毁
    }
}

// 自毁前:
value = 100, 合约有 10 ETH

// 调用 kill() 后:
// 1. 10 ETH → 转给 owner ✅
// 2. value = 0 (存储清空) ✅
// 3. 字节码:仍然在链上! ❌ 不是预期
// 4. 再次调用 value():返回 0,不是报错! ❌ 不是预期
// 5. 再次调用 kill():交易成功,无事发生! ❌ 不是预期

自毁的精确后果:

关键认知:自毁后的合约没有消失,它变成了一个“植物人”——有心跳(可调用),没意识(无效果)。


二、两种代理,两种命运

2.1 UUPS:自毁 = 永久死刑

// UUPS 架构:升级函数在逻辑合约中
contract UUPSLogic is UUPSUpgradeable {
    address public owner;
    mapping(address => uint256) public balances;  // 假设锁了 1000 万 USDT
    
    function withdraw() external {
        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;
        // 转账逻辑...
    }
    
    // ❌ 危险函数:看起来无害,实则致命
    function emergencyStop() external onlyOwner {
        selfdestruct(payable(owner));  // 💀 自毁!
    }
    
    // 升级函数在逻辑合约中!
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

// 攻击或误操作后:
// 1. 逻辑合约自毁 → 代码标记为“已销毁”
// 2. 代理合约仍然指向这个“植物人”逻辑合约
// 3. 用户调用 withdraw() → ✅ 交易成功,但钱纹丝不动
// 4. 管理员调用 upgradeTo() → ✅ 交易成功,但 implementation 槽没变!
// 5. 💀 永久瘫痪,资产永久锁定

为什么无法升级?

// UUPS 升级需要 delegatecall 到逻辑合约
proxy.upgradeTo(newImpl);
// → proxy delegatecall 到 LogicV1
// → LogicV1 已自毁,代码是空操作
// → _setImplementation(newImpl) 永远不会执行
// → implementation 槽永远指向死掉的 LogicV1
// → 死循环,永远困住!

2.2 透明代理:自毁 = 虚惊一场

// 透明代理架构:升级函数在代理合约中
contract TransparentProxy {
    address implementation;  // 指向逻辑合约
    address admin;
    
    // 🛡️ 升级函数在代理中,不依赖逻辑合约!
    function upgradeTo(address newImpl) external onlyAdmin {
        implementation = newImpl;
    }
    
    fallback() external payable {
        if (msg.sender == admin) {
            // 管理函数在代理中执行
        } else {
            _delegate(implementation);  // 业务转发
        }
    }
}

// 即使逻辑合约自毁:
// 1. 代理合约依然健在
// 2. admin 调用 upgradeTo → ✅ 成功修改 implementation
// 3. 指向新逻辑合约 → ✅ 业务恢复
// 4. 资产安全,服务恢复!

// 后果:短暂瘫痪,可恢复

2.3 命运对决


三、资产锁定:看得见,取不出

3.1 什么是资产锁定?

// 假设这是一个锁定了 1000 万 USDT 的 UUPS 合约
// 自毁发生后:

// 1. 资金还在代理合约里
const balance = await USDT.balanceOf(proxy.address);
console.log(balance); // 10,000,000 USDT ✅ 钱还在!

// 2. 但所有业务函数都失效了
await proxy.withdraw(); 
// ✅ 交易成功!
// ❌ USDT 余额纹丝不动!
// ❌ 状态变量没变化!
// ❌ 什么都没发生!

// 3. 无法升级
await proxy.upgradeTo(newImpl);
// ✅ 交易成功!
// ❌ implementation 槽没变!
// ❌ 依然指向死掉的逻辑合约!

// 4. 无法直接转出
await USDT.transfer(owner, balance);
// 需要从代理合约调用,但代理合约的业务逻辑死了
// owner 根本无法操作代理合约里的 USDT!

// 5. 无法在 EVM 层面强制转出
// ERC20 标准没有提供“强制提取”功能
// 除非有私钥,否则无解

这就是资产锁定:钱在眼前,永远取不出。

3.2 真实案例:Parity 多签钱包

时间:2017年11月
损失:约15亿美元 ETH(当时价值)
结局:至今仍锁在合约里,8年无法取出

技术真相

// Parity 钱包库合约(简化)
contract WalletLibrary {
    // ❌ 致命错误:public 函数,任何人都能调用!
    function kill(address _to) public {
        selfdestruct(_to);  // 自毁
    }
}

// 攻击者调用 kill()
// 库合约自毁 → 所有依赖这个库的钱包合约失去业务逻辑
// 每个钱包合约里锁着数万到数百万 ETH
// 资金还在,但永远取不出来

这不是“被盗”,这是“被判了无期徒刑”。


四、资产锁定 vs 资产被盗

4.1 本质区别

4.2 痛苦对比

// 场景 A:资产被盗
// 用户视角:虽然难受,但还有希望
"我的钱被黑客转走了,FBI在追,交易所冻结了地址..."

// 场景 B:资产锁定(UUPS自毁)
// 用户视角:每天看着余额,无能为力
"钱还在合约里,我随时能查到余额,但就是取不出来...
 项目方说无法升级,无法恢复,只能等奇迹...
 一年过去了,两年过去了,余额还是那个数字..."

哪个更残忍?


五、为什么开发者会写出 selfdestruct?

5.1 历史包袱

// 2015-2017 年的“最佳实践”
contract MyContract {
    function kill() external onlyOwner {
        selfdestruct(payable(owner));  // 当时认为这是“优雅退出”
    }
}

5.2 迁移思维的误导

// 传统开发者的直觉
contract OldSystem {
    function delete() external {  // 传统数据库可以删除
        this.cleanup();
        this.destroy();
    }
}

// 移植到区块链的错误类比
contract NewContract {
    function migrate() external onlyOwner {
        selfdestruct(payable(newVersion));  // ❌ 这不是迁移,是自杀!
    }
}

5.3 对 UUPS 的不理解

// 开发者以为:
"我只是销毁逻辑合约,代理合约还能升级吧?"

// 残酷真相:
"UUPS 的升级函数在逻辑合约里,逻辑合约死了,升级这条路就永远堵死了。"

六、安全规范:如何避免资产锁定?

✅ 黄金法则(UUPS 专用)

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract SafeContract is UUPSUpgradeable, OwnableUpgradeable {
    
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();  // 法则1:禁止逻辑合约初始化
    }
    
    function initialize() initializer public {
        __Ownable_init();
        __UUPSUpgradeable_init();
    }
    
    // 法则2:必须重写升级授权函数
    function _authorizeUpgrade(address) internal override onlyOwner {}
    
    // ❌ 法则3:绝不写 selfdestruct
    // function kill() external onlyOwner { selfdestruct(...); }  // 禁止!
    
    // ❌ 法则4:绝不写不可信的 delegatecall
    // function execute(address target) external { target.delegatecall(...); }  // 禁止!
    
    // ✅ 法则5:用 pause 替代自毁
    bool public isEmergency;
    function pause() external onlyOwner {
        isEmergency = true;
    }
    function unpause() external onlyOwner {
        isEmergency = false;
    }
    modifier whenNotPaused() {
        require(!isEmergency, "Paused");
        _;
    }
}

🔴 高危信号(立即阻止上线)

// 以下任何一行出现在 UUPS 逻辑合约中 = 一票否决
selfdestruct(payable(...));
suicide(...);  // 旧版本别名
assembly { selfdestruct(...) }
delegatecall(gas(), target, ...)  // 如果 target 可控

七、如果已经发生了,怎么办?

7.1 透明代理 ✅ 有救

// 立即升级到新的逻辑合约
await proxy.connect(admin).upgradeTo(newImplAddress);
// 业务恢复,资产安全

7.2 UUPS ❌ 没救

// 残酷的答案:目前没有任何可靠方案
// 1. 不能升级(upgradeTo 已死)
// 2. 不能直接转出 ERC20(没有私钥操作)
// 3. 不能硬分叉(除非整个社区为你改规则)

// 唯一理论上的可能(几乎不可行):
// - 找到逻辑合约自毁前的 storage 证明
// - 通过复杂的 Merkle 证明逐个提取
// - 成本极高,技术极难,从未成功过

接受现实,当作昂贵的安全课。


八、终极总结:资产锁定三定律

第一定律

自毁不会让合约消失,只会让它变成植物人。

第二定律

透明代理的自毁是感冒,UUPS 的自毁是癌症。

第三定律

资产锁定比资产被盗更痛苦——被盗还有希望追回,锁定是永恒的绝望。


写在最后

这是区块链世界最残酷的 Bug 之一:
它不是让黑客暴富,而是让普通用户每天看着自己的余额,却永远取不出来。

记住:在 UUPS 合约中,selfdestruct 不是退出机制,是集体陪葬。


本文基于以太坊 EVM 架构(2026年),Solidity 0.8.x,OpenZeppelin 合约库。安全建议请以官方最新文档为准。


如果你从这篇文章只记住一句话,请记住:

在 UUPS 逻辑合约里写 selfdestruct,等于亲手给用户的资产判了无期徒刑。