Ngày 21/5, Anh triệu tập nhà ngoại giao Iran vì cáo buộc tấn công ủy nhiệm ở châu Âu. Proxy attacks — một thuật ngữ địa chính trị, nhưng trong thế giới smart contract, nó cũng là cơn ác mộng thường trực. Từ proxy pattern trong EVM cho đến delegatecall, khái niệm 'ủy nhiệm' trong code đã gây ra những vụ mất hàng trăm triệu USD. Và lần này, tôi không nói về chính trị, tôi nói về một lỗi logic mà tôi từng mổ xẻ trong hợp đồng upgradeable của một dự án DeFi mới ra mắt.
EVM không tha thứ cho sự cẩu thả. Nhưng trước hết, hãy hiểu bối cảnh. Proxy contract cho phép dev nâng cấp logic mà không thay đổi địa chỉ contract, giữ nguyên trạng thái và số dư. Có ba pattern chính: Transparent Proxy, UUPS (Universal Upgradeable Proxy Standard), và Beacon Proxy. UUPS hiện được khuyến khích vì nhẹ hơn, nhưng nó đặt trách nhiệm kiểm soát nâng cấp vào tay implementation — tức là nếu bạn viết sai logic trong hàm _authorizeUpgrade, bất kỳ ai cũng có thể chiếm quyền.
Tôi đã từng audit một contract UUPS cho một giao thức farming. Whitepaper mô tả hoa mỹ về cơ chế 'multi-sig quản trị', nhưng khi đọc code, tôi phát hiện: _authorizeUpgrade chỉ kiểm tra msg.sender == owner(), mà owner lại là một EOA có thể bị compromised. Tệ hơn, không có độ trễ thời gian (timelock). Một kẻ tấn công chiếm được private key của owner, chỉ cần một transaction là có thể nâng cấp contract lên implementation độc hại (ví dụ: drain tất cả token từ balanceOf). Tôi đã viết PoC trong 30 phút. Đó là lúc tôi nhận ra: proxy pattern trao cho dev sức mạnh vô hạn, nhưng cũng trao cho hacker cánh cửa mở rộng nếu bạn không khóa chặt.
ICO 2018: lỗi đa ký, bài học nhức nhối. Hồi đó, tôi phát hiện lỗi trong hợp đồng vesting của Oyster Protocol. Lỗi đa ký không phải proxy, nhưng bản chất giống nhau: một cơ chế vốn để phân quyền lại trở thành lỗ hổng do thiếu kiểm tra điều kiện. Với proxy, vấn đề càng nghiêm trọng hơn vì nó ảnh hưởng đến toàn bộ storage của contract. Hãy nhìn vào vụ hack Parity multi-sig (2017): lỗi không phải proxy, nhưng là delegatecall tới một library bị xóa. Điểm chung: sự tin tưởng mù quáng vào cơ chế 'ủy nhiệm'.
Phân tích kỹ thuật cốt lõi: Hãy xét một implementation đơn giản dùng UUPS. Trong hàm upgradeToAndCall, implementation gọi _authorizeUpgrade(newImplementation) — nếu hàm này rỗng hoặc chỉ kiểm tra tx.origin == owner, bạn đã mở cửa. Attacker có thể deploy implementation độc, gọi selfdestruct để xóa contract gốc, hoặc đơn giản là set owner = attacker. Đây là attack vector phổ biến nhất trong các cuộc tấn công upgradeable. CVE-2021-39138 (OpenZeppelin UUPS) là một ví dụ: _authorizeUpgrade không kiểm tra msg.sender khi initializer chưa chạy, cho phép ai đó upgrade contract ngay sau khi deploy. Mặc dù OpenZeppelin đã fix, nhưng hàng nghìn contract fork từ code cũ vẫn còn tồn tại.
Hãy so sánh với hooks trong Uniswap V4. Hooks cho phép dev can thiệp vào từng bước của pool — đó là một dạng 'ủy nhiệm' khác. Nhưng V4 có cơ chế beforeSwap và afterSwap, nếu hook trả về giá trị sai, pool có thể bị drain. Mức độ phức tạp tăng vọt, như tôi từng nói: Hooks của Uniswap V4 biến DEX thành Lego lập trình được, nhưng mức độ phức tạp tăng vọt sẽ làm 90% developer nản lòng. Và nơi có độ phức tạp, có lỗi.
Contrarian angle: Nhiều người cho rằng proxy là giải pháp tốt nhất cho upgradeability. Nhưng ngược lại, nếu bạn không cần nâng cấp thường xuyên, immutable contract an toàn hơn nhiều. Proxy đưa vào một lớp tin cậy (trust) mà lẽ ra blockchain cố gắng loại bỏ. Tôi đã thấy những dự án dùng proxy chỉ vì 'biết đâu sau này cần' — và kết quả là họ đã tạo ra một attack surface không cần thiết. Các giao thức như Uniswap, Compound vẫn chạy ổn với immutable contract. Lựa chọn proxy nên là một quyết định kiến trúc có suy tính, không phải mặc định.
Takeaway: Proxy attack vector sẽ không biến mất. Khi giá token tăng, hacker sẽ săn lùng các upgradeable contract với access control yếu. Nếu bạn đang deploy một upgradeable system, hãy audit kỹ _authorizeUpgrade, thêm timelock, và cân nhắc immutable alternative. ICO 2018: lỗi đa ký, bài học nhức nhối. EVM không tha thứ cho sự cẩu thả. Hãy nhớ: mỗi lần bạn thêm một proxy, bạn đang mời kẻ tấn công vào nhà.