SCWE-063: Insecure Event Emission
Stable Version v1.0
This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).
Relationships¶
- CWE-778: Insufficient Logging
https://cwe.mitre.org/data/definitions/778.html
Description¶
Events in Solidity play a critical role in logging contract activity and ensuring transparency. Improper event handling can lead to security risks such as:
- Missing critical event emissions – Making external monitoring difficult.
- Emitting misleading or incorrect data – Resulting in users or external systems making incorrect assumptions.
- Logging sensitive information – Leaking private or security-sensitive data.
Failure to handle events properly can affect contract auditing, debugging, and external monitoring tools, making it difficult to detect anomalies or track contract states correctly.
Remediation¶
- Emit events for all critical state changes, such as token transfers, ownership changes, or contract upgrades.
- Ensure that the data logged in events accurately represents the actual contract state.
- Avoid logging sensitive information such as private keys, hashes used for authentication, or confidential business logic.
Vulnerable Contract Example¶
contract Example {
mapping(address => uint) public balances;
event Withdraw(address indexed user, uint amount);
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient funds");
payable(msg.sender).transfer(_amount);
balances[msg.sender] -= _amount;
// Incorrect event emission: Logs the requested amount instead of the actual withdrawn amount
emit Withdraw(msg.sender, _amount);
}
}
Issues in the Vulnerable Code¶
- The Withdraw event logs
_amount, which is the requestedwithdrawalamount, but if the transfer fails (due to gas limits, reentrancy, or an external issue), the event still logs it as if the withdrawal happened. - If an attacker exploits a discrepancy between event logs and actual state changes, they could mislead users, external indexers, or off-chain services.
Fixed Contract Example¶
contract Example {
mapping(address => uint) public balances;
event Withdraw(address indexed user, uint actualAmount);
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient funds");
(bool success, ) = payable(msg.sender).call{value: _amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= _amount;
// Logs the amount actually withdrawn (equals _amount on success)
emit Withdraw(msg.sender, _amount);
}
}
Fixes in the Secure Code¶
- Uses
call{value: _amount}("")to send funds safely and ensures success before updating the balance. - Updates state and emits the event only after a successful transfer; logs
_amount(the actual withdrawn amount on success). - Prevents false event emissions by reverting on transfer failure before any state change or event.