首例智慧合約中的全動態訪問控制管理解決方案

買賣虛擬貨幣
訪問控制是軟體基礎設施安全的基本要素。企業應用程式需要嚴格的規則來決定誰可以做什麼,這取決於每個使用者的許可權。可以說,智慧合約中的訪問控制需要更嚴格的審查,因為漏洞可能導致惡意參與者控制系統。如今智慧合約中僅存有簡單形式的靜態訪問控制。最常見的是onlyOwner模式。另一個是Openzeppelin的“角色”智慧合約,該智慧合約可以在部署之前定義角色。儘管這為大多數智慧合約應用程式提供了良好的基礎,但基於現有的角色的訪問控制(RBAC)系統使管理員能夠在執行時動態地定義角色。從這個意義上說,Roles智慧合約是具有限制性的,因為在部署之後無法定義角色。本文介紹現有的智慧合約訪問控制模式,並提出了RBAC和基於屬性的訪問控制(ABAC)智慧合約的定義。Only OwneronlyOwner模式是智慧合約中最常用、最容易實現的訪問控制方法。
程式碼1顯示了Ownable智慧合約的Openzeppelin實現。1pragma solidity ^0.5.0; 2 3import "../GSN/Context.sol"; 4/** 5 * @dev Contract module which provides a basic access control mechanism, where
 6 * there is an account (an owner) that can be granted exclusive access to 7 * specific functions. 8 * 9 * This module is used through inheritance. It will make available the modifier10 * `onlyOwner`, which can be applied to your functions to restrict their use to11 * the owner.
12 */13contract Ownable is Context {14    address private _owner;1516    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);17
18    /**19     * @dev Initializes the contract setting the deployer as the initial owner.20     */21    constructor () internal {22        address msgSender = _msgSender();23        _owner = msgSender;
24        emit OwnershipTransferred(address(0), msgSender);25    }2627    /**28     * @dev Returns the address of the current owner.29     */
30    function owner() public view returns (address) {31        return _owner;32    }3334    /**35     * @dev Throws if called by any account other than the owner.
36     */37    modifier onlyOwner() {38        require(isOwner(), "Ownable: caller is not the owner");39        _;40    }41
42    /**43     * @dev Returns true if the caller is the current owner.44     */45    function isOwner() public view returns (bool) {46        return _msgSender() == _owner;47    }
4849    /**50     * @dev Leaves the contract without owner. It will not be possible to call51     * `onlyOwner` functions anymore. Can only be called by the current owner.52     *53     * NOTE: Renouncing ownership will leave the contract without an owner,
54     * thereby removing any functionality that is only available to the owner.55     */56    function renounceOwnership() public onlyOwner {57        emit OwnershipTransferred(_owner, address(0));58        _owner = address(0);59    }
6061    /**62     * @dev Transfers ownership of the contract to a new account (`newOwner`).63     * Can only be called by the current owner.64     */65    function transferOwnership(address newOwner) public onlyOwner {
66        _transferOwnership(newOwner);67    }6869    /**70     * @dev Transfers ownership of the contract to a new account (`newOwner`).71     */
72    function _transferOwnership(address newOwner) internal {73        require(newOwner != address(0), "Ownable: new owner is the zero address");74        emit OwnershipTransferred(_owner, newOwner);75        _owner = newOwner;76    }77}
該模式假定智慧合約只有一個管理員,並使管理員可以將所有權轉移到另一個地址。擴充套件Ownable智慧合約允許子智慧合約使用onlyOwner自定義修飾符定義函式。這些函式要求事務的傳送者是單一管理員。程式碼2展示瞭如何在子智慧合約中實現此示例。1pragma solidity ^0.5.0; 2 3import "@openzeppelin/contracts/ownership/Ownable.sol"; 4
 5contract MyContract is Ownable { 6    function normalThing() public { 7        // anyone can call this normalThing() 8    } 910    function specialThing() public onlyOwner {
11        // only the owner can call specialThing()!12    }13}Roles如果Ownable僅限於一個管理員,則Openzeppelin的Roles庫可以定義多個角色。 1pragma solidity ^0.5.0;
 2 3/** 4 * @title Roles 5 * @dev Library for managing addresses assigned to a Role. 6 */ 7library Roles {
 8    struct Role { 9        mapping (address => bool) bearer;10    }1112    /**13     * @dev Give an account access to this role.
14     */15    function add(Role storage role, address account) internal {16        require(!has(role, account), "Roles: account already has role");17        role.bearer[account] = true;18    }19
20    /**21     * @dev Remove an account's access to this role.22     */23    function remove(Role storage role, address account) internal {24        require(has(role, account), "Roles: account does not have role");25        role.bearer[account] = false;
26    }2728    /**29     * @dev Check if an account has this role.30     * @return bool31     */
32    function has(Role storage role, address account) internal view returns (bool) {33        require(account != address(0), "Roles: account is the zero address");34        return role.bearer[account];35    }36}程式碼3展示了Roles智慧合約的實施。與Ownable不同,Role不提供自定義訪問修飾符。相反使用此庫的智慧合約必須在函式內部實現角色要求。他們還必須為每個角色定義Roles.Role型別狀態變數。程式碼4展示了實現兩個角色的ERC20智慧合約的示例:_burners和_minters。
1pragma solidity ^0.5.0; 2 3import "@openzeppelin/contracts/access/Roles.sol"; 4import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; 6
 7contract MyToken is ERC20, ERC20Detailed { 8    using Roles for Roles.Role; 910    Roles.Role private _minters;11    Roles.Role private _burners;12
13    constructor(address[] memory minters, address[] memory burners)14        ERC20Detailed("MyToken", "MTKN", 18)15        public16    {17        for (uint256 i = 0; i < minters.length; ++i) {18            _minters.add(minters[i]);
19        }2021        for (uint256 i = 0; i < burners.length; ++i) {22            _burners.add(burners[i]);23        }24    }
2526    function mint(address to, uint256 amount) public {27        // Only minters can mint28        require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE");2930        _mint(to, amount);
31    }3233    function burn(address from, uint256 amount) public {34        // Only burners can burn35        require(_burners.has(msg.sender), "DOES_NOT_HAVE_BURNER_ROLE");36
37       _burn(from, amount);38    }39}它具有靈活性,允許智慧合約定義所需的儘可能多的角色,但這將require語句的實現留給了智慧合約。由於未提供自定義訪問修飾符,因此在某種程度上降低了可讀性,並且增加了引入編碼錯誤的可能性。但是沒有什麼能阻止智慧合約實現包含require語句的自定義訪問修飾符。Limitations(侷限性)這些當前的解決方案在部署智慧合約之前提供了角色的靈活性。由於角色取決於硬編碼規則,因此不支援動態建立的角色。這非常適合在已知的內部智慧合約之間提供訪問控制,但它不能提供與現代面向使用者的訪問控制軟體相當的靈活性。
具有活躍使用者基礎的軟體,特別是企業軟體,本質上需要不同級別的訪問。隨著組織的發展或萎縮,訪問控制應用程式的管理員需要能夠輕鬆新增和分配新角色的能力。這些結構如今以基於角色的訪問控制(RBAC)和基於屬性的訪問控制(ABAC)的形式存在於軟體中。Role-Based Access Control (RBAC)在RBAC中,每個使用者都被分配了一個角色,每個角色都具有一組許可權,並且只要他們的角色具有正確的許可權,使用者就可以訪問資源。只要系統不需要新的角色,RBAC主要透過Roles智慧合約來滿足。Attribute-Based Access Control (ABAC)在ABAC中,為每個使用者分配了一組主題屬性,為每個資源分配了一組物件屬性。中央訪問控制機構定義有關需要執行哪些主題和物件屬性的規則。
與RBAC相比,這是一個更復雜且耗時的解決方案。但是對於大型應用程式和企業而言,它更加靈活,因為它允許每個使用者獨有的各種許可權。在runtime,Ownable或Roles都不滿足這些公共模式中的任何一種。如果需要建立新角色,則需要傳送新程式碼。這些體系結構不允許InfoSec管理員透過簡單的介面建立、更新或刪除新角色。幸運的是,我們正在努力解決這一問題。Access Control (Beta)Openzeppelin即將釋出的v3.0版本(當前處於Beta版)已終止Roles庫,轉而使用名為AccessControl的abstract 智慧合約。注意:不建議您在生產應用程式中使用此解決方案,因為它仍處於測試階段。
程式碼5展示了AccessControl的實現。 1pragma solidity ^0.6.0;  2  3import "../utils/EnumerableSet.sol";  4import "../utils/Address.sol";  5import "../GSN/Context.sol";
  6  7/**  8 * @dev Contract module that allows children to implement role-based access  9 * control mechanisms. 10 * 11 * Roles are referred to by their `bytes32` identifier. These should be exposed
 12 * in the external API and be unique. The best way to achieve this is by 13 * using `public constant` hash digests: 14 * 15 * ``` 16 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 17 * ```
 18 * 19 * Roles can be used to represent a set of permissions. To restrict access to a 20 * function call, use {hasRole}: 21 * 22 * ``` 23 * function foo() public {
 24 *     require(hasRole(MY_ROLE, _msgSender())); 25 *     ... 26 * } 27 * ``` 28 * 29 * Roles can be granted and revoked dynamically via the {grantRole} and
 30 * {revokeRole} functions. Each role has an associated admin role, and only 31 * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 32 * 33 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 34 * that only accounts with this role will be able to grant or revoke other 35 * roles. More complex role relationships can be created by using
 36 * {_setRoleAdmin}. 37 */ 38abstract contract AccessControl is Context { 39    using EnumerableSet for EnumerableSet.AddressSet; 40    using Address for address; 41
 42    struct RoleData { 43        EnumerableSet.AddressSet members; 44        bytes32 adminRole; 45    } 46 47    mapping (bytes32 => RoleData) private _roles;
 48 49    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 50 51    /** 52     * @dev Emitted when `account` is granted `role`. 53     *
 54     * `sender` is the account that originated the contract call, an admin role 55     * bearer except when using {_setupRole}. 56     */ 57    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 58 59    /**
 60     * @dev Emitted when `account` is revoked `role`. 61     * 62     * `sender` is the account that originated the contract call: 63     *   - if using `revokeRole`, it is the admin role bearer 64     *   - if using `renounceRole`, it is the role bearer (i.e. `account`) 65     */
 66    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 67 68    /** 69     * @dev Returns `true` if `account` has been granted `role`. 70     */ 71    function hasRole(bytes32 role, address account) public view returns (bool) {
 72        return _roles[role].members.contains(account); 73    } 74 75    /** 76     * @dev Returns the number of accounts that have `role`. Can be used 77     * together with {getRoleMember} to enumerate all bearers of a role.
 78     */ 79    function getRoleMemberCount(bytes32 role) public view returns (uint256) { 80        return _roles[role].members.length(); 81    } 82 83    /**
 84     * @dev Returns one of the accounts that have `role`. `index` must be a 85     * value between 0 and {getRoleMemberCount}, non-inclusive. 86     * 87     * Role bearers are not sorted in any particular way, and their ordering may 88     * change at any point. 89     *
 90     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure 91     * you perform all queries on the same block. See the following 92     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] 93     * for more information. 94     */ 95    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
 96        return _roles[role].members.at(index); 97    } 98 99    /**100     * @dev Returns the admin role that controls `role`. See {grantRole} and101     * {revokeRole}.
102     *103     * To change a role's admin, use {_setRoleAdmin}.104     */105    function getRoleAdmin(bytes32 role) public view returns (bytes32) {106        return _roles[role].adminRole;107    }
108109    /**110     * @dev Grants `role` to `account`.111     *112     * If `account` had not been already granted `role`, emits a {RoleGranted}113     * event.
114     *115     * Requirements:116     *117     * - the caller must have ``role``'s admin role.118     */119    function grantRole(bytes32 role, address account) public virtual {
120        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");121122        _grantRole(role, account);123    }124125    /**
126     * @dev Revokes `role` from `account`.127     *128     * If `account` had been granted `role`, emits a {RoleRevoked} event.129     *130     * Requirements:131     *
132     * - the caller must have ``role``'s admin role.133     */134    function revokeRole(bytes32 role, address account) public virtual {135        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");136137        _revokeRole(role, account);
138    }139140    /**141     * @dev Revokes `role` from the calling account.142     *143     * Roles are often managed via {grantRole} and {revokeRole}: this function's
144     * purpose is to provide a mechanism for accounts to lose their privileges145     * if they are compromised (such as when a trusted device is misplaced).146     *147     * If the calling account had been granted `role`, emits a {RoleRevoked}148     * event.149     *
150     * Requirements:151     *152     * - the caller must be `account`.153     */154    function renounceRole(bytes32 role, address account) public virtual {155        require(account == _msgSender(), "AccessControl: can only renounce roles for self");
156157        _revokeRole(role, account);158    }159160    /**161     * @dev Grants `role` to `account`.
162     *163     * If `account` had not been already granted `role`, emits a {RoleGranted}164     * event. Note that unlike {grantRole}, this function doesn't perform any165     * checks on the calling account.166     *167     * Requirements:
168     *169     * - this function can only be called from a constructor.170     */171    function _setupRole(bytes32 role, address account) internal virtual {172        require(!address(this).isContract(), "AccessControl: roles cannot be setup after construction");173        _grantRole(role, account);
174    }175176    /**177     * @dev Sets `adminRole` as ``role``'s admin role.178     */179    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
180        _roles[role].adminRole = adminRole;181    }182183    function _grantRole(bytes32

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;