云端接口挂在公网上,谁都能请求。但接口的能力差别很大:查版本号是无害的,改密码、读私信、消耗 AI 配额则是高敏操作。鉴权因此做成了分级而不是一刀切。
三档模式
| 模式 | 行为 | 适用场景 |
|---|---|---|
grace | 低敏操作放行但限速;高敏操作必须带令牌 | 老客户端尚未全部更新的过渡期 |
strict | 所有操作都必须带令牌 | 全部客户端更新完毕后的常态 |
log | 全部放行,只记录未带令牌的调用 | 排障与应急回退 |
grace 还带一个截止时间:时间一到自动等价于 strict,不需要再去改配置 —— 避免宽限期被忘记而永久敞开。
哪些算高敏操作
function cdc_op_is_sensitive(string $op): bool {
static $s = ['reset', 'msg_users', 'msg_conv', 'msg_send', 'msg_read',
'msg_unread', 'msg_peers', 'bind_device', 'chat'];
return in_array(strtolower($op), $s, true);
}reset—— 改密码,等于账号接管msg_*—— 私信收发与读取,涉及他人隐私bind_device—— 绑设备,直接影响配额chat—— AI 代理,不带令牌就能耗掉服务器配额
令牌比对与限速
/** 令牌是否匹配(hash_equals 恒定时间比较,避免时序侧信道泄露) */
function cdc_token_ok(): bool {
$t = cdc_client_token();
return $t !== '' && hash_equals((string)TOKEN, $t); // TOKEN:config.php 中的共享令牌常量
}
/** 未带令牌的请求限速(每 IP 每分钟 N 次);带正确令牌的请求不计入 */
function cdc_rate_limited(): bool {
$fh = @fopen($f, 'c+');
if (!$fh) return false; // 拿不到锁 / 写不了文件 → 一律放行
@flock($fh, LOCK_EX);
...
$blocked = $b['n'] > $lim;
...
return $blocked;
}| 措施 | 目的 |
|---|---|
hash_equals | 恒定时间比较。普通 == 会在首个不同字符处提前返回,理论上可被计时推断 |
| 每 IP 每分钟限速 | 挡住无令牌的批量试探;正常用户根本触发不到 |
| 写不了文件就放行 | 宁可少拦也不误伤 —— 磁盘满或权限异常时不能让所有人用不了 |
接口分发
所有客户端请求都打到同一个入口,靠 op 字段分发:
| 分类 | op |
|---|---|
| 激活与配额 | activate · redeem · quota · bind_device |
| 账号 | register · login · reset |
| 私信 | msg_users · msg_peers · msg_send · msg_unread · msg_read · msg_conv |
| 版本 | version(同时供自动更新使用) |