关于 meteor-security
面向 Meteor 开发者的安全指南,涵盖方法与发布的访问控制、内容安全策略、速率限制及 OAuth 密钥加密。
主要功能
- 01检查方法参数并验证身份
- 02为发布设置用户过滤和字段投影
- 03配置 CSP 与浏览器策略
- 04为敏感方法添加速率限制
- 05加密 OAuth 提供方密钥
适用场景
- 01审查方法和发布的访问控制
- 02防止发布敏感字段或无限量数据
- 03保护应用免受脚本和框架风险
- 04限制登录及密码重置请求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ meteor/ agent-skills --skill meteor-security安装后在 Claude Code 中输入/meteor-security确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/browser-policy-csp.mdeval-cases.mdmethod-and-publish-guards.md详细介绍
Meteor 安全
Meteor 的安全模型立场鲜明:服务器掌握权限,不能信任客户端,只有方法(写入路径)和发布(读取路径)会在数据到达用户之前进行筛选。
决策流程
- 审查每个方法:是否对每个参数都调用
check(),并在需要身份验证时检查this.userId或Meteor.userId()? - 审查每个发布:是否按
this.userId筛选(适用于特定用户的数据),并使用fields投影列? - 在开发环境中添加
audit-argument-checks,以捕获遗漏的check()。 - 添加
browser-policy并配置 CSP。 - 为敏感方法(登录、密码重置、资源创建)添加
DDPRateLimiter规则。 - 如果应用使用 OAuth,请设置
oauthSecretKey,以加密静态存储的提供商密钥。 - 将宽松的客户端写入规则替换为经过身份验证的方法。在等效的仅方法策略启用前,保留保护性
deny规则;即使应用没有allow规则,Accounts 也可以添加仅限所有者的profile许可。请参阅下文的 profile 策略。
Accounts 集合和 profile 策略
在 Meteor 3.6 固定的 Accounts 配对机制中,自定义用户集合会获得账户索引;当存在客户端变更方法时,还会获得默认的仅限所有者的 profile 许可。安装保护措施前,需在两种运行环境中配置集合。如果必须阻止所有客户端 profile 写入,请在所选集合上保留 Meteor.users.deny({ update: () => true }),直到确认等效策略有效。defineMutationMethods: false 会提供一个不带这些验证器的仅方法集合;RC 仍会创建账户索引。更早版本的包需要检查其设置行为。有关集合标识、次要 Accounts 实例和包边界,请参阅 meteor-accounts。绝不要信任存储在客户端可编辑 profile 中的角色。
方法保护检查清单
import { Meteor } from "meteor/meteor";
import { check, Match } from "meteor/check";
Meteor.methods({
async updateProfile(payload) {
check(payload, { displayName: String, bio: Match.Optional(String) });
if (!this.userId) {
throw new Meteor.Error("not-authorized");
}
await Meteor.users.updateAsync(this.userId, { $set: { profile: payload } });
},
async updateAddress(payload) {
check(payload, String);
if (!Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
await Meteor.users.updateAsync(Meteor.userId(), { $set: { address: payload } });
},
});
拒绝任何不符合以下要求的方法:对每个参数调用 check,在需要时通过 userId 检查身份,并在失败时使用 Meteor.Error(code, reason),且在服务器上使用 *Async Mongo。
发布保护检查清单
Meteor.publish("items.mine", function () {
if (!this.userId) return this.ready();
return Items.find(
{ ownerId: this.userId },
{ fields: { title: 1, qty: 1 }, limit: 200 },
);
});
拒绝任何返回无界游标、遗漏字段投影,或未对特定用户的数据执行 userId 筛选的发布。
通过 browser-policy 配置 CSP
meteor add browser-policy
// server top-level or inside Meteor.startup
import { BrowserPolicy } from "meteor/browser-policy-common";
import { Meteor } from "meteor/meteor";
Meteor.startup(async () => {
await BrowserPolicy.content.disallowInlineScripts();
BrowserPolicy.content.disallowEval();
BrowserPolicy.framing.disallow();
});
BrowserPolicy 仅用于服务器端。应在模块初始化或启动期间进行配置,以便每个请求都采用确定的进程级策略。当前实现会在策略变更后使其缓存的 CSP 失效,但不要针对每个请求或每个用户变更此全局策略。配置示例(Stripe、Google Maps、字体、内联样式许可)请参阅 references/browser-policy-csp.md。
敏感方法的 DDPRateLimiter
import { DDPRateLimiter } from "meteor/ddp-rate-limiter";
DDPRateLimiter.addRule(
{
type: "method",
name: "login",
clientAddress: () => true,
},
5,
60000, // 5 attempts per 60s, per IP
);
只有匹配器字段会参与速率限制桶键的构成。如果没有
clientAddress、connectionId 或 userId,所有匹配的调用方都会共用一个
全局桶。Meteor 3.5+ 允许匹配器函数异步执行数据库决策;由于连接会等待这些函数返回,请确保查询速度足够快。在
Meteor 3.0 到 3.4 中,匹配器必须保持同步。请使用固定规则、预先计算的同步状态,或升级版本,而不要在此处等待 Mongo 查询。
accounts-base 内置了默认规则(每个匹配方法、每个 DDP 连接在 10 秒内最多 5 次,适用于登录 / 注册 / 密码重置)。Meteor 3.6-beta.1 的
[email protected] 还会匹配
requestLoginTokenForUser;[email protected] 会在查找 / 创建前验证完整载荷。RC 版本的配对为
[email protected] 和 [email protected];处理回调中的速率限制错误时,不要立即进入重试循环。对于较旧的应用,请检查这两个包的版本。只有在替换默认规则时,才使用 Accounts.removeDefaultRateLimit() 将其移除。
对于 [email protected] 的 Cookie 加固,请使用
meteor-accounts:受信任的源、有效令牌、no-store 响应和 SameSite=Strict 都有特定的迁移检查。其 HTTP 端点每个客户端地址在 10 秒内最多允许 30 次请求,与 DDP 限制相互独立。HttpOnly 存储无法阻止活跃的同源 XSS 获取用于 DDP 身份验证的恢复令牌。
OAuth 密钥加密
添加 oauth-encryption,并在模块顶层(而不是 Meteor.startup 内)将一个 16 字节的 base64 密钥(不是 32 字节)传给
Accounts.config:
meteor node -e "console.log(require('crypto').randomBytes(16).toString('base64'))"
import { Accounts } from "meteor/accounts-base";
Accounts.config({
oauthSecretKey: Meteor.settings.oauthSecretKey,
});
启动时,accounts-oauth 会封装 ServiceConfiguration.configurations.secret 中尚未封装的提供商应用密钥。提供商包也会封装受支持的用户令牌字段,例如 services.github.accessToken 或 Twitter 的 accessTokenSecret。不存在通用的
Meteor.users.services.<provider>.secret 字段。在断言哪些用户凭据经过加密之前,请先检查提供商的架构。
audit-argument-checks
meteor add audit-argument-checks
如果有方法或发布函数运行时,未使用 check() 检查每个参数,则会抛出异常。对于确实需要接受任意输入的方法,请明确声明这一点:
Meteor.methods({
rawLog(...args) {
check(args, [Match.Any]);
// ...
},
});
反模式
- 对敏感数据采用宽松的客户端写入规则。请改用受保护的方法;在证明策略等效之前,不要移除防护性拒绝规则。
- 使用
Meteor.settings.public.<secret>。客户端可以看到public。请将密钥移至settings.json的顶层。 - 发布整个
Meteor.users集合。请始终投影(例如fields: { username: 1, profile: 1 })并进行筛选。仅向用户本人或其他明确授权的受众发布电子邮件地址。 - 对第三方脚本使用
BrowserPolicy.content.allowOriginForAll。它会将该源授权给所有当前内容指令。只允许集成所需的脚本、框架、连接、图像、样式或字体指令。 - 接受类似回调的参数的方法。函数无法通过 DDP 传输。
- 在
Meteor.startup内调用Accounts.config({ oauthSecretKey })。 必须放在模块顶层,以便在 OAuth 包读取之前加载。
另请参阅
references/method-and-publish-guards.mdreferences/browser-policy-csp.mdreferences/eval-cases.md- 相关技能:
meteor-methods、meteor-pubsub、meteor-accounts。