Token Approval Without Spending Limits
id: LS45M
title: Token Approval Without Spending Limits
baseSeverity: M
category: approvals
language: solidity
blockchain: [ethereum]
impact: Unlimited asset drain by approved party
status: draft
complexity: low
attack_vector: external
mitigation_difficulty: easy
versions: [">0.6.0", "<0.8.25"]
cwe: CWE-275
swc: SWC-114
π Description
- Token approval without spending limits occurs when a user or contract approves another address (e.g., DEX, vault, spender) for the maximum
uint256value (2^256 - 1), rather than a limited or session-specific amount. - This practice, while common for gas optimization, introduces a major risk:
- If the approved address is compromised, the attacker can:
- Drain all approved token balances at once,
- Front-run or race the intended use,
- Exploit long-lived approvals across unrelated sessions or dApps.
- This vulnerability has led to repeated losses in DeFi phishing attacks, compromised frontends, and wallet integrations.
π¨ Vulnerable Code
function approveUnlimited(IERC20 token, address spender) external {
token.approve(spender, type(uint256).max); // β Dangerous unlimited approval
}
π§ͺ Exploit Scenario
Step-by-step attack:
- A user approves a DEX contract with 2^256 - 1 allowance.
- Later, that DEX's contract is compromised or a malicious upgrade is pushed.
- The attacker calls transferFrom(user, attacker, entire balance) using the lingering allowance.
- Userβs tokens are drained instantly, with no further signature or interaction required.
Assumptions:
- The user or contract gives unlimited allowance.
- The spender is compromised or malicious in the future.
β Fixed Code
function safeApprove(IERC20 token, address spender, uint256 amount) external {
require(amount > 0 && amount <= token.balanceOf(msg.sender), "Invalid amount");
token.approve(spender, amount); // β
Approve only whatβs needed
}
π§ Contextual Severity
- context: "Default"
severity: M
reasoning: "Common approval pattern but depends on the safety of the approved contract."
- context: "DeFi protocol with router approvals"
severity: H
reasoning: "Widespread use of infinite approval multiplies blast radius."
- context: "Private wallet use with trusted spender"
severity: L
reasoning: "Risk minimal if spender is secure and trusted."
π‘οΈ Prevention
Primary Defenses
- Avoid approve(spender, type(uint256).max) unless absolutely required and trusted.
- Approve only the amount needed for a specific transaction.
- Use ERC-20 permit() (EIP-2612) for gasless, single-use approvals where possible.
Additional Safeguards
- Implement approval race mitigation by setting allowance to zero before updating.
- Add expiration deadlines or nonces to time-limit approvals.
- Track and revoke stale or unused allowances in off-chain monitoring systems (e.g., revoke.cash).
Detection Methods
- Slither: unlimited-approval, unsafe-token-approve, approve-max detectors.
- Manual audit of all approve(...) logic in contracts and dApp integration flows.
- Integration testing to ensure minimum necessary approvals are enforced.
π°οΈ Historical Exploits
- Name: Badger DAO Exploit
- Date: December 2021
- Loss: Approximately $120 million
- Post-mortem: Link to post-mortem
π Further Reading
- SWC-114: Unrestricted Token Approval
- EIP-2612 Permit Standard
- OpenZeppelin Docs β Approval Patterns
- Revoke.cash β Approval Risks
β Vulnerability Report
id: LS45M
title: Token Approval Without Spending Limits
severity: M
score:
impact: 5
exploitability: 3
reachability: 4
complexity: 1
detectability: 5
finalScore: 4.1
π Justifications & Analysis
- Impact: High β full balance loss is possible with one bad approval.
- Exploitability: Depends on spender being compromised or malicious, which is common in phishing.
- Reachability: Very common in DeFi frontends and wallet interactions.
- Complexity: Trivial fix β just approve the needed amount.
- Detectability: High β easily spotted in audits and flagged by tools.