ACCESS CONTROL AS AN ATTACK VECTOR IN SOLIDITY Access Control Checks on Critical Functions
INTRODUCTION
Before we can discuss this topic, let’s understand what Blockchain and Smart contracts are.
Blockchain is a globally shared transactional database. This means that everyone can read entries in the database just by participating in the network. The participation happens via transaction. Imagine it as an open ledger where all the transactions can publicly be accessed, provided you have access to the internet!
Smart contracts are programs (sets of code) stored on a blockchain that run based on the logic stated in it. It is often said that “Code is law” It means as soon as a smart contract is deployed on the blockchain, it becomes immutable, making it impossible to change its terms.
Security is one of the most important considerations when writing smart contracts. mistakes are costly and easily exploited by bad actors readily available to take advantage of any form of loophole, hence the need to prevent contracts from such attacks. The emphasis in this article is on smart contract functions.
What is Access Control in Solidity?
Access control protects against attacks and maintains the integrity of the blockchain ecosystem by guaranteeing that only authorised users can interact with sensitive data and functionalities within a smart contract.
Functions are sets of instructions that must be followed to produce a particular result, it is safe to say there is no smart contract without functions. Functions are collections of actions that frequently require inputs to produce outputs. A function that adds two integers, for instance, may accept two numbers as input, compute them, and then give the result as output. Therefore, the function is where a smart contract's logic is written. Access control ensures that only authorised parties can execute or alter sensitive parts of a function, contract states, or data.
Smart contracts use a mechanism called Role-based access control (RBAC) to offer access and privileges for different roles which may include; admin, manager, and user. The functions can only be called by users with the relevant role.
Access Control Checks on Critical Function
Some of the common vulnerabilities related to access control are explained as follows;
- Missed Modifier Validations
Missed modifier validations can become an attack vector in solidity when developers forget or neglect to include necessary modifiers in their functions. Some of these include modifying the owner, transfer of funds and tokens pausing and unpausing the contracts, among others. Missed validations either in the modifier or inside require or conditional statements may lead to compromise of the contract or loss of funds.
Check out the code snippet below
pragma solidity 0.8.19;
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only the contract owner can call this function");
_; // Continue with the function execution if the modifier condition is met
}
function changeOwner(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid new owner address");
owner = newOwner;
}
function getOwner() public view returns (address) {
return owner;
}
function someFunction() public onlyOwner {
// Function logic restricted to the contract owner
}
}
The onlyOwner modifier restricts access to functions, allowing only the contract owner to call those functions. In a situation where there is a need to change the owner to a new owner, the onlyOwner set in the modifier is still the one that can call the function.
- Incorrect Modifier Names
Incorrect modifier name due to the developer’s mistakes and spelling errors in a modifier or function can mislead both the developer and the users of the contract, thereby opening a loophole for malicious actors to call the critical function without the modifier, which may in turn lead to loss of funds or change of ownership depending on the logic in the function.
pragma solidity 0.8.19;
contract IncorrectModifierExample {
address public owner;
// Incorrectly named modifier
modifier onlyOwnerOrAdmin() {
require(msg.sender == owner || msg.sender == admin(), "Access denied");
_;
}
function admin() public view returns (address) {
// Function representing an admin address retrieval
// But the modifier doesn't use this correctly
return 0x123...;
}
function someFunction() public onlyOwnerOrAdmin {
// Function logic
}
}
In the above code, the address of the admin was not declared as a state variable, so there was no proper adminAddress variable to store the admin's address.
The onlyOwnerOrAdmin modifier in the function has a hard-coded fixed address of an admin which cannot be accessed. This is already a vulnerability to watch against.
- Overpowered Roles
Overpowered roles in solidity refers to a function that grants excessive or unnecessary privileges to a certain role which may extend beyond the intended scope or requirements of that role. Overpowered roles may lead to vulnerabilities. Therefore the practice of least privileges must always be followed in assigning privileges
pragma solidity 0.8.19;
contract OverpoweredRolesExample {
mapping(address => string) public userRoles;
mapping(address => bool) public isAdmin;
constructor() {
// set contract is set as an admin at deployment
isAdmin[msg.sender] = true;
}
modifier onlyAdmin() {
require(isAdmin[msg.sender], "Only admin can call this function");
_;
}
function addUser(address _user, string memory _role) public onlyAdmin {
userRoles[_user] = _role;
}
// An overpowered function
// admin can change any user's role, even other admins
function changeUserRole(address _user, string memory _newRole) public onlyAdmin {
userRoles[_user] = _newRole;
}
}
Giving the admin(msg.sender) excessive privilege to change userRole and other admin is an Overpowered role, which could give room to attackers.
Conclusion
In conclusion, developers have to watch out for missed validations either in the modifier or inside require or conditional statements may lead to compromise of the contract or loss of funds. Be keen on naming modifiers and functions to curb mistakes and spelling errors in the modifier or function which can mislead both the developer and the users of the contract, giving bad actors an upper hand. An overpowered role in solidity is not a good practice as it grants excessive or unnecessary privileges to a certain role, thereby making the contract vulnerable.