- Home
- smart contract hoạt động như thế nào
- Hiểu lỗi Reentrancy là gì trong Smart Contract: Cơ chế tấn công, rủi ro rút tiền lặp cho người mới
Hiểu lỗi Reentrancy là gì trong Smart Contract: Cơ chế tấn công, rủi ro rút tiền lặp cho người mới
Lỗi reentrancy là một lỗ hổng bảo mật trong smart contract, xảy ra khi hợp đồng gọi ra bên ngoài trước khi hoàn tất cập nhật trạng thái nội bộ, từ đó tạo cơ hội để mã độc quay lại thực thi cùng một logic nhiều lần. Với người mới học blockchain, đây là một trong những khái niệm quan trọng nhất để hiểu vì nó chạm trực tiếp vào câu hỏi lớn hơn: smart contract hoạt động như thế nào khi tiền, logic và trạng thái cùng thay đổi trong một giao dịch.
Cụ thể hơn, reentrancy không chỉ là một bug kỹ thuật đơn lẻ mà là lỗi về thứ tự thực thi. Một contract tưởng như viết đúng chức năng rút tiền, chia quỹ hoặc thanh toán có thể vẫn nguy hiểm nếu nó chuyển quyền kiểm soát sang contract khác quá sớm. Chính vì vậy, khi tìm hiểu lỗi này, người đọc thường muốn biết thêm cơ chế callback, external call, fallback hay receive đã khiến cuộc tấn công diễn ra như thế nào.
Bên cạnh cơ chế, ý định tìm kiếm quan trọng tiếp theo là rủi ro thực tế. Reentrancy có thể dẫn đến việc rút tiền lặp, làm sai số dư, phá vỡ logic accounting và trong trường hợp nghiêm trọng có thể hút cạn tài sản của giao thức. Sự kiện DAO hack năm 2016 thường được nhắc đến như ví dụ lịch sử giúp người mới thấy rằng lỗi nhỏ trong code có thể tạo ra hậu quả hệ thống rất lớn.
Ngoài ra, khi đi sâu hơn vào cách đọc code contract cơ bản, người học sẽ nhận ra reentrancy còn là cánh cửa dẫn sang nhiều khái niệm nền tảng khác như kiểm soát luồng gọi hàm, cập nhật state, pattern Check-Effects-Interactions và cả mối liên hệ với transaction và gas trong smart contract. Sau đây, bài viết sẽ đi từ định nghĩa đến cơ chế, từ rủi ro đến cách phòng tránh, rồi mới mở rộng sang các biến thể tinh vi hơn.
Lỗi Reentrancy trong Smart Contract là gì?
Lỗi reentrancy là lỗ hổng bảo mật trong smart contract, xuất hiện khi một hàm gọi ra contract bên ngoài trước khi cập nhật xong state, khiến luồng gọi có thể quay lại và dùng lại cùng dữ liệu trạng thái cũ.
Để hiểu rõ hơn lỗi reentrancy trong smart contract, cần nhìn nó như một lỗi về flow thực thi chứ không chỉ là lỗi cú pháp hay logic điều kiện. Khi một contract thực hiện gửi ETH hoặc gọi một hàm bên ngoài, nó có thể tạm thời trao quyền điều khiển cho contract nhận. Nếu contract nhận là độc hại, nó không nhất thiết phải “phá” hệ thống bằng một lệnh đặc biệt; thay vào đó, nó chỉ cần gọi ngược trở lại hàm cũ vào đúng thời điểm mà state bên trong chưa kịp thay đổi. Chính điểm “quay lại trước khi hoàn tất” này tạo nên bản chất của reentrancy.
Trong ngữ cảnh học Solidity, đây là một khái niệm nền tảng vì nó cho thấy code on-chain không vận hành tuyến tính đơn giản như nhiều người mới tưởng. Rất nhiều người khi mới học chỉ chú ý đến việc hàm withdraw() có cộng trừ số dư đúng hay không, nhưng lại bỏ qua việc contract đã chuyển quyền điều khiển sang bên ngoài ở giữa quá trình. Đó là lý do reentrancy luôn được nhắc đến trong phần security considerations của Solidity và xuất hiện trong các danh sách rủi ro bảo mật hàng đầu của smart contract.
Reentrancy có phải là lỗi cho phép rút tiền nhiều lần không?
Có, reentrancy thường cho phép rút tiền nhiều lần vì contract sử dụng cùng một trạng thái chưa được cập nhật, chuyển quyền điều khiển ra ngoài quá sớm và không khóa luồng gọi lặp lại.
Cụ thể hơn, khi một hàm rút tiền kiểm tra rằng người dùng còn số dư, rồi gửi tiền ra ngoài trước khi trừ số dư trong mapping nội bộ, attacker có thể lợi dụng chính khoảng trống này. Contract độc hại nhận được ETH sẽ kích hoạt callback hoặc fallback để gọi lại hàm rút tiền lần nữa. Vì biến số dư chưa bị giảm, lần gọi lại vẫn vượt qua kiểm tra. Quá trình đó có thể lặp đi lặp lại cho đến khi tài sản trong contract bị rút gần hết.
Điểm quan trọng là “rút tiền nhiều lần” không phải lúc nào cũng là biểu hiện duy nhất, nhưng lại là hình thức trực quan nhất để người mới nhìn thấy hậu quả của reentrancy. Nó giúp giải thích vì sao một contract không có lỗi số học, không có lỗi quyền truy cập quá rõ ràng, vẫn có thể bị mất tiền chỉ vì sai thứ tự hành động.
Reentrancy khác gì với lỗi logic thông thường trong hợp đồng thông minh?
Reentrancy khác lỗi logic thông thường ở chỗ nó gắn với khả năng bị gọi lại từ bên ngoài trước khi một lần thực thi kết thúc, còn lỗi logic thông thường thường chỉ sai ở điều kiện, công thức hoặc đường đi nghiệp vụ.
Tuy nhiên, sự khác biệt này không có nghĩa reentrancy tách biệt hoàn toàn với logic. Ngược lại, nó là một loại lỗi logic đặc thù liên quan đến control flow. Nếu một bug thông thường có thể là dùng nhầm phép toán hoặc đặt sai điều kiện require, thì reentrancy xuất hiện khi lập trình viên không kiểm soát đúng trật tự “kiểm tra → cập nhật trạng thái → tương tác bên ngoài”. Nói cách khác, bug logic thông thường làm contract xử lý sai; còn reentrancy khiến contract bị kéo vào một vòng thực thi ngoài dự tính.
Ở góc độ học tập, đây cũng là nơi người mới bắt đầu hiểu sâu hơn smart contract hoạt động như thế nào: mỗi khi contract gọi ra ngoài, nó không chỉ “gửi dữ liệu” mà còn có thể mở ra một nhánh thực thi mới. Vì vậy, đọc code contract không thể chỉ nhìn từng dòng riêng lẻ; phải nhìn cả chuỗi tương tác giữa các contract.
Reentrancy Attack xảy ra như thế nào trong Smart Contract?
Reentrancy attack diễn ra theo chuỗi 4 bước chính: kiểm tra điều kiện rút tiền, gọi external call, attacker callback quay lại hàm cũ và lặp rút trước khi state được cập nhật.
Để hiểu rõ hơn cơ chế này, hãy hình dung một contract lưu số dư người dùng trong mapping. Khi người dùng gọi withdraw(), contract kiểm tra balances[msg.sender] > 0, sau đó gửi ETH cho msg.sender, rồi cuối cùng mới gán balances[msg.sender] = 0. Nếu msg.sender là một contract độc hại, nó có thể lợi dụng việc nhận ETH để kích hoạt lại withdraw() ngay trong lúc lần gọi đầu tiên chưa hoàn tất. Vì state vẫn chưa đổi, điều kiện vẫn đúng, và ETH tiếp tục được gửi ra.
Cơ chế này cho thấy bản chất của reentrancy không nằm ở “hack bằng phép màu”, mà ở chỗ attacker tận dụng đúng những gì EVM cho phép: gọi hàm, nhận ETH, thực thi callback và tái nhập vào flow cũ. Nói đơn giản, contract dễ tổn thương đã tự mở cửa cho việc đó.
External call có phải là điểm khởi phát chính của reentrancy không?
Có, external call là điểm khởi phát chính của reentrancy vì nó chuyển quyền điều khiển từ contract hiện tại sang địa chỉ bên ngoài trước khi lần thực thi được chốt trạng thái.
Cụ thể, external call có thể là gửi ETH, gọi hàm interface, dùng low-level call, hoặc những tương tác tương tự làm contract khác thực thi code của nó. Khi quyền điều khiển bị nhường đi, contract hiện tại không còn đơn thuần “đi tiếp từ trên xuống dưới” như người mới thường hình dung. Nếu bên nhận là hợp đồng độc hại, nó sẽ tranh thủ thời điểm state còn cũ để gọi ngược lại.
Đây cũng là lý do mà khi học cách đọc code contract cơ bản, lập trình viên không chỉ cần nhìn hàm nào sửa state, mà còn phải dò kỹ chỗ nào gọi ra ngoài. Chỉ một lệnh call đặt sai vị trí cũng có thể biến logic bình thường thành lỗ hổng nghiêm trọng.
Những thành phần nào thường xuất hiện trong một cuộc tấn công reentrancy?
Có 5 thành phần chính thường xuất hiện trong reentrancy attack: vulnerable contract, attacker contract, hàm rút tiền, external call và state chưa cập nhật.
Để hiểu sâu hơn thành phần của cuộc tấn công, có thể chia như sau:
- Vulnerable contract: contract mục tiêu, thường chứa logic giữ tiền hoặc quản lý số dư.
- Attacker contract: contract độc hại có khả năng nhận ETH và gọi ngược trở lại.
- Hàm nhạy cảm: thường là withdraw, claim, refund, hoặc một hàm chuyển tài sản.
- External call: điểm mà vulnerable contract gửi tiền hoặc gọi code bên ngoài.
- State chưa cập nhật: yếu tố quyết định, vì nếu state đã đổi trước thì attacker khó lặp lại điều kiện cũ.
Bảng dưới đây tóm tắt vai trò của từng thành phần trong chuỗi tấn công reentrancy:
| Thành phần | Vai trò trong tấn công | Vì sao quan trọng |
|---|---|---|
| Vulnerable contract | Giữ tài sản và chứa logic lỗi | Là mục tiêu bị khai thác |
| Attacker contract | Nhận quyền điều khiển khi external call xảy ra | Tạo callback để tái nhập |
| Hàm withdraw/claim | Điểm vào của lần gọi đầu và các lần gọi lặp | Thường gắn với dòng tiền |
| External call | Chuyển quyền điều khiển ra ngoài | Mở cửa cho reentrancy |
| State chưa cập nhật | Giữ lại điều kiện cũ cho attacker lợi dụng | Cho phép rút lặp hoặc thao túng logic |
Nhìn vào bảng này, người học sẽ thấy reentrancy không phải bug ngẫu nhiên. Nó gần như luôn xuất hiện khi các mảnh ghép trên kết hợp với nhau trong cùng một flow xử lý tài sản.
Reentrancy diễn ra qua fallback hoặc receive function như thế nào?
Reentrancy thường diễn ra qua fallback hoặc receive function khi contract độc hại nhận ETH và dùng chính hook nhận tiền đó để gọi ngược lại hàm dễ tổn thương.
Để minh họa, giả sử vulnerable contract dùng call{value: amount}(“”) để chuyển ETH. Khi ETH tới attacker contract, receive() hoặc fallback() của attacker có thể tự động chạy. Bên trong hook đó, attacker gọi lại withdraw() của contract mục tiêu. Nếu số dư nội bộ chưa bị giảm, lần gọi mới vẫn qua được điều kiện kiểm tra. Đây là cơ chế cực kỳ kinh điển mà nhiều tài liệu an ninh blockchain dùng để giải thích reentrancy cho người mới.
Điểm cần nhớ là fallback/receive không xấu tự thân. Chúng là công cụ hợp pháp trong Solidity. Vấn đề nằm ở chỗ contract mục tiêu tin rằng sau khi gửi ETH thì logic của nó vẫn “an toàn để đi tiếp”, trong khi thực tế quyền điều khiển đã nằm trong tay bên nhận.
Theo tài liệu Security Considerations của Solidity, cách phòng ngừa phổ biến là tránh để contract gọi ra ngoài trước khi cập nhật xong state, bởi việc callback vào logic cũ có thể dẫn đến double-withdraw hoặc double-spend.
Lỗi Reentrancy nguy hiểm như thế nào đối với Smart Contract?
Lỗi reentrancy nguy hiểm vì nó có thể rút cạn quỹ, làm sai state, phá hỏng logic nghiệp vụ và gây mất niềm tin vào toàn bộ giao thức chứ không chỉ riêng một hàm.
Để hiểu rõ hơn mức độ nguy hiểm của reentrancy, cần nhìn nó ở hai tầng. Tầng đầu là thiệt hại tài chính trực tiếp: attacker có thể rút tài sản nhiều lần trong cùng một flow. Tầng thứ hai là thiệt hại hệ thống: các số dư, điều kiện, quyền lợi và luồng xử lý bên trong contract trở nên sai lệch. Một giao thức DeFi dù còn tài sản ở contract khác vẫn có thể rơi vào trạng thái hỗn loạn nếu accounting của nó bị phá vỡ.
Ngoài ra, trong môi trường blockchain, hậu quả thường khó đảo ngược hơn phần mềm truyền thống. Một khi transaction đã được xác nhận và tài sản đã bị chuyển đi, đội ngũ dự án không thể chỉ “rollback database” như ứng dụng web thông thường. Đó là lý do bảo mật smart contract luôn được xem là khu vực mà sai lầm nhỏ có thể trả giá rất đắt.
Reentrancy có thể khiến Smart Contract mất toàn bộ tài sản không?
Có, reentrancy có thể khiến smart contract mất toàn bộ tài sản nếu lỗ hổng nằm ở logic giữ quỹ chính, không có khóa chống tái nhập và cho phép attacker lặp đến khi cạn tiền.
Tuy nhiên, mức độ thiệt hại còn phụ thuộc vào cấu trúc hệ thống. Nếu contract chỉ giữ một phần quỹ nhỏ, tổn thất có thể giới hạn. Ngược lại, nếu đó là vault, pool, bridge component hoặc module chi trả phần thưởng, thiệt hại có thể lan rộng. Trong DeFi, nhiều module liên kết với nhau nên một lỗ hổng tại điểm rút tiền có thể làm xáo trộn nhiều lớp logic phía sau.
Sự nguy hiểm này từng được thị trường ghi nhớ qua DAO hack. Cuộc tấn công DAO năm 2016 dựa trên nguyên lý reentrancy, trong đó attacker rút tiền đệ quy trước khi số dư được cập nhật. Vụ việc đó cho thấy một lỗ hổng reentrancy không chỉ gây mất quỹ mà còn có thể tạo ra hệ quả sâu rộng với cả hệ sinh thái blockchain.
Những hậu quả nào thường gặp khi Smart Contract dính lỗi reentrancy?
Có 5 hậu quả thường gặp khi contract dính reentrancy: thất thoát tài sản, sai số dư nội bộ, hỏng logic nghiệp vụ, gián đoạn giao thức và tổn hại uy tín dự án.
Cụ thể hơn:
- Thất thoát tài sản: biểu hiện dễ thấy nhất, đặc biệt ở các hàm rút tiền hoặc claim reward.
- Sai accounting: mapping số dư, shares, debt, reward hoặc collateral có thể lệch khỏi thực tế.
- Phá vỡ logic: một số điều kiện chỉ nên chạy một lần, nhưng reentrancy khiến chúng bị lặp.
- Ngắt quãng dịch vụ: dự án có thể phải tạm dừng chức năng bằng cơ chế emergency pause.
- Mất uy tín: sau tấn công, niềm tin của cộng đồng và thanh khoản có thể giảm mạnh.
Đây là lúc khái niệm transaction và gas trong smart contract cũng trở nên quan trọng. Nhiều người mới tưởng rằng một transaction chỉ là một chuỗi lệnh thẳng. Nhưng trong thực tế, một transaction có thể chứa nhiều lớp gọi hàm lồng nhau, và gas cho phép những lớp thực thi này tiếp tục vận hành cho đến khi dừng bởi điều kiện logic hoặc hết gas. Hiểu điều đó giúp người học thấy vì sao reentrancy là vấn đề của flow và thực thi, không chỉ là “gửi tiền rồi xong”.
Reentrancy và drain funds khác nhau hay là cùng một kết quả?
Reentrancy là cơ chế tấn công, còn drain funds là một kết quả; chúng liên quan chặt chẽ nhưng không phải cùng một khái niệm.
Cụ thể, một contract có thể bị drain funds vì nhiều nguyên nhân khác nhau như lỗi quyền truy cập, sai oracle, logic mint sai hoặc flash loan manipulation. Trong khi đó, reentrancy là một kiểu lỗ hổng cụ thể tận dụng việc gọi lại trước khi state cập nhật. Nói cách khác, reentrancy là “cách cửa bị mở”, còn drain funds là “hậu quả khi tài sản bị lấy đi”.
Việc phân biệt này rất quan trọng trong content chuyên môn vì nó giúp người đọc không nhầm giữa nguyên nhân và hệ quả. Đồng thời, nó cũng tăng chất lượng semantic SEO: bài viết không chỉ trả lời “lỗi reentrancy là gì” mà còn đặt đúng vị trí của khái niệm này trong bức tranh bảo mật smart contract rộng hơn.
Làm thế nào để phòng tránh lỗi Reentrancy trong Smart Contract?
Phòng tránh reentrancy hiệu quả nhất là áp dụng 4 nguyên tắc: cập nhật state trước external call, giảm bề mặt gọi ra ngoài, dùng guard khi cần và thiết kế luồng rút tiền an toàn.
Để hiểu rõ hơn cách phòng tránh, cần nhớ rằng không có một “viên đạn bạc” duy nhất. Reentrancy là lỗi của flow, nên phòng thủ tốt nhất là kết hợp giữa thiết kế đúng thứ tự, công cụ khóa và kiến trúc thanh toán phù hợp. Rất nhiều đội ngũ mắc sai lầm khi nghĩ chỉ cần thêm một modifier là xong; trên thực tế, nếu toàn bộ flow sai, guard chỉ là lớp chặn tạm thời chứ không thay thế được tư duy thiết kế an toàn.
Có nên cập nhật state trước khi gọi external call không?
Có, nên cập nhật state trước khi gọi external call vì đây là cách trực tiếp nhất để cắt điều kiện mà attacker muốn tái sử dụng khi quay lại hàm cũ.
Cụ thể hơn, nếu contract kiểm tra số dư rồi trừ số dư ngay, sau đó mới gửi ETH, thì ngay cả khi attacker callback vào withdraw() lần nữa, điều kiện cũ đã không còn. Đây chính là tinh thần của pattern Check-Effects-Interactions: kiểm tra trước, ghi nhận thay đổi nội bộ trước, tương tác bên ngoài sau. Đây là cách phổ biến và đáng tin để ngăn các callback độc hại gây double-withdraw hoặc double-spend.
Với người mới học cách đọc code contract cơ bản, đây là một mẹo kiểm tra rất hữu ích: mỗi khi nhìn thấy call, transfer, send hoặc gọi interface ra ngoài, hãy hỏi ngay “state đã được cập nhật chưa?”. Chỉ một câu hỏi đó đã giúp phát hiện rất nhiều flow nguy hiểm.
Những cách phòng tránh reentrancy phổ biến nhất là gì?
Có 4 cách phòng tránh reentrancy phổ biến nhất: Check-Effects-Interactions, ReentrancyGuard, Pull Payment và audit kỹ luồng external call.
Dưới đây là các cách phổ biến nhất và bối cảnh sử dụng của chúng:
- Check-Effects-Interactions: đổi state trước, tương tác bên ngoài sau. Đây là lớp phòng thủ logic nền tảng.
- ReentrancyGuard: dùng modifier để chặn việc tái nhập vào các hàm được bảo vệ trong cùng thời điểm thực thi.
- Pull Payment: thay vì contract chủ động gửi tiền ngay, hệ thống ghi nhận khoản được nhận rồi để người dùng tự rút theo flow an toàn hơn.
- Audit external call: rà soát tất cả điểm gọi ra ngoài, bao gồm token hook, callback, low-level call và tương tác cross-contract.
Bảng dưới đây giúp nhìn nhanh từng cách phòng tránh và mục tiêu chính của nó:
| Cách phòng tránh | Mục tiêu chính | Khi nào hữu ích |
|---|---|---|
| Check-Effects-Interactions | Chặn điều kiện cũ bị tái dùng | Hầu hết flow rút tiền hoặc cập nhật số dư |
| ReentrancyGuard | Khóa tái nhập trong lúc hàm đang chạy | Hàm nhạy cảm, nhiều điểm external call |
| Pull Payment | Giảm việc đẩy tiền chủ động ra ngoài | Hệ thống thanh toán, refund, reward |
| Audit external call | Phát hiện bề mặt tấn công bị bỏ sót | Dự án có nhiều module hoặc DeFi composability |
Những phương pháp này giúp lập trình viên tiếp cận reentrancy theo cả hướng kỹ thuật lẫn kiến trúc hệ thống, thay vì chỉ vá lỗi ở bề mặt.
Reentrancy Guard khác gì với việc chỉ sửa thứ tự code?
Sửa thứ tự code giải quyết gốc logic, còn ReentrancyGuard thêm lớp khóa runtime; cách thứ nhất sửa nguyên nhân, cách thứ hai chặn biểu hiện tái nhập trong lúc thực thi.
Tuy nhiên, hai cách này không nên bị đặt trong thế “chọn một”. Một contract viết đúng thứ tự nhưng có nhiều điểm tương tác phức tạp vẫn nên cân nhắc guard. Ngược lại, một contract viết sai flow mà chỉ thêm guard có thể vẫn tiềm ẩn vấn đề thiết kế khác. Vì vậy, guard tốt nhất nên được xem là lớp bảo vệ bổ sung trên nền một flow an toàn.
Dù dùng công cụ nào, tư duy “không chuyển quyền điều khiển quá sớm” vẫn là trung tâm của phòng thủ reentrancy.
Những biến thể reentrancy nào dễ bị bỏ sót khi audit Smart Contract?
Có ít nhất 4 biến thể reentrancy dễ bị bỏ sót khi audit smart contract: cross-function reentrancy, cross-contract reentrancy, read-only reentrancy và các flow kết hợp composability phức tạp.
Đến đây, sau khi đã trả lời trực tiếp truy vấn “lỗi reentrancy là gì”, bài viết bước qua ranh giới ngữ cảnh từ macro sang micro. Nghĩa là trọng tâm không còn chỉ là định nghĩa, cơ chế cơ bản hay cách chặn phổ thông nữa, mà là những biến thể tinh vi hơn thường khiến audit sơ sài bỏ sót. Đây cũng là phần giúp nội dung có chiều sâu thẩm quyền, bởi người đọc không chỉ biết lỗi cơ bản mà còn hiểu vì sao reentrancy vẫn tiếp tục là một chủ đề sống trong bảo mật Web3.
Cross-function reentrancy có khác reentrancy trong một hàm duy nhất không?
Có, cross-function reentrancy khác ở chỗ attacker không nhất thiết quay lại đúng cùng một hàm, mà có thể tái nhập qua một hàm khác nhưng vẫn tận dụng cùng trạng thái chưa được đồng bộ đầy đủ.
Điểm này khiến nhiều người mới audit bị nhầm. Họ kiểm tra một hàm withdraw() khá kỹ, thêm guard cho hàm đó, rồi cho rằng đã xong. Nhưng nếu contract còn hàm claim(), exit(), redeem() hoặc updateReward() tương tác chung trên cùng state, attacker có thể đi một vòng khác để chạm lại dữ liệu chưa ổn định. Vì vậy, audit reentrancy không được đóng khung ở một hàm đơn lẻ; phải nhìn toàn bộ nhóm hàm chạm đến cùng nguồn state.
Cross-contract reentrancy có phải là rủi ro lớn trong DeFi composability không?
Có, cross-contract reentrancy là rủi ro lớn trong DeFi composability vì nhiều contract liên kết qua interface, callback, token hook và các module trung gian, khiến biên kiểm soát trở nên phức tạp hơn.
Trong hệ DeFi, một thao tác của người dùng có thể chạy qua vault, strategy, reward distributor, oracle adapter và token contract. Mỗi điểm gọi ra ngoài đều có thể trở thành mắt xích của reentrancy nếu state trung tâm chưa được chốt đúng lúc. Chính tính mô-đun và khả năng ghép giao thức với nhau làm DeFi mạnh lên, nhưng cũng khiến audit khó hơn nhiều so với contract đơn giản.
Đây là lý do mà khi mở rộng từ kiến thức cơ bản sang thực hành, người học không nên chỉ hỏi “hàm này có lỗi không” mà nên hỏi “toàn bộ flow giữa các contract có nhường quyền điều khiển sai thời điểm không”.
Read-only reentrancy là gì và vì sao vẫn nguy hiểm dù không rút tiền trực tiếp?
Read-only reentrancy là biến thể mà attacker lợi dụng trạng thái đọc chưa ổn định để thao túng kết quả tính toán, định giá hoặc điều kiện logic, dù không trực tiếp hút tiền bằng một lệnh rút lặp truyền thống.
Cụ thể hơn, một số giao thức không mất tiền ngay ở bước callback đầu tiên, nhưng các hàm đọc dữ liệu giữa chừng có thể bị dùng để làm sai tỷ giá, sai accounting hoặc sai điều kiện kích hoạt ở module khác. Khi đó, hậu quả tài chính xuất hiện gián tiếp thay vì “rút sạch trong một hàm”. Đây là kiểu biến thể khó hơn cho người mới, nhưng rất đáng biết vì nó nhắc rằng reentrancy không chỉ là vấn đề withdraw().
Check-Effects-Interactions và Pull Payment khác nhau thế nào trong phòng thủ reentrancy?
Check-Effects-Interactions tối ưu việc sắp xếp thứ tự logic trong cùng hàm, còn Pull Payment tối ưu kiến trúc thanh toán bằng cách giảm nhu cầu đẩy tiền chủ động sang bên ngoài.
Cụ thể, Check-Effects-Interactions trả lời câu hỏi: “Nếu tôi vẫn phải gọi ra ngoài, tôi nên sắp xếp lệnh theo thứ tự nào?”. Trong khi đó, Pull Payment trả lời câu hỏi lớn hơn: “Liệu tôi có cần phải gửi tiền chủ động ngay bây giờ không, hay nên ghi nhận khoản nợ và để người nhận tự rút sau?”. Cách thứ hai thường giúp giảm bề mặt reentrancy vì contract không phải vừa xử lý logic vừa đẩy tài sản trong cùng một thời điểm nhạy cảm.
Tóm lại, nếu bạn là người mới, hãy ghi nhớ một chuỗi học tập rất thực dụng: hiểu smart contract hoạt động như thế nào, nắm được transaction và gas trong smart contract, luyện cách đọc code contract cơ bản, rồi kiểm tra kỹ thứ tự state update và external call. Chỉ riêng thói quen đó đã giúp bạn nhận diện phần lớn lỗ hổng reentrancy cơ bản trước khi bước sang những biến thể phức tạp hơn.



































