1
月
在社区成长 1 月
LEVEL 1
1 XP
一、背景
加密货币钱包的核心风险并不只来自私钥泄露。现实中,很多资产损失发生在用户主动连接钱包、签署消息或发送交易之后。常见场景包括无限额度 ERC-20 授权、NFT 全局授权、伪装成登录请求的 EIP-712 签名,以及把高权限合约误认为普通收款地址。
本文以以太坊及 EVM 兼容链为例,说明钱包授权和签名的工作原理,并给出可在本地节点中复现的检查步骤。全文仅使用本地实验地址和防御性查询,不针对任何真实目标,也不讨论价格预测或投资收益。
二、理解钱包真正签署了什么
钱包常见操作可以分为三类:
1. 普通消息签名
personal_sign 通常用于证明某个地址的控制权,例如登录站点。它本身不会直接修改链上状态,但签名可能被其他协议作为授权凭证,因此仍需检查消息内容、域名、随机数和有效期。
2. EIP-712 类型化数据签名
eth_signTypedData_v4 会展示结构化字段,常见于 Permit、订单、委托和登录。用户不支付 Gas 不代表没有资产风险。只要目标合约支持 Permit,一份有效签名可能允许第三方在链上提交授权。
重点检查字段包括:
- domain.name:签名所属协议名称;
- domain.chainId:签名适用的链;
- domain.verifyingContract:实际验证签名的合约;
- primaryType:Permit、PermitSingle、Order 或其他结构;
- spender:被授权转移资产的地址;
- value 或 amount:授权数量;
- nonce:防止签名被重复使用的序号;
- deadline 或 expiration:签名及授权的有效期。
不同代币对 Permit 的实现并不完全一致,不能只看到“EIP-712”就认定安全。Permit2 等通用授权合约还可能形成“代币先授权给 Permit2,再通过链下签名授权具体 spender”的两层权限关系。
3. 链上交易
交易会修改链上状态。钱包界面显示的“交互合约”未必是最终执行逻辑,因为代理合约可以通过 delegatecall 调用实现合约。检查时至少要同时关注目标地址、函数选择器、参数、ETH 数量和内部调用模拟结果。
三、几种必须识别的高风险权限
ERC-20 常见授权函数为:
approve(address spender, uint256 amount)
授权额度记录在:
allowance(owner, spender)
如果 amount 等于 uint256 最大值,即 2^256-1,通常代表无限授权。无限授权并非必然恶意,但一旦 spender 合约被攻击、升级或其管理密钥失陷,钱包中的相关代币就可能受影响。
ERC-721 和 ERC-1155 常见全局授权函数为:
setApprovalForAll(address operator, bool approved)
当 approved 为 true 时,operator 往往可以操作该集合下属于用户的全部 NFT,而不只是一枚 NFT。相比单枚 tokenId 的 approve,这类权限范围更大。
还应警惕名称与行为不一致的调用。函数名只是 ABI 的解释结果,最终效果取决于目标字节码和执行路径。未知合约不能仅凭网页按钮上的“登录”“验证”或“领取”字样判断安全。
四、前置条件
建议在隔离目录中安装 Foundry,并确认以下命令可用:
forge --version
cast --version
anvil --version
所有私钥都只能使用 anvil 输出的本地测试私钥。anvil 默认账户和私钥是公开测试数据,绝对不能向这些地址转入真实资产,也不能把真实助记词导入实验环境。
启动本地节点:
anvil
另开终端,记录 anvil 输出的第一个测试地址、第二个测试地址和第一个测试私钥:
export RPC=http://127.0.0.1:8545
export OWNER=第一个本地测试地址
export SPENDER=第二个本地测试地址
export PK=第一个本地测试私钥
五、本地复现 ERC-20 授权
1. 创建实验项目
forge init wallet-approval-lab
cd wallet-approval-lab
2. 新建 src/DemoToken.sol
文件内容如下:
pragma solidity ^0.8.20;
contract DemoToken {
string public name = 'Demo Token';
string public symbol = 'DMT';
uint8 public decimals = 18;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor() {
balanceOf[msg.sender] = 1000000 ether;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
require(allowed >= amount, 'allowance');
require(balanceOf[from] >= amount, 'balance');
if (allowed != type(uint256).max) {
allowance[from][msg.sender] = allowed - amount;
}
balanceOf[from] -= amount;
balanceOf[to] += amount;
return true;
}
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
这个示例只用于展示授权状态,不应部署到生产环境。它省略了大量正式代币需要考虑的边界条件和标准接口。
3. 编译并部署
forge build
forge create src/DemoToken.sol
emoToken --rpc-url $RPC --private-key $PK --broadcast
从输出中复制 Deployed to 后面的地址:
export TOKEN=本地部署的合约地址
4. 检查初始状态
cast call $TOKEN 'balanceOf(address)(uint256)' $OWNER --rpc-url $RPC
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
初始 allowance 应为 0。
5. 构造无限授权
先计算 uint256 最大值:
export MAX=$(cast max-uint)
发送授权交易:
cast send $TOKEN 'approve(address,uint256)' $SPENDER $MAX --rpc-url $RPC --private-key $PK
再次查询:
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
如果结果接近 1.1579×10^77,即 uint256 最大值,说明这是无限授权。
6. 撤销授权
撤销 ERC-20 授权通常是将额度设置为 0:
cast send $TOKEN 'approve(address,uint256)' $SPENDER 0 --rpc-url $RPC --private-key $PK
再次调用 allowance,确认结果已经变为 0。不要仅依赖钱包或授权管理网站显示“已提交”;应等待交易确认,并直接读取链上状态。
六、在签名前检查 calldata
已知 ABI 时,可以先在离线环境构造 calldata:
cast calldata 'approve(address,uint256)' $SPENDER $MAX
approve(address,uint256) 的函数选择器是 calldata 的前 4 字节,后面依次编码 spender 和 amount。对待签交易数据进行解码:
cast calldata-decode 'approve(address,uint256)' 0x待检查的数据
检查结果时重点回答四个问题:
1. spender 是谁?是否与当前访问的协议公开合约地址一致?
2. amount 是实际需要的数额,还是最大整数?
3. 目标合约是否为正确网络上的正确地址?同名代币在不同链上的地址通常不同。
4. 交易是否附带非零 value?普通 ERC-20 approve 一般不需要附带 ETH。
如果没有可信 ABI,解码结果可能存在歧义。4 字节选择器不是强身份标识,理论上不同函数签名可能碰撞,因此仍需结合已验证源码、字节码和模拟结果判断。
七、防御性链上检查方法
对自己钱包已经交互过的合约,可以执行以下只读检查。
1. 判断地址是否包含合约代码
cast code $SPENDER --rpc-url $RPC
返回 0x 通常表示当前区块状态下没有合约代码;返回较长字节码说明地址是合约。但“有代码”不等于可信,“暂时无代码”也不等于永久安全,例如 CREATE2 和账户抽象场景需要额外分析。
2. 查询 ERC-20 授权
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
必须按“链 ID、代币地址、owner、spender”四元组记录结果。只记录项目名称很容易混淆不同链和仿冒代币。
3. 查询 NFT 全局授权
对于实现标准接口的 NFT 合约:
cast call $NFT 'isApprovedForAll(address,address)(bool)' $OWNER $OPERATOR --rpc-url $RPC
返回 true 代表存在全局操作权限。撤销时应从可信界面或直接调用:
cast send $NFT 'setApprovalForAll(address,bool)' $OPERATOR false --rpc-url $RPC --private-key 本地测试私钥
在真实钱包中执行撤销前仍需核对 NFT 合约地址、operator 和当前网络,且不要把真实私钥直接写入 shell 历史。
4. 查看授权事件
ERC-20 Approval 事件可帮助定位何时产生了授权:
cast logs --from-block 0 --to-block latest --address $TOKEN 'Approval(address,address,uint256)' --rpc-url $RPC
事件适合审计历史,但最终权限应以合约当前状态为准。某些非标准代币可能不规范地发出事件,代理升级也可能改变后续行为。
八、建立可执行的风险识别流程
建议把待签操作按以下顺序审查:
第一步:确认网络。核对 chainId,避免把测试网、主网或其他 EVM 链混为一谈。
第二步:确认来源。不要通过搜索广告或陌生私信进入钱包页面;优先使用项目公开文档中交叉验证过的域名与合约地址。
第三步:确认动作类型。区分普通消息签名、EIP-712 签名和链上交易。看到 Permit、PermitSingle、PermitBatch、setApprovalForAll、approve 等字段时应提高审查级别。
第四步:确认权限边界。记录资产合约、spender、数量、有效期、nonce,以及授权是否覆盖单个资产还是整个集合。
第五步:检查合约属性。查看源码是否公开验证、是否为代理、实现合约能否升级、升级管理员是谁、是否存在暂停或资产转移等高权限入口。源码已验证只能说明字节码和展示源码可对应,并不自动证明业务安全。
第六步:进行交易模拟。优先使用钱包自带模拟或可信本地工具,查看余额变化、授权变化和内部调用。模拟结果依赖特定区块状态,并不能保证未来状态下结果完全相同。
第七步:最小化授权。能按精确金额授权时,不默认使用无限额度;完成操作后重新查询剩余额度,并根据使用频率决定是否撤销。
第八步:账户隔离。长期储存地址不要频繁连接陌生应用。可将日常交互钱包与冷钱包分离,并限制交互钱包中的资产规模。
九、常见误区与风险提示
1. “签名不花 Gas,所以一定安全”
错误。链下签名可以被第三方提交到链上。Permit、订单和委托都是典型例子。
2. “撤销网页显示成功就够了”
不够。应检查交易是否上链,并重新读取 allowance 或 isApprovedForAll。
3. “无限授权只是方便,不会主动转走资产”
授权本身通常不会立即转账,但它为 spender 提供了后续 transferFrom 的条件。spender 或可升级实现一旦出现问题,风险可能被放大。
4. “硬件钱包可以防止所有钓鱼”
硬件钱包能保护私钥不直接离开设备,但如果用户在设备上确认了恶意交易或危险签名,合法签名仍然会被生成。关键字段必须在可信显示界面上核对。
5. “地址曾经正常使用,所以以后也安全”
代理合约可能升级,管理员密钥可能泄露,域名也可能被劫持。历史行为只能作为证据之一。
6. “只要撤销代币授权,就能让所有签名失效”
不一定。尚未过期的链下订单、Permit2 子授权或其他协议签名可能有独立 nonce 和取消机制。需要根据具体协议检查 nonce、deadline 和取消函数。
十、常见问题
问:是否应该把所有授权都设置为 0?
答:从最小权限角度看,长期不用的授权应撤销。但撤销是链上交易,需要 Gas;部分旧代币还要求先将原额度设为 0,再设置新额度。应基于实际使用频率、资产价值和 spender 风险决定。
问:如何判断一个 EIP-712 登录签名没有转账权限?
答:检查 primaryType 和完整字段。符合 EIP-4361 思路的登录消息通常包含域名、URI、chain ID、nonce、签发时间等,但仍要确认应用实际验证逻辑。若出现 token、spender、amount、Permit、operator 等资产授权字段,就不应把它当作普通登录。
问:授权给路由合约是否安全?
答:无法只凭“路由合约”这一名称下结论。需要核对合约地址、源码、代理结构、升级权限、审计范围和历史事件。即使协议知名,也应遵循最小额度原则。
问:已经签了可疑消息怎么办?
答:先断开站点连接并保存签名内容、时间、链和地址等证据;随后检查相关代币授权、NFT operator、Permit2 权限及协议 nonce。必要时通过协议提供的取消接口使订单或签名失效。若助记词或私钥曾输入网页,则不能只靠撤销授权解决,应在可信设备上创建新钱包并迁移资产,同时先处理可能阻碍迁移的恶意授权或自动化风险。
十一、总结
钱包安全的关键不是机械地点击“拒绝”或“撤销”,而是明确授权对象、资产范围、额度、有效期和验证合约。最可靠的检查方式是把前端展示与链上状态分开验证:先识别签名或交易类型,再解码参数,随后查询 allowance、isApprovedForAll、合约代码和授权事件,最后通过本地或可信工具模拟状态变化。
本文的本地实验展示了无限 ERC-20 授权的产生、查询和撤销过程。实际使用中还应结合代理升级权限、Permit nonce、跨链地址差异和账户隔离策略。任何工具的风险评分都只能作为辅助证据,不能替代对交易数据和链上状态的独立核验。
加密货币钱包的核心风险并不只来自私钥泄露。现实中,很多资产损失发生在用户主动连接钱包、签署消息或发送交易之后。常见场景包括无限额度 ERC-20 授权、NFT 全局授权、伪装成登录请求的 EIP-712 签名,以及把高权限合约误认为普通收款地址。
本文以以太坊及 EVM 兼容链为例,说明钱包授权和签名的工作原理,并给出可在本地节点中复现的检查步骤。全文仅使用本地实验地址和防御性查询,不针对任何真实目标,也不讨论价格预测或投资收益。
二、理解钱包真正签署了什么
钱包常见操作可以分为三类:
1. 普通消息签名
personal_sign 通常用于证明某个地址的控制权,例如登录站点。它本身不会直接修改链上状态,但签名可能被其他协议作为授权凭证,因此仍需检查消息内容、域名、随机数和有效期。
2. EIP-712 类型化数据签名
eth_signTypedData_v4 会展示结构化字段,常见于 Permit、订单、委托和登录。用户不支付 Gas 不代表没有资产风险。只要目标合约支持 Permit,一份有效签名可能允许第三方在链上提交授权。
重点检查字段包括:
- domain.name:签名所属协议名称;
- domain.chainId:签名适用的链;
- domain.verifyingContract:实际验证签名的合约;
- primaryType:Permit、PermitSingle、Order 或其他结构;
- spender:被授权转移资产的地址;
- value 或 amount:授权数量;
- nonce:防止签名被重复使用的序号;
- deadline 或 expiration:签名及授权的有效期。
不同代币对 Permit 的实现并不完全一致,不能只看到“EIP-712”就认定安全。Permit2 等通用授权合约还可能形成“代币先授权给 Permit2,再通过链下签名授权具体 spender”的两层权限关系。
3. 链上交易
交易会修改链上状态。钱包界面显示的“交互合约”未必是最终执行逻辑,因为代理合约可以通过 delegatecall 调用实现合约。检查时至少要同时关注目标地址、函数选择器、参数、ETH 数量和内部调用模拟结果。
三、几种必须识别的高风险权限
ERC-20 常见授权函数为:
approve(address spender, uint256 amount)
授权额度记录在:
allowance(owner, spender)
如果 amount 等于 uint256 最大值,即 2^256-1,通常代表无限授权。无限授权并非必然恶意,但一旦 spender 合约被攻击、升级或其管理密钥失陷,钱包中的相关代币就可能受影响。
ERC-721 和 ERC-1155 常见全局授权函数为:
setApprovalForAll(address operator, bool approved)
当 approved 为 true 时,operator 往往可以操作该集合下属于用户的全部 NFT,而不只是一枚 NFT。相比单枚 tokenId 的 approve,这类权限范围更大。
还应警惕名称与行为不一致的调用。函数名只是 ABI 的解释结果,最终效果取决于目标字节码和执行路径。未知合约不能仅凭网页按钮上的“登录”“验证”或“领取”字样判断安全。
四、前置条件
建议在隔离目录中安装 Foundry,并确认以下命令可用:
forge --version
cast --version
anvil --version
所有私钥都只能使用 anvil 输出的本地测试私钥。anvil 默认账户和私钥是公开测试数据,绝对不能向这些地址转入真实资产,也不能把真实助记词导入实验环境。
启动本地节点:
anvil
另开终端,记录 anvil 输出的第一个测试地址、第二个测试地址和第一个测试私钥:
export RPC=http://127.0.0.1:8545
export OWNER=第一个本地测试地址
export SPENDER=第二个本地测试地址
export PK=第一个本地测试私钥
五、本地复现 ERC-20 授权
1. 创建实验项目
forge init wallet-approval-lab
cd wallet-approval-lab
2. 新建 src/DemoToken.sol
文件内容如下:
pragma solidity ^0.8.20;
contract DemoToken {
string public name = 'Demo Token';
string public symbol = 'DMT';
uint8 public decimals = 18;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor() {
balanceOf[msg.sender] = 1000000 ether;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
require(allowed >= amount, 'allowance');
require(balanceOf[from] >= amount, 'balance');
if (allowed != type(uint256).max) {
allowance[from][msg.sender] = allowed - amount;
}
balanceOf[from] -= amount;
balanceOf[to] += amount;
return true;
}
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
这个示例只用于展示授权状态,不应部署到生产环境。它省略了大量正式代币需要考虑的边界条件和标准接口。
3. 编译并部署
forge build
forge create src/DemoToken.sol
从输出中复制 Deployed to 后面的地址:
export TOKEN=本地部署的合约地址
4. 检查初始状态
cast call $TOKEN 'balanceOf(address)(uint256)' $OWNER --rpc-url $RPC
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
初始 allowance 应为 0。
5. 构造无限授权
先计算 uint256 最大值:
export MAX=$(cast max-uint)
发送授权交易:
cast send $TOKEN 'approve(address,uint256)' $SPENDER $MAX --rpc-url $RPC --private-key $PK
再次查询:
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
如果结果接近 1.1579×10^77,即 uint256 最大值,说明这是无限授权。
6. 撤销授权
撤销 ERC-20 授权通常是将额度设置为 0:
cast send $TOKEN 'approve(address,uint256)' $SPENDER 0 --rpc-url $RPC --private-key $PK
再次调用 allowance,确认结果已经变为 0。不要仅依赖钱包或授权管理网站显示“已提交”;应等待交易确认,并直接读取链上状态。
六、在签名前检查 calldata
已知 ABI 时,可以先在离线环境构造 calldata:
cast calldata 'approve(address,uint256)' $SPENDER $MAX
approve(address,uint256) 的函数选择器是 calldata 的前 4 字节,后面依次编码 spender 和 amount。对待签交易数据进行解码:
cast calldata-decode 'approve(address,uint256)' 0x待检查的数据
检查结果时重点回答四个问题:
1. spender 是谁?是否与当前访问的协议公开合约地址一致?
2. amount 是实际需要的数额,还是最大整数?
3. 目标合约是否为正确网络上的正确地址?同名代币在不同链上的地址通常不同。
4. 交易是否附带非零 value?普通 ERC-20 approve 一般不需要附带 ETH。
如果没有可信 ABI,解码结果可能存在歧义。4 字节选择器不是强身份标识,理论上不同函数签名可能碰撞,因此仍需结合已验证源码、字节码和模拟结果判断。
七、防御性链上检查方法
对自己钱包已经交互过的合约,可以执行以下只读检查。
1. 判断地址是否包含合约代码
cast code $SPENDER --rpc-url $RPC
返回 0x 通常表示当前区块状态下没有合约代码;返回较长字节码说明地址是合约。但“有代码”不等于可信,“暂时无代码”也不等于永久安全,例如 CREATE2 和账户抽象场景需要额外分析。
2. 查询 ERC-20 授权
cast call $TOKEN 'allowance(address,address)(uint256)' $OWNER $SPENDER --rpc-url $RPC
必须按“链 ID、代币地址、owner、spender”四元组记录结果。只记录项目名称很容易混淆不同链和仿冒代币。
3. 查询 NFT 全局授权
对于实现标准接口的 NFT 合约:
cast call $NFT 'isApprovedForAll(address,address)(bool)' $OWNER $OPERATOR --rpc-url $RPC
返回 true 代表存在全局操作权限。撤销时应从可信界面或直接调用:
cast send $NFT 'setApprovalForAll(address,bool)' $OPERATOR false --rpc-url $RPC --private-key 本地测试私钥
在真实钱包中执行撤销前仍需核对 NFT 合约地址、operator 和当前网络,且不要把真实私钥直接写入 shell 历史。
4. 查看授权事件
ERC-20 Approval 事件可帮助定位何时产生了授权:
cast logs --from-block 0 --to-block latest --address $TOKEN 'Approval(address,address,uint256)' --rpc-url $RPC
事件适合审计历史,但最终权限应以合约当前状态为准。某些非标准代币可能不规范地发出事件,代理升级也可能改变后续行为。
八、建立可执行的风险识别流程
建议把待签操作按以下顺序审查:
第一步:确认网络。核对 chainId,避免把测试网、主网或其他 EVM 链混为一谈。
第二步:确认来源。不要通过搜索广告或陌生私信进入钱包页面;优先使用项目公开文档中交叉验证过的域名与合约地址。
第三步:确认动作类型。区分普通消息签名、EIP-712 签名和链上交易。看到 Permit、PermitSingle、PermitBatch、setApprovalForAll、approve 等字段时应提高审查级别。
第四步:确认权限边界。记录资产合约、spender、数量、有效期、nonce,以及授权是否覆盖单个资产还是整个集合。
第五步:检查合约属性。查看源码是否公开验证、是否为代理、实现合约能否升级、升级管理员是谁、是否存在暂停或资产转移等高权限入口。源码已验证只能说明字节码和展示源码可对应,并不自动证明业务安全。
第六步:进行交易模拟。优先使用钱包自带模拟或可信本地工具,查看余额变化、授权变化和内部调用。模拟结果依赖特定区块状态,并不能保证未来状态下结果完全相同。
第七步:最小化授权。能按精确金额授权时,不默认使用无限额度;完成操作后重新查询剩余额度,并根据使用频率决定是否撤销。
第八步:账户隔离。长期储存地址不要频繁连接陌生应用。可将日常交互钱包与冷钱包分离,并限制交互钱包中的资产规模。
九、常见误区与风险提示
1. “签名不花 Gas,所以一定安全”
错误。链下签名可以被第三方提交到链上。Permit、订单和委托都是典型例子。
2. “撤销网页显示成功就够了”
不够。应检查交易是否上链,并重新读取 allowance 或 isApprovedForAll。
3. “无限授权只是方便,不会主动转走资产”
授权本身通常不会立即转账,但它为 spender 提供了后续 transferFrom 的条件。spender 或可升级实现一旦出现问题,风险可能被放大。
4. “硬件钱包可以防止所有钓鱼”
硬件钱包能保护私钥不直接离开设备,但如果用户在设备上确认了恶意交易或危险签名,合法签名仍然会被生成。关键字段必须在可信显示界面上核对。
5. “地址曾经正常使用,所以以后也安全”
代理合约可能升级,管理员密钥可能泄露,域名也可能被劫持。历史行为只能作为证据之一。
6. “只要撤销代币授权,就能让所有签名失效”
不一定。尚未过期的链下订单、Permit2 子授权或其他协议签名可能有独立 nonce 和取消机制。需要根据具体协议检查 nonce、deadline 和取消函数。
十、常见问题
问:是否应该把所有授权都设置为 0?
答:从最小权限角度看,长期不用的授权应撤销。但撤销是链上交易,需要 Gas;部分旧代币还要求先将原额度设为 0,再设置新额度。应基于实际使用频率、资产价值和 spender 风险决定。
问:如何判断一个 EIP-712 登录签名没有转账权限?
答:检查 primaryType 和完整字段。符合 EIP-4361 思路的登录消息通常包含域名、URI、chain ID、nonce、签发时间等,但仍要确认应用实际验证逻辑。若出现 token、spender、amount、Permit、operator 等资产授权字段,就不应把它当作普通登录。
问:授权给路由合约是否安全?
答:无法只凭“路由合约”这一名称下结论。需要核对合约地址、源码、代理结构、升级权限、审计范围和历史事件。即使协议知名,也应遵循最小额度原则。
问:已经签了可疑消息怎么办?
答:先断开站点连接并保存签名内容、时间、链和地址等证据;随后检查相关代币授权、NFT operator、Permit2 权限及协议 nonce。必要时通过协议提供的取消接口使订单或签名失效。若助记词或私钥曾输入网页,则不能只靠撤销授权解决,应在可信设备上创建新钱包并迁移资产,同时先处理可能阻碍迁移的恶意授权或自动化风险。
十一、总结
钱包安全的关键不是机械地点击“拒绝”或“撤销”,而是明确授权对象、资产范围、额度、有效期和验证合约。最可靠的检查方式是把前端展示与链上状态分开验证:先识别签名或交易类型,再解码参数,随后查询 allowance、isApprovedForAll、合约代码和授权事件,最后通过本地或可信工具模拟状态变化。
本文的本地实验展示了无限 ERC-20 授权的产生、查询和撤销过程。实际使用中还应结合代理升级权限、Permit nonce、跨链地址差异和账户隔离策略。任何工具的风险评分都只能作为辅助证据,不能替代对交易数据和链上状态的独立核验。