mcpField
S
security

security

github.com/scientiacapital安全2878 次安装4 周前更新
代码仓库

关于 security

面向 Web 应用开发者的安全 Skill,提供身份验证、密钥管理、输入验证和常见漏洞防护指南,涵盖 Supabase、Next.js 与 Python 后端的实用模式,并附主题参考资料和部署检查清单。

主要功能

  1. 01应用 JWT 与会话安全模式
  2. 02管理并验证密钥
  3. 03验证和清理用户输入
  4. 04防范 SQL 注入、XSS 与 CSRF
  5. 05配置 Supabase RLS 和安全响应头
  6. 06检查部署安全清单

适用场景

  1. 01设计 Web 应用身份验证
  2. 02保护密钥与环境变量
  3. 03在应用边界验证数据
  4. 04部署前检查安全控制措施

安装

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

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

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

包含的文件

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

config.json
reference/
auth-patterns.md
input-validation.md
owasp-top-10.md
rls-policies.md
secrets-management.md

详细介绍

全面的安全技能,涵盖身份验证模式、密钥管理、输入验证和常见漏洞防护。侧重于使用 Supabase、Next.js 和 Python 后端构建 Web 应用的实用模式。

安全不是可选项,而是基本要求。这项技能可帮助你从一开始就构建安全的应用,而不是事后再补上安全措施。

<quick_start> 任何新项目的安全要点:

  1. 密钥:绝不提交到 git,并在启动时进行验证

    // .env (gitignored) + envSchema.parse(process.env)
    
  2. 身份验证:短时效 JWT + httpOnly cookie

    jwt.sign(payload, secret, { expiresIn: '15m' })
    
  3. 输入:使用 schema 验证所有内容

    const data = z.object({ email: z.string().email() }).parse(input)
    
  4. SQL:始终使用参数化查询(ORM 会处理这一点)

  5. RLS:为所有 Supabase 表启用 RLS,并配置按用户范围限定的策略 </quick_start>

<success_criteria> 安全实现成功的标准:

  • 所有密钥都存储在环境变量中,并在启动时进行验证
  • 版本控制中不包含任何密钥(使用 gitleaks 验证)
  • JWT 令牌有效期较短(≤15 min),并采用刷新令牌轮换机制
  • 使用 Zod 或类似的 schema 验证工具验证所有用户输入
  • 为所有数据库表启用 RLS,并配置适当的策略
  • 配置 CSP 标头(尽可能不使用 unsafe-inline)
  • 部署前完成安全检查清单 </success_criteria>

<security_mindset>

安全思维

核心原则
  1. 纵深防御——采用多层安全防护,而不是只靠一道屏障
  2. 最小权限——仅授予所需的最低访问权限
  3. 永不信任输入——验证来自用户和外部系统的所有内容
  4. 安全失败——发生错误时应拒绝访问,而不是授予访问权限
  5. 保守密钥——API 密钥绝不出现在代码或日志中
需要提出的安全问题

发布任何功能前:

  • 此功能会暴露哪些数据?
  • 谁可以访问此端点/页面?
  • 如果用户发送恶意输入,会发生什么?
  • 密钥是否得到了妥善保护?
  • 日志中是否记录了敏感数据? </security_mindset>
## 身份验证模式
JWT 与会话
方面JWT会话
存储客户端(localStorage/cookie)服务端(DB/Redis)
可扩展性无状态,易于扩展需要共享会话存储
撤销困难(需要黑名单)简单(从存储中删除即可)
大小较大(包含声明)较小(仅包含会话 ID)
适用场景API、微服务传统 Web 应用
JWT 最佳实践
// DO: Short-lived access tokens + refresh tokens
const accessToken = jwt.sign(
  { userId: user.id, role: user.role },
  process.env.JWT_SECRET,
  { expiresIn: '15m' } // Short-lived!
);

const refreshToken = jwt.sign(
  { userId: user.id },
  process.env.JWT_REFRESH_SECRET,
  { expiresIn: '7d' }
);

// DON'T: Long-lived tokens with sensitive data
// Bad example - never do this:
// { expiresIn: '365d' } // Too long!
// Including PII like SSN in token payload
令牌存储
方法防 XSS防 CSRF建议
localStorage否是避免用于身份验证
httpOnly cookie是否(需要 CSRF 令牌)推荐
内存(变量)是是最适合 SPA
刷新令牌流程
┌─────────┐                    ┌─────────┐                    ┌─────────┐
│ Client  │                    │  Server │                    │   DB    │
└────┬────┘                    └────┬────┘                    └────┬────┘
     │                              │                              │
     │  Login (email, password)     │                              │
     │─────────────────────────────>│                              │
     │                              │  Verify credentials          │
     │                              │─────────────────────────────>│
     │                              │<─────────────────────────────│
     │  Access token (15m)          │                              │
     │  Refresh token (7d)          │  Store refresh token hash    │
     │<─────────────────────────────│─────────────────────────────>│
     │                              │                              │
     │  API call + access token     │                              │
     │─────────────────────────────>│                              │
     │  Response                    │                              │
     │<─────────────────────────────│                              │
     │                              │                              │
     │  [Access token expired]      │                              │
     │  Refresh token               │                              │
     │─────────────────────────────>│  Verify refresh token        │
     │                              │─────────────────────────────>│
     │  New access token            │                              │
     │<─────────────────────────────│                              │
密码处理
import bcrypt from 'bcrypt';

// DO: Hash with sufficient rounds
const SALT_ROUNDS = 12; // ~300ms on modern hardware
const hash = await bcrypt.hash(password, SALT_ROUNDS);

// DO: Constant-time comparison
const isValid = await bcrypt.compare(inputPassword, storedHash);

// DON'T: Use weak hashing algorithms like MD5 or SHA1 for passwords
密码要求
const passwordSchema = z.string()
  .min(8, 'Minimum 8 characters')
  .max(128, 'Maximum 128 characters')
  .regex(/[a-z]/, 'Must contain lowercase')
  .regex(/[A-Z]/, 'Must contain uppercase')
  .regex(/[0-9]/, 'Must contain number')
  .regex(/[^a-zA-Z0-9]/, 'Must contain special character');

// Check against common passwords (haveibeenpwned API)
async function isPasswordPwned(password: string): Promise<boolean> {
  const sha1 = crypto.createHash('sha1').update(password).digest('hex').toUpperCase();
  const prefix = sha1.slice(0, 5);
  const suffix = sha1.slice(5);

  const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
  const text = await response.text();

  return text.includes(suffix);
}

<secrets_management>

密钥管理

**黄金法则:**绝不将密钥提交到版本控制。使用 .env 文件(加入 gitignore),在启动时使用 Zod schema 验证所有环境变量,支持同时启用多个密钥以便轮换,并且绝不记录敏感数据。

有关 gitignore 模式、环境变量模板、Zod 验证、轮换模式和日志脱敏,请参阅 reference/secrets-management.md。 </secrets_management>

<input_validation>

输入验证

在系统边界进行验证
┌─────────────────────────────────────────────────────────┐
│                    Your Application                      │
│                                                          │
│   ┌──────────┐     VALIDATE      ┌──────────────────┐  │
│   │  User    │ ───────────────>  │  Business Logic   │  │
│   │  Input   │                   │  (trusted data)   │  │
│   └──────────┘                   └──────────────────┘  │
│                                                          │
│   ┌──────────┐     VALIDATE      ┌──────────────────┐  │
│   │ External │ ───────────────>  │  Services         │  │
│   │   API    │                   │                   │  │
│   └──────────┘                   └──────────────────┘  │
└─────────────────────────────────────────────────────────┘
Schema 验证(Zod)
import { z } from 'zod';

// Define schemas
const createUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8).max(128),
  name: z.string().min(1).max(100),
  age: z.number().int().min(13).max(120).optional(),
});

// Validate input
export async function createUser(input: unknown) {
  const data = createUserSchema.parse(input); // Throws if invalid
  // data is now typed and validated
  return db.users.create(data);
}

// API handler
export async function POST(req: Request) {
  try {
    const body = await req.json();
    const user = await createUser(body);
    return Response.json(user, { status: 201 });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return Response.json({ errors: error.errors }, { status: 400 });
    }
    throw error;
  }
}
清理
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

const window = new JSDOM('').window;
const purify = DOMPurify(window);

// Sanitize HTML (for rich text fields)
const cleanHtml = purify.sanitize(userInput, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
  ALLOWED_ATTR: ['href']
});

// For plain text - escape or strip HTML
const plainText = purify.sanitize(userInput, { ALLOWED_TAGS: [] });

</input_validation>

<sql_injection>

SQL 注入防护

问题

攻击者可以通过未经清理的输入操纵 SQL 查询。 攻击载荷示例:'; DROP TABLE users; --

解决方案:参数化查询
// SAFE: Parameterized query (Prisma)
const user = await prisma.user.findUnique({
  where: { email: email }
});

// SAFE: Parameterized query (raw SQL)
const user = await db.query(
  'SELECT * FROM users WHERE email = $1',
  [email]
);

// SAFE: Supabase
const { data } = await supabase
  .from('users')
  .select()
  .eq('email', email);
Supabase RLS(行级安全)
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Users can only see their own posts
CREATE POLICY "Users see own posts"
  ON posts FOR SELECT
  USING (auth.uid() = user_id);

-- Users can only create posts as themselves
CREATE POLICY "Users create own posts"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = user_id);

-- Users can only update their own posts
CREATE POLICY "Users update own posts"
  ON posts FOR UPDATE
  USING (auth.uid() = user_id);

-- Users can only delete their own posts
CREATE POLICY "Users delete own posts"
  ON posts FOR DELETE
  USING (auth.uid() = user_id);

</sql_injection>

<xss_prevention>

XSS 防护

问题

攻击者注入恶意脚本,使其在受害者的浏览器中执行,从而窃取 cookie/数据。

解决方案:自动转义 + CSP
// React auto-escapes by default - this is safe
return <div>Welcome, {userName}</div>;

// AVOID rendering raw HTML from user input
// If you absolutely must render user HTML, ALWAYS sanitize with DOMPurify first
import DOMPurify from 'dompurify';
const sanitizedContent = DOMPurify.sanitize(userContent);

</xss_prevention>

内容安全策略
// next.config.js
const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: [
      "default-src 'self'",
      "script-src 'self'", // Avoid 'unsafe-inline' if possible
      "style-src 'self' 'unsafe-inline'",
      "img-src 'self' data: https:",
      "font-src 'self'",
      "connect-src 'self' https://api.supabase.co",
    ].join('; ')
  }
];

</xss_prevention>

<csrf_protection>

CSRF 防护

问题

攻击者会诱骗已通过身份验证的用户向你的网站提交恶意请求。

解决方案:CSRF 令牌 + SameSite Cookie
// Server: Generate token
import { randomBytes } from 'crypto';

function generateCsrfToken(): string {
  return randomBytes(32).toString('hex');
}

// Store in session
session.csrfToken = generateCsrfToken();

// Client: Include in forms as hidden field
// Server: Validate token matches session

// Modern approach: SameSite cookies (most effective)
res.cookie('session', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict', // or 'lax'
});

</csrf_protection>

如需了解详细模式,请加载相应的参考文件:
主题参考文件加载时机
身份验证模式reference/auth-patterns.mdJWT、OAuth、会话
密钥reference/secrets-management.mdAPI 密钥、环境变量
输入验证reference/input-validation.md清理、架构
Supabase RLSreference/rls-policies.md行级安全
OWASP Top 10reference/owasp-top-10.md漏洞检查清单

加载方式: 请求特定主题,或查看上下文是否有相关提示。

## 安全检查清单

部署前:

身份验证
  • 密码使用 bcrypt 哈希(12 轮以上)
  • JWT 令牌有效期较短(最长 15 分钟)
  • 安全存储刷新令牌
  • 会话 Cookie 设置 httpOnly + secure + sameSite
密钥
  • 代码或版本控制中不包含密钥
  • 启动时验证环境变量
  • 不记录密钥日志
输入
  • 使用架构验证所有用户输入
  • SQL 使用参数化查询
  • 渲染前清理 HTML
  • 验证文件上传(类型、大小、名称)
标头
  • 强制使用 HTTPS
  • 配置 CSP 标头
  • 将 CORS 限制为允许的源
  • 设置安全标头(HSTS、X-Frame-Options)
授权
  • 为所有表启用 RLS
  • API 端点检查权限
  • 保护管理路由
  • 对身份验证端点启用速率限制

输出结果旁文件

最后一步,将内容写入 ~/.claude/skill-analytics/last-outcome-security.json:

{"ts":"[UTC ISO8601]","skill":"security","version":"1.0.0","variant":"default",
 "status":"[success|partial|error]","runtime_ms":[estimated ms from start],
 "metrics":{"vulnerabilities_found":[n],"fixes_applied":[n],"audit_checks_passed":[n]},
 "error":null,"session_id":"[YYYY-MM-DD]"}

如果部分阶段失败但仍生成了结果,请使用状态 "partial"。仅当未生成任何输出时,才使用 "error"。

评论

支持 Markdown