Composable Security Middleware Hooks
Abstract
This EIP proposes a standard interface, ILayer
, for implementing composable security layers in smart contracts. These layers act as middleware, enabling runtime validation of function calls before and after execution, independent of the protected contract’s logic. This approach facilitates modular security, allowing independent providers to manage and upgrade security layers across multiple contracts.
Motivation
Current smart contract security practices often rely on monolithic validation logic within the contract itself. This can lead to tightly coupled code, making it difficult to isolate and address security concerns. Existing ERCs already are using something that can be seen as specific implementation of such layers wrapping: ERC-4337 describes requirements to perform validate user operations in a separate contract, and later elaborates same need to paymaster validation, that can be seen as a separate layers of a generic system. The Security Layers Standard introduces a modular approach, enabling:
- Independent Security Providers: Specialized security providers can focus on developing and maintaining specific security checks.
- Composable Security: Layers can be combined to create comprehensive security profiles tailored to individual contract needs.
- Upgradability: Security layers can be updated without requiring changes to the protected contract.
- Flexibility: Layers can perform a wide range of validation checks, including access control, input sanitization, output verification, and more.
Having a generalized standard for such layers can help to build more secure and modular systems as well as enable security providers to build generic, service-oriented security oracle solutions.
Specification
A contract implementing the ILayer
interface MUST provide two functions:
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.20;
interface ILayer {
/// @notice Validates a function call before execution.
/// @param configuration Layer-specific configuration data.
/// @param selector The function selector being called.
/// @param sender The address initiating the call.
/// @param value The amount of ETH sent with the call (if any).
/// @param data The calldata for the function call.
/// @return beforeCallResult Arbitrary data to be passed to `afterCallValidation`.
/// @dev MUST revert if validation fails.
function beforeCall(
bytes memory configuration,
bytes4 selector,
address sender,
uint256 value,
bytes memory data
) external returns (bytes memory);
/// @notice Validates a function call after execution.
/// @param configuration Layer-specific configuration data.
/// @param selector The function selector being called.
/// @param sender The address initiating the call.
/// @param value The amount of ETH sent with the call (if any).
/// @param data The calldata for the function call.
/// @param beforeCallResult The data returned by `beforeCallValidation`.
/// @dev MUST revert if validation fails.
function afterCall(
bytes memory configuration,
bytes4 selector,
address sender,
uint256 value,
bytes memory data,
bytes memory beforeCallResult
) external;
}
A protected contract MAY integrate security layers by calling the beforeCallValidation
function before executing its logic and the afterCallValidation
function afterwards. Multiple layers can be registered and executed in a defined order. The protected contract MUST revert if any layer reverts.
Rationale
Flexibility: The layerConfig
parameter allows for layer-specific customization, enabling a single layer implementation to serve multiple contracts with varying requirements.
non-static calls: Layers can maintain their own state, allowing for more complex validation logic (e.g., rate limiting, usage tracking).
Strict Validation: Reverts on validation failure ensure a fail-safe mechanism, preventing execution of potentially harmful transactions.
Gas Costs: Layers naturally will have gas costs associated with their execution. However, the benefits of enhanced security and modularity outweigh these costs, especially as blockchain technology continues to evolve and we expect gas costs to decrease over time.
Reference Implementation
A reference implementation of the ILayer
interface and a sample protected contract can be found in the repository:
In the ../assets/eip-7746/ILayer.sol
a reference interface is provided.
In this test, a Protected.sol
contract is protected by a RateLimitLayer.sol
layer. The RateLimitLayer
implements the ILayer
interface and enforces a rate which client has configured.
The Drainer
simulates a vulnerable contract that acts in a malicious way. In the test.ts
The Drainer
contract is trying to drain the funds from the Protected
contract. It is assumed that Protected
contract has bug that allows partial unauthorized access to the state.
The RateLimitLayer
is configured to allow only 10 transactions per block from same sender. The test checks that the Drainer
contract is not able to drain the funds from the Protected
contract.
Security Considerations
Layer Trust: Thoroughly audit and vet any security layer before integrating it into your contract. Malicious layers can compromise contract security.
Copyright
Copyright and related rights waived via CC0.