mcpField
M
meteor-security

meteor-security

github.com/meteor安全10295 次安装5 天前更新
代码仓库

关于 meteor-security

面向 Meteor 开发者的安全指南,涵盖方法与发布的访问控制、内容安全策略、速率限制及 OAuth 密钥加密。

主要功能

  1. 01检查方法参数并验证身份
  2. 02为发布设置用户过滤和字段投影
  3. 03配置 CSP 与浏览器策略
  4. 04为敏感方法添加速率限制
  5. 05加密 OAuth 提供方密钥

适用场景

  1. 01审查方法和发布的访问控制
  2. 02防止发布敏感字段或无限量数据
  3. 03保护应用免受脚本和框架风险
  4. 04限制登录及密码重置请求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/meteor/agent-skills --skill meteor-security

安装后在 Claude Code 中输入/meteor-security确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
browser-policy-csp.md
eval-cases.md
method-and-publish-guards.md

详细介绍

Meteor 安全

Meteor 的安全模型立场鲜明:服务器掌握权限,不能信任客户端,只有方法(写入路径)和发布(读取路径)会在数据到达用户之前进行筛选。

决策流程

  1. 审查每个方法:是否对每个参数都调用 check(),并在需要身份验证时检查 this.userId 或 Meteor.userId()?
  2. 审查每个发布:是否按 this.userId 筛选(适用于特定用户的数据),并使用 fields 投影列?
  3. 在开发环境中添加 audit-argument-checks,以捕获遗漏的 check()。
  4. 添加 browser-policy 并配置 CSP。
  5. 为敏感方法(登录、密码重置、资源创建)添加 DDPRateLimiter 规则。
  6. 如果应用使用 OAuth,请设置 oauthSecretKey,以加密静态存储的提供商密钥。
  7. 将宽松的客户端写入规则替换为经过身份验证的方法。在等效的仅方法策略启用前,保留保护性 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.md
  • references/browser-policy-csp.md
  • references/eval-cases.md
  • 相关技能:meteor-methods、meteor-pubsub、meteor-accounts。

评论

支持 Markdown