Files
vibe_coding/.cursor/rules/references/026-secure-coding-deep.md
2026-03-05 21:27:11 +08:00

6.8 KiB
Raw Blame History

026-secure-coding.mdc (Deep Reference)

该文件为原始详细规范归档,供 Tier 3 按需读取。


🔐 Secure Coding Standards (Project CodeGuard)

安全不是事后审查,而是编码时的默认选择。

禁用加密算法(绝对禁止)

以下算法已被证实不安全,禁止在任何新代码中使用

类型 禁用 替代
哈希 MD2、MD4、MD5、SHA-0、SHA-1 SHA-256、SHA-3
对称加密 RC2、RC4、Blowfish、DES、3DES、AES-ECB AES-GCM、ChaCha20-Poly1305
密钥交换 静态 RSA、匿名 DH ECDHE (X25519)
// ❌ 禁止
$hash = md5($password);
$hash = sha1($data);
openssl_encrypt($data, 'AES-128-ECB', $key);

// ✅ 正确
$hash = password_hash($password, PASSWORD_ARGON2ID);
$hash = hash('sha256', $data);
openssl_encrypt($data, 'AES-256-GCM', $key, 0, $iv, $tag);

💉 注入防护

SQL 注入

  • 100% 使用参数化查询,禁止字符串拼接 SQL
  • 使用 Hyperf ORM 或 PDO 绑定参数
// ❌ 禁止
$result = Db::select("SELECT * FROM users WHERE name = '{$name}'");

// ✅ 正确
$result = Db::select('SELECT * FROM users WHERE name = ?', [$name]);
// 或
User::where('name', $name)->first();

OS 命令注入

  • 优先使用内置函数替代 shell 调用
  • 禁止将用户输入直接传入 exec()system()shell_exec()
// ❌ 禁止
exec("convert {$userFile} output.png");

// ✅ 正确(使用内置库)
$image = new Imagick($sanitizedPath);

TypeScript Prototype Pollution

  • 使用 Map / Set 替代对象字面量存储用户数据
  • 合并对象时拦截 __proto__constructorprototype
// ❌ 禁止
function merge(target, source) {
  Object.assign(target, source) // 可能导致 prototype pollution
}

// ✅ 正确
const safe = Object.create(null)
const blocked = ['__proto__', 'constructor', 'prototype']
Object.keys(source).filter(k => !blocked.includes(k)).forEach(k => { safe[k] = source[k] })

🔑 密码存储

// ❌ 禁止bcrypt 有 72 字节截断限制)
$hash = password_hash($password, PASSWORD_BCRYPT);

// ✅ 推荐Argon2id更安全无长度限制
$hash = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,  // 64 MiB
    'time_cost'   => 2,
    'threads'     => 1,
]);

// 验证(常量时间比较,防时序攻击)
$valid = password_verify($input, $hash);

🔒 访问控制IDOR / Mass Assignment 防护)

IDOR 防护

  • 永远不要只凭用户传入的 ID 查询资源,必须附加所有权校验
// ❌ 禁止 — 用户可访问任意 ID 的数据
$order = Order::find($request->input('id'));

// ✅ 正确 — 限定当前用户范围
$order = $this->user->orders()->findOrFail($request->input('id'));

Mass Assignment 防护

  • Hyperf FormRequest 必须声明 rules() 白名单
  • Model 必须使用 $fillable 而非 $guarded = []
// ❌ 禁止
$user->fill($request->all());

// ✅ 正确(只允许明确字段)
$user->fill($request->only(['name', 'email', 'avatar']));
// 登录成功后必须轮转 Session ID防 Session Fixation
session_regenerate_id(true);

// Cookie 必须设置安全属性
setcookie('session', $id, [
    'secure'   => true,        // 仅 HTTPS
    'httponly' => true,        // 禁止 JS 访问
    'samesite' => 'Strict',    // 防 CSRF
    'path'     => '/',
]);
  • Session 超时:高风险操作 5 分钟,常规 30 分钟,绝对上限 8 小时
  • 禁止在 localStorage / sessionStorage 存储 Session TokenXSS 可窃取)

🌐 客户端安全Vue 3 / TypeScript

XSS 防护

// ❌ 禁止 — 直接使用 v-html 且无净化
// <div v-html="userContent"></div>

// ✅ 正确 — 使用 DOMPurify 净化
import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(userContent, {
  ALLOWED_TAGS: ['b', 'i', 'p', 'a', 'ul', 'li'],
  ALLOWED_ATTR: ['href'],
})
// <div v-html="clean"></div>

// ❌ 禁止 — 动态代码执行
eval(userCode)
new Function(userCode)()
setTimeout(userString, 100)

// ❌ 禁止 — 直接 DOM 写入
element.innerHTML = userInput
document.write(userInput)

外链安全

<!-- ✅ 所有 target="_blank" 必须加防护 -->
<a href="..." target="_blank" rel="noopener noreferrer">外链</a>

CSRF 防护

  • 所有状态变更请求POST/PUT/DELETE/PATCH必须携带 CSRF Token
  • 禁止用 GET 请求执行状态变更操作

📁 文件上传安全

// ✅ 文件上传安全检查清单

// 1. 白名单扩展名(不是黑名单)
$allowed = ['jpg', 'png', 'gif', 'pdf'];
$ext = strtolower(pathinfo($file->getClientFilename(), PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) throw new ValidationException('不允许的文件类型');

// 2. 验证 magic bytes不信任 Content-Type
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);

// 3. 生成随机文件名(不使用用户提供的文件名)
$safeName = Str::uuid() . '.' . $ext;

// 4. 存储在 webroot 外
$storagePath = BASE_PATH . '/storage/uploads/' . $safeName;

// 5. 设置文件大小限制
if ($file->getSize() > 10 * 1024 * 1024) throw new ValidationException('文件过大');

📊 安全日志规范

// ❌ 禁止记录敏感信息
Log::info('用户登录', ['password' => $password, 'token' => $token]);

// ✅ 正确 — 只记录安全信息,用 hash 标识 session
Log::info('用户登录', [
    'user_id'    => $userId,
    'ip'         => $request->getServerParams()['REMOTE_ADDR'],
    'user_agent' => substr($request->getHeaderLine('user-agent'), 0, 200),
    'session_id' => substr(hash('sha256', $sessionId), 0, 16), // 前16位不泄露原值
    'timestamp'  => date('c'),
]);

⚙️ API 安全

  • 禁止在 URL 参数中传递敏感数据(会出现在日志里)
  • GraphQL生产环境禁用 introspection
  • SSRF所有外部 HTTP 请求必须验证目标域名白名单
// SSRF 防护示例
$allowedDomains = ['api.trusted.com', 'cdn.trusted.com'];
$parsedUrl = parse_url($userProvidedUrl);
if (!in_array($parsedUrl['host'], $allowedDomains)) {
    throw new SecurityException('不允许的目标地址');
}
// 还需阻断私有 IP 范围10.x, 172.16.x, 192.168.x, 127.x

编码时自查清单

每次提交前确认:

  • 无 MD5/SHA1/DES/AES-ECB 使用
  • SQL 查询 100% 参数化
  • 资源查询已限定所有权范围(无 IDOR 风险)
  • $request->all() 直接绑定模型
  • Cookie 包含 Secure + HttpOnly + SameSite 属性
  • v-html 使用了 DOMPurify 净化
  • 文件上传使用 UUID 文件名 + magic bytes 验证
  • 日志未包含明文密码/Token/Session ID

📚 深度参考:.cursor/skills/security-audit/references/codeguard/