
先明确合约的风险特点
关键词中的“只能合约”通常指“智能合约”。智能合约是在区块链上执行的程序,可以按照代码处理资产或数据。公开函数可能被外部账户调用,因此不能假设只有预期用户会按预期方式操作。
合约部署后通常不容易直接修改;如果设计缺陷影响资产处理或关键功能,修复也可能复杂。安全工作因此不只是写代码,还包括权限设计、测试、审查和部署后的管理。

权限设计:让关键操作只由适当角色执行
先列出哪些操作会改变重要状态,例如创建代币、调整配置、暂停功能或管理其他权限,再明确每项操作由谁执行。对不应向所有用户开放的操作,应通过访问控制检查调用者,而不是仅依赖前端界面隐藏按钮。

单一所有者模式适合管理者明确、权限较简单的合约;但所有者账户也会成为重要风险点。若密钥丢失或被他人控制,受保护的管理功能可能遭到滥用。所有权转移时还要核实目标账户确实可用,避免把管理权交给无法操作合约的地址。
当不同操作需要不同责任人时,可使用基于角色的权限控制,例如将铸造、暂停和权限管理分别交给不同角色。遵循最小权限原则,只授予完成任务所需的能力,并审慎设计谁有权授予或撤销角色。特别要关注默认管理员权限:如果它能够管理其他角色,其控制账户应受到严格保护。
多签可以要求多个参与者共同批准管理操作,降低单个密钥失陷或单人误操作造成的风险。但它不能替代权限审查:仍需明确签署门槛、参与者管理和权限变更流程。
用条件检查防止异常状态变化
对用户输入、调用者身份和当前状态进行校验,确保操作满足预期条件;条件不成立时应停止执行,避免留下部分状态变更。Solidity 中常见的 require、assert 和 revert 都能用于触发回滚,但用途应区分:require通常用于检查输入或业务前提,assert适合检查程序内部应始终成立的不变量,revert则可在不满足条件时明确终止执行。
不变量是系统运行中应持续成立的性质,例如某项总量或余额关系。开发者应先定义这些性质,再在代码和测试中验证,不能仅凭函数在正常样例下运行成功就认为状态安全。
测试、分析与独立审查缺一不可
单元测试适合验证单个函数在给定输入下的行为,但测试结果受测试用例覆盖范围限制,容易漏掉边界条件和不常见调用顺序。可进一步采用属性测试或模糊测试,以更多输入探索异常行为;静态分析可检查代码结构和可能的执行路径,动态分析则通过实际运行发现违反预期性质的情况。
对关键安全性质要求更高时,可以考虑形式化验证:先把需要满足的性质写成明确规格,再分析合约模型是否符合。但这类方法依赖规格本身是否完整,也不意味着可以省略代码审查和实际测试。
独立代码审查或安全审计能补充开发者自身检查,但不应被当作发现所有漏洞的保证。提交审查前应让代码和设计说明清楚,并确保审查覆盖关键权限、状态变化和外部交互路径。
适用条件与常见问题
权限方案应与合约复杂度相匹配:管理者单一且操作简单时,所有权模式可能足够;有多类敏感操作或需要分工时,角色控制更灵活;需要多人共同批准时,可考虑多签。无论采用哪种方式,都要确认权限授予、撤销、转移和紧急处置流程清晰。
常见问题:通过了单元测试是否就安全?不是,测试只能覆盖已设计的场景。完成审计是否可以不再维护?也不是,审计只是额外审查,不能排除所有缺陷。角色成员能否自动授予其他人权限?不一定,能否授予或撤销取决于角色管理员的配置,应在部署前核对。
因此,智能合约安全应贯穿设计、编码、测试、独立审查与权限管理全过程。本文讨论的是通用开发原则;具体合约仍需根据其功能、权限结构和运行环境进行评估。