mcpField
B
better-auth-security-best-practices

better-auth-security-best-practices

github.com/better-auth安全22748.7k 次安装8 周前更新
代码仓库

关于 better-auth-security-best-practices

面向应用开发者的 Better Auth 安全配置指南,涵盖密钥、速率限制、CSRF、可信来源、会话、Cookie、OAuth 令牌和审计钩子。

主要功能

  1. 01密钥配置指南
  2. 02速率限制与存储选项
  3. 03CSRF 和可信来源控制
  4. 04会话与 Cookie 安全
  5. 05OAuth 令牌保护
  6. 06安全审计钩子

适用场景

  1. 01为生产环境配置 Better Auth
  2. 02设置敏感端点的请求限制
  3. 03保护会话和跨子域 Cookie
  4. 04为认证操作添加审计日志

安装

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

$npx skills add https://github.com/better-auth/skills --skill better-auth-security-best-practices

安装后在 Claude Code 中输入/better-auth-security-best-practices确认已加载。

详细介绍

密钥管理

配置密钥
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  secret: process.env.BETTER_AUTH_SECRET, // or via `BETTER_AUTH_SECRET` env
});

Better Auth 按以下顺序查找密钥:

  1. 配置中的 options.secret
  2. BETTER_AUTH_SECRET 环境变量
  3. AUTH_SECRET 环境变量
密钥要求
  • 在生产环境中拒绝默认或占位密钥
  • 如果密钥长度少于 32 个字符或熵低于 120 位,则发出警告
  • 生成密钥:openssl rand -base64 32
  • 切勿将密钥提交到版本控制系统

速率限制

默认在生产环境中启用,并应用于所有端点。插件可以覆盖特定端点的设置。

默认配置
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  rateLimit: {
    enabled: true, // Default: true in production
    window: 10, // Time window in seconds (default: 10)
    max: 100, // Max requests per window (default: 100)
  },
});
存储选项

可选项:"memory"(重启后重置,请勿用于无服务器环境)、"database"(持久化)、"secondary-storage"(Redis;有可用存储时默认使用)。

rateLimit: {
  storage: "database",
}
自定义存储

自行实现速率限制存储:

rateLimit: {
  customStorage: {
    get: async (key) => {
      // Return { count: number, expiresAt: number } or null
    },
    set: async (key, data) => {
      // Store the rate limit data
    },
  },
}
特定端点规则

敏感端点默认每 10 秒允许 3 次请求(/sign-in、/sign-up、/change-password、/change-email)。覆盖设置:

rateLimit: {
  customRules: {
    "/api/auth/sign-in/email": {
      window: 60, // 1 minute window
      max: 5, // 5 attempts
    },
    "/api/auth/some-safe-endpoint": false, // Disable rate limiting
  },
}

CSRF 防护

多层防护:源站标头验证、Fetch Metadata 检查和首次登录防护。

配置
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    disableCSRFCheck: false, // Default: false (keep enabled)
  },
});

仅在测试或使用其他 CSRF 防护机制时禁用。

可信来源

配置可信来源
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  baseURL: "https://api.example.com",
  trustedOrigins: [
    "https://app.example.com",
    "https://admin.example.com",
  ],
});

baseURL 的源站会自动设为可信来源。也可以通过环境变量配置:BETTER_AUTH_TRUSTED_ORIGINS=https://app.example.com,https://admin.example.com

通配符模式
trustedOrigins: [
  "*.example.com", // Matches any subdomain
  "https://*.example.com", // Protocol-specific wildcard
  "exp://192.168.*.*:*/*", // Custom schemes (e.g., Expo)
]
动态可信来源

根据请求计算可信来源:

trustedOrigins: async (request) => {
  // Validate against database, header, etc.
  const tenant = getTenantFromRequest(request);
  return [`https://${tenant}.myapp.com`];
}

会针对可信来源验证 callbackURL、redirectTo、errorCallbackURL、newUserCallbackURL 和 origin。无效 URL 会收到 403 响应。

会话安全

会话过期
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days (default)
    updateAge: 60 * 60 * 24, // Refresh session every 24 hours (default)
  },
});
会话缓存策略

将会话数据缓存在 Cookie 中,以减少数据库查询:

session: {
  cookieCache: {
    enabled: true,
    maxAge: 60 * 5, // 5 minutes
    strategy: "compact", // Options: "compact", "jwt", "jwe"
  },
}

策略:"compact"(Base64url + HMAC,体积最小)、"jwt"(HS256,标准方式)、"jwe"(加密;会话包含敏感数据时使用)。

Cookie 安全

默认值:secure: true(HTTPS/生产环境)、sameSite: "lax"、httpOnly: true、path: "/",前缀为 __Secure-。

自定义 Cookie 配置
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    useSecureCookies: true, // Force secure cookies
    cookiePrefix: "myapp", // Custom prefix (default: "better-auth")
    defaultCookieAttributes: {
      sameSite: "strict", // Stricter CSRF protection
      path: "/auth", // Limit cookie scope
    },
  },
});
跨子域 Cookie
advanced: {
  crossSubDomainCookies: {
    enabled: true,
    domain: ".example.com", // Note the leading dot
    additionalCookies: ["session_token", "session_data"],
  },
}

仅在需要共享身份验证且信任所有子域时启用。

OAuth/社交服务提供商安全

所有 OAuth 流程都会自动使用 PKCE。State 令牌是 32 个字符的随机字符串,10 分钟后过期。

State 参数存储
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  account: {
    storeStateStrategy: "cookie", // Options: "cookie" (default), "database"
  },
});
加密 OAuth 令牌
account: {
  encryptOAuthTokens: true, // Uses AES-256-GCM
}

如果要存储 OAuth 令牌,以代表用户访问 API,请启用此选项。仅当无法维持 Cookie 的移动应用需要时,才使用 skipStateCookieCheck: true。

基于 IP 的安全防护

IP 地址配置
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    ipAddress: {
      ipAddressHeaders: ["x-forwarded-for", "x-real-ip"], // Headers to check
      disableIpTracking: false, // Keep enabled for rate limiting
    },
  },
});

设置 ipv6Subnet(128、64、48、32;默认为 64)以对 IPv6 地址分组。仅当处于可信反向代理之后时,才启用 trustedProxyHeaders: true。

用于安全审计的数据库钩子

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  databaseHooks: {
    session: {
      create: {
        after: async ({ data, ctx }) => {
          await auditLog("session.created", {
            userId: data.userId,
            ip: ctx?.request?.headers.get("x-forwarded-for"),
            userAgent: ctx?.request?.headers.get("user-agent"),
          });
        },
      },
      delete: {
        before: async ({ data }) => {
          await auditLog("session.revoked", { sessionId: data.id });
        },
      },
    },
    user: {
      update: {
        after: async ({ data, oldData }) => {
          if (oldData?.email !== data.email) {
            await auditLog("user.email_changed", {
              userId: data.id,
              oldEmail: oldData?.email,
              newEmail: data.email,
            });
          }
        },
      },
    },
    account: {
      create: {
        after: async ({ data }) => {
          await auditLog("account.linked", {
            userId: data.userId,
            provider: data.providerId,
          });
        },
      },
    },
  },
});

在 before 钩子中返回 false,即可阻止操作。

后台任务

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    backgroundTasks: {
      handler: (promise) => {
        // Platform-specific handler
        // Vercel: waitUntil(promise)
        // Cloudflare: ctx.waitUntil(promise)
        waitUntil(promise);
      },
    },
  },
});

确保发送电子邮件等操作不会影响响应时间。

防止账号枚举

内置措施:使用一致的响应消息、对无效请求执行虚拟操作、在后台发送电子邮件。返回通用错误消息(“凭据无效”),而非具体错误消息(“未找到用户”)。

完整安全配置示例

import { betterAuth } from "better-auth";

export const auth = betterAuth({
  secret: process.env.BETTER_AUTH_SECRET,
  baseURL: "https://api.example.com",
  trustedOrigins: [
    "https://app.example.com",
    "https://*.preview.example.com",
  ],
  
  // Rate limiting
  rateLimit: {
    enabled: true,
    storage: "secondary-storage",
    customRules: {
      "/api/auth/sign-in/email": { window: 60, max: 5 },
      "/api/auth/sign-up/email": { window: 60, max: 3 },
    },
  },
  
  // Session security
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    updateAge: 60 * 60 * 24, // 24 hours
    freshAge: 60 * 60, // 1 hour for sensitive actions
    cookieCache: {
      enabled: true,
      maxAge: 300,
      strategy: "jwe", // Encrypted session data
    },
  },
  
  // OAuth security
  account: {
    encryptOAuthTokens: true,
    storeStateStrategy: "cookie",
  },
  
  
  // Advanced settings
  advanced: {
    useSecureCookies: true,
    cookiePrefix: "myapp",
    defaultCookieAttributes: {
      sameSite: "lax",
    },
    ipAddress: {
      ipAddressHeaders: ["x-forwarded-for"],
      ipv6Subnet: 64,
    },
    backgroundTasks: {
      handler: (promise) => waitUntil(promise),
    },
  },
  
  // Security auditing
  databaseHooks: {
    session: {
      create: {
        after: async ({ data, ctx }) => {
          console.log(`New session for user ${data.userId}`);
        },
      },
    },
    user: {
      update: {
        after: async ({ data, oldData }) => {
          if (oldData?.email !== data.email) {
            console.log(`Email changed for user ${data.id}`);
          }
        },
      },
    },
  },
});

安全检查清单

部署到生产环境之前:

  • 密钥:使用强度高且唯一的密钥(32 个以上字符,具有高熵)
  • HTTPS:确保 baseURL 使用 HTTPS
  • 可信来源:配置所有有效来源(前端、移动应用)
  • 速率限制:保持启用,并设置适当的限制
  • CSRF 保护:保持启用(disableCSRFCheck: false)
  • 安全 Cookie:使用 HTTPS 时自动启用
  • OAuth 令牌:如果要存储令牌,请考虑设置 encryptOAuthTokens: true
  • 后台任务:为无服务器平台进行配置
  • 审计日志:通过 databaseHooks 或 hooks 实现
  • IP 跟踪:如果位于代理之后,请配置请求头

评论

支持 Markdown