Public Mappings
id: LS13M
title: Public Mappings
baseSeverity: M
category: data-leakage
language: solidity
blockchain: [ethereum]
impact: Unauthorized access to sensitive information via auto-generated getter
status: draft
complexity: low
attack_vector: external
mitigation_difficulty: easy
versions: [">=0.4.0", "<=0.8.25"]
cwe: CWE-200
swc: SWC-136
π Description
- In Solidity, declaring a mapping as public automatically generates a getter function for it.
- However, when the mapping contains nested types (such as structs, arrays, or other mappings), the auto-generated getter allows external users to retrieve internal state values on-demand, exposing potentially sensitive data.
- While this is not a vulnerability in all contexts, it becomes critical when:
- The nested data includes sensitive business logic (e.g., votes, KYC status, secret salts).
- The mapping is assumed to be βinternal-onlyβ due to its type or naming.
- Access control checks are bypassed unintentionally due to auto-exposure.
π¨ Vulnerable Code
pragma solidity ^0.8.0;
contract VoteTracker {
struct Vote {
bool hasVoted;
uint8 choice;
}
mapping(address => Vote) public votes; // β Public mapping exposes Vote struct
}
π§ͺ Exploit Scenario
- A contract implements a voting system with Vote structs mapped per user.
- The mapping is declared public to enable internal read access (e.g., votes[msg.sender]).
- An attacker or curious user queries votes(victimAddress) directly via the generated getter.
- Sensitive data like vote choices or flags are publicly revealed.
Assumptions:
- Developers are unaware of the full implications of public on mappings.
- Sensitive data is nested under mappings assumed to be internally scoped.
- No external access guard is applied to the getter.
β Fixed Code
pragma solidity ^0.8.0;
contract SecureVoteTracker {
struct Vote {
bool hasVoted;
uint8 choice;
}
mapping(address => Vote) internal votes; // β
Internal prevents external access
function hasVoted(address user) external view returns (bool) {
return votes[user].hasVoted;
}
}
π§ Contextual Severity
- context: "Default"
severity: M
reasoning: "Can lead to sensitive data exposure and governance manipulation in common use cases."
- context: "Public DeFi Protocol with token-based voting"
severity: H
reasoning: "Vote buying, stake-based manipulation, or airdrop sybil attacks possible."
- context: "Private/internal app with low user count"
severity: L
reasoning: "Limited damage; user addresses likely known or non-sensitive."
π‘οΈ Prevention
Primary Defenses
- Do not mark nested mappings or mappings of structs as public if data confidentiality is expected.
- Use custom getter functions with access control as needed.
Additional Safeguards
- Restrict contract functions to return redacted or masked data.
- Design mappings with visibility expectations explicitly documented.
- Consider zero-knowledge or hash-based storage for privacy-critical logic.
Detection Methods
- Search for public mapping(address => Struct) or similar patterns.
- Check if nested values expose user privacy, choices, or business-critical flags.
- Tools: Slither (public-mapping-struct rule), manual audit
π°οΈ Historical Exploits
- Name: Confidential Airdrop Whitelist Exposure
- Date: 2021
- Loss: No funds lost, but targeted sybil attacks observed
- Post-mortem: Link to post-mortem
π Further Reading
- SWC-136: Unencrypted Sensitive Data
- Solidity Docs β Public Getter Generation
- Slither Detector β Public Mapping of Struct
β Vulnerability Report
id: LS13M
title: Public Mappings
severity: M
score:
impact: 3
exploitability: 3
reachability: 5
complexity: 2
detectability: 3
finalScore: 3.25
π Justifications & Analysis
- Impact: Reveals sensitive or strategic information; affects fairness or integrity.
- Exploitability: Simple external call retrieves supposedly private data.
- Reachability: Fully exposed via public getter generated by Solidity.
- Complexity: Low-complexity coding mistake; misunderstood visibility semantics.
- Detectability: Easy to overlook unless audit explicitly checks visibility of mappings.