Access Control Issues
Access Control Issues
Access control vulnerabilities occur when functions lack proper permission checks, allowing unauthorized users to execute critical operations.
Common Access Control Vulnerabilities
1. Missing Access Controls
// Vulnerable: No access control
function withdrawAll() public {
payable(msg.sender).transfer(address(this).balance);
}
// Fixed: Add access control
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not authorized");
_;
}
function withdrawAll() public onlyOwner {
payable(owner).transfer(address(this).balance);
}2. Default Function Visibility
Always explicitly declare function visibility:
// Vulnerable: No visibility specified (defaults to public in old Solidity)
function sensitiveFunction() {
// Critical logic
}
// Fixed: Explicit visibility
function sensitiveFunction() private {
// Critical logic
}3. tx.origin Authentication
Never use `tx.origin` for authorization:
// Vulnerable
function withdraw() public {
require(tx.origin == owner);
// ...
}
// Fixed: Use msg.sender
function withdraw() public {
require(msg.sender == owner);
// ...
}Secure Access Control Patterns
Using OpenZeppelin Ownable
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyContract is Ownable {
function sensitiveFunction() public onlyOwner {
// Only owner can call this
}
}Role-Based Access Control (RBAC)
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}
function mint(address to) public onlyRole(MINTER_ROLE) {
// Only minters can call this
}
}Multi-Signature Control
For critical operations, require multiple signatures:
uint public constant REQUIRED_CONFIRMATIONS = 2;
mapping(bytes32 => mapping(address => bool)) public confirmations;
mapping(bytes32 => uint) public confirmationCount;
function executeTransaction(bytes32 txId) public {
require(confirmationCount[txId] >= REQUIRED_CONFIRMATIONS);
// Execute transaction
}Best Practices
1. Always use explicit function visibility
2. Implement the principle of least privilege
3. Use established access control libraries (OpenZeppelin)
4. Use multi-sig for critical operations
5. Implement timelocks for sensitive changes
6. Document all access control decisions
7. Test access control thoroughly
Audit Checklist
- [ ] All functions have explicit visibility
- [ ] Critical functions have access control
- [ ] No use of tx.origin for authentication
- [ ] Access control modifiers are correctly implemented
- [ ] Admin functions use multi-sig when appropriate
- [ ] Role hierarchies are properly designed