跳转到正文
报告库
用途分类 / 数据分析

Nodejs Backend Patterns Skill 安全审计

作者说它能做什么(原文)

Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.

第三方安全检查结论

先别安装或运行

已检查文件
3
发现的风险
6
会不会运行危险命令?检查是否下载程序后直接运行、让他人远程控制电脑,或藏起要运行的命令。未发现风险
会不会泄露文件和密钥?检查是否发送含密码或密钥的文件,以及代码里是否直接写了密钥。发现 3 项风险
高风险

更新示例把请求字段名直接拼入 SQL,可造成 SQL 注入或越权修改列

原文依据:2 处
发现了什么

控制器把整个请求体作为 updates 传递,而仓储层用 Object.keys(updates) 生成 SQL 标识符。只有字段值使用了参数占位符;攻击者控制的字段名仍被直接插入查询。TypeScript DTO 不会在运行时过滤 HTTP JSON。

为什么需要注意

若更新端点可被攻击者调用,构造的字段名可能改写角色、权限或其他未授权列,也可能改变查询结构或使查询持续失败。操作会以应用数据库账户的权限执行。

这是文档中的可复制示例,不会因读取 Skill 自动执行;但若用户采用该更新流程,请求体会直接成为 updates,而运行时没有展示字段白名单。仓储层只参数化值,却把 Object.keys(updates) 直接拼成 SQL 列名。恶意字段名可能改变 SQL 结构,普通额外字段也可能造成越权列更新。用户可要求作者加入运行时 schema 严格模式及固定的“允许更新列”映射。

references/details.md:140来自说明文档打开原文件
  async updateUser(req: Request, res: Response, next: NextFunction) {    try {      const { id } = req.params;      const updates: UpdateUserDTO = req.body;      const user = await this.userService.updateUser(id, updates);      res.json(user);    } catch (error) {
查看另外 1 个位置
references/details.md:261来自说明文档打开原文件
  async update(id: string, updates: UpdateUserDTO): Promise<UserEntity | null> {    const fields = Object.keys(updates);    const values = Object.values(updates);    const setClause = fields      .map((field, idx) => `${field} = $${idx + 2}`)      .join(", ");    const query = `      UPDATE users      SET ${setClause}, updated_at = CURRENT_TIMESTAMP      WHERE id = $1      RETURNING *    `;    const { rows } = await this.db.query(query, [id, ...values]);    return rows[0] || null;
中风险

请求日志记录完整 URL、IP 和 User-Agent,可能把令牌或个人信息写入日志

原文依据:1 处
发现了什么

日志中直接保存 req.url、req.ip 和 User-Agent。完整 URL 包括查询字符串;若应用把令牌、邮箱、搜索内容或重置码放在查询参数中,这些内容会进入日志系统。

为什么需要注意

能够读取本地日志、集中日志平台或支持导出的人员可能获得账户令牌、个人标识和用户活动信息,且日志保留时间通常长于原始请求。

该日志中间件是示例,并非 Skill 自行收集数据。若应用采用它,完成每个响应时会记录 req.url、IP 和 User-Agent;req.url 可包含查询字符串,所以放在 URL 中的重置码、令牌、邮箱或搜索内容会进入日志。影响取决于应用是否把敏感值放入 URL,以及日志的访问和保留范围。用户可要求只记录路径,并对查询参数和标识符进行删除或脱敏。

references/details.md:457来自说明文档打开原文件
  // Log response when finished  res.on("finish", () => {    const duration = Date.now() - start;    logger.info({      method: req.method,      url: req.url,      status: res.statusCode,      duration: `${duration}ms`,      userAgent: req.headers["user-agent"],      ip: req.ip,    });  });
中风险

通用缓存装饰器把全部参数放入 Redis 键并缓存完整结果

原文依据:4 处
发现了什么

缓存键由方法名和 JSON.stringify(args) 组成,结果也完整序列化进 Redis。用于登录、用户、支付或令牌相关方法时,密码、令牌、邮箱和返回记录可能同时出现在键和值中;键通常还会被监控和诊断工具列出。

为什么需要注意

Redis 管理员、备份、监控系统或取得缓存访问权的攻击者可能读取敏感参数和响应。不同对象若生成相同方法名和参数键,还可能发生不适当的跨上下文缓存复用。

风险是有条件的:资料没有展示该装饰器实际用于登录、支付或用户方法,但它是无类型限制的通用装饰器。采用后,它会把所有参数的 JSON 表示放入 Redis 键,并把完整返回值序列化为缓存值;用于敏感方法时可能暴露密码、令牌或个人资料,还可能造成跨用户缓存混用。用户可要求明确禁止敏感方法、使用散列且带租户/用户作用域的键,并只缓存经过筛选的结果。

references/advanced-patterns.md:333来自说明文档打开原文件
  async set(key: string, value: any, ttl?: number): Promise<void> {    const serialized = JSON.stringify(value);    if (ttl) {      await redis.setex(key, ttl, serialized);    } else {      await redis.set(key, serialized);    }  }
查看另外 3 个位置
references/advanced-patterns.md:363来自说明文档打开原文件
    descriptor.value = async function (...args: any[]) {      const cache = new CacheService();      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;      const cached = await cache.get(cacheKey);      if (cached) {        return cached;      }      const result = await originalMethod.apply(this, args);      await cache.set(cacheKey, result, ttl);
references/advanced-patterns.md:355来自说明文档打开原文件
// Cache decoratorexport function Cacheable(ttl: number = 300) {  return function (    target: any,    propertyKey: string,    descriptor: PropertyDescriptor,  ) {    const originalMethod = descriptor.value;    descriptor.value = async function (...args: any[]) {      const cache = new CacheService();      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;
references/advanced-patterns.md:367来自说明文档打开原文件
      const cached = await cache.get(cacheKey);      if (cached) {        return cached;      }      const result = await originalMethod.apply(this, args);      await cache.set(cacheKey, result, ttl);      return result;    };
会不会删除文件或一直在后台运行?检查是否大范围删除文件、改写磁盘,或设置自动启动。未发现风险
会不会绕过安全保护?检查是否跳过网站安全验证、开放过多文件权限,或取消操作前的确认。发现 2 项风险
中风险

七天刷新令牌没有轮换或撤销检查,失窃后可持续换取访问令牌

原文依据:2 处
发现了什么

refreshToken 只验证 JWT 签名并确认用户仍存在,然后签发新访问令牌。刷新令牌本身有效七天;示例没有令牌版本、服务器端会话记录、撤销列表、一次性轮换或重用检测。

为什么需要注意

攻击者一旦复制刷新令牌,即使合法用户退出或修改密码,仍可能在剩余有效期内不断获得新的访问令牌,除非密钥被整体更换或用户被删除。

示例的刷新流程验证签名并检查用户存在,随后直接签发新访问令牌;所示方法没有轮换、服务端会话状态或撤销检查。刷新令牌被签为七天有效,因此一旦被窃取,在过期前可能被重复使用(除非应用在示例之外另有控制)。用户可要求使用服务端会话/令牌族、每次刷新轮换、重用检测和即时撤销能力。

references/advanced-patterns.md:274来自说明文档打开原文件
  async refreshToken(refreshToken: string) {    try {      const payload = jwt.verify(        refreshToken,        process.env.REFRESH_TOKEN_SECRET!,      ) as { userId: string };      const user = await this.userRepository.findById(payload.userId);      if (!user) {        throw new UnauthorizedError("User not found");      }      const token = this.generateToken({        userId: user.id,        email: user.email,      });      return { token };    } catch (error) {      throw new UnauthorizedError("Invalid refresh token");    }  }
查看另外 1 个位置
references/advanced-patterns.md:304来自说明文档打开原文件
  private generateRefreshToken(payload: any): string {    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {      expiresIn: "7d",    });  }
低风险

Fastify 示例反射任意请求来源,未实现生产环境来源白名单

原文依据:3 处
发现了什么

示例使用 cors 的 origin: true,会接受并反射请求的 Origin;这与主文件“生产环境不要使用 *”的目标并不等同于严格白名单。后续若启用跨域凭据或依赖浏览器来源隔离,这个默认值会扩大可访问来源。

为什么需要注意

恶意网站可能从受害者浏览器向 API 发起跨域调用;若部署又允许 Cookie 等环境凭据,响应可能被恶意页面读取。即使没有凭据,也会取消对公开端点的浏览器来源限制。

这是 Fastify 设置示例,不会由 Skill 自动启动;但复制后,origin: true 会动态接受/反射请求 Origin,而不是生产白名单。当前片段没有展示凭据配置,因此不能断言已发生跨站账户访问;若以后允许凭据或把 Origin 当安全边界,任意站点来源会扩大浏览器访问面。用户可要求按环境配置明确的来源列表,并测试未列入来源会被拒绝。

references/details.md:58来自说明文档打开原文件
// Pluginsawait fastify.register(helmet);await fastify.register(cors, { origin: true });await fastify.register(compress);
查看另外 2 个位置
SKILL.md:32来自说明文档打开原文件
5. **Implement logging**: Use structured logging (Pino, Winston)6. **Add rate limiting**: Prevent abuse7. **Use HTTPS**: Always in production8. **Implement CORS properly**: Don't use `*` in production9. **Use dependency injection**: Easier testing and maintenance
SKILL.md:33来自说明文档打开原文件
6. **Add rate limiting**: Prevent abuse7. **Use HTTPS**: Always in production8. **Implement CORS properly**: Don't use `*` in production9. **Use dependency injection**: Easier testing and maintenance
会不会误导 AI 或隐藏内容?检查工作说明是否要求 AI 忽略你的指令、干扰检查结果,或夹带看不见的文字。未发现风险
会不会偷偷改推广链接或收款方?检查是否强制替换推广链接或收款对象,同时要求隐瞒更改。发现 1 项风险
高风险

订单示例信任传入价格和数量,可能造成少付货款或负库存

原文依据:3 处
发现了什么

createOrder 接收 items 后直接用其内容计算订单总额、写入 item.price,并无条件从库存减去 item.quantity。示例没有从受信商品表读取价格,也没有在更新语句中验证库存充足或数量为正。

为什么需要注意

若 items 来自客户请求,客户可能提交低价、零价、负数或超库存数量,破坏收入、订单金额和库存记录的一致性。事务只保证这些错误操作一起提交,并不会验证其商业正确性。

这是示例实现,只有在被采用并让客户端控制 items 时才产生风险。可见代码把 items 交给未展示的 calculateTotal,因此无法确认总价如何校验;但 item.price 和 item.quantity 确实直接写入 SQL 参数,库存扣减也没有正数或库存充足条件。攻击者可能提交负数、超库存数量或伪造明细价格。用户可要求服务端从商品库读取价格、校验正整数,并用带库存条件的原子更新。

references/advanced-patterns.md:185来自说明文档打开原文件
export class OrderService {  constructor(private db: Pool) {}  async createOrder(userId: string, items: any[]) {    const client = await this.db.connect();    try {      await client.query("BEGIN");      // Create order      const orderResult = await client.query(        "INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id",        [userId, calculateTotal(items)],      );      const orderId = orderResult.rows[0].id;
查看另外 2 个位置
references/advanced-patterns.md:201来自说明文档打开原文件
      // Create order items      for (const item of items) {        await client.query(          "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)",          [orderId, item.productId, item.quantity, item.price],        );        // Update inventory        await client.query(          "UPDATE products SET stock = stock - $1 WHERE id = $2",          [item.quantity, item.productId],        );      }
references/advanced-patterns.md:208来自说明文档打开原文件
        // Update inventory        await client.query(          "UPDATE products SET stock = stock - $1 WHERE id = $2",          [item.quantity, item.productId],        );      }

Skill 逻辑拆解

4 个说明模块

该 Skill 是一组 Node.js 后端设计说明和可复制的 TypeScript 示例;主文件明确引导代理继续读取两个参考文档。提供的来源中没有安装命令或自动执行脚本,因此风险主要在用户采用这些示例后出现。

查看原文
SKILL.md:21来自说明文档打开原文件
## Detailed patterns and worked examplesDetailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
references/details.md:584来自说明文档打开原文件
Key patterns covered in [references/advanced-patterns.md](references/advanced-patterns.md):- **PostgreSQL with connection pool** — `pg` Pool configuration and graceful shutdown- **MongoDB with Mongoose** — connection management and schema definition- **Transaction pattern** — `BEGIN`/`COMMIT`/`ROLLBACK` with `pg` client## Authentication & AuthorizationJWT-based auth with access tokens (short-lived, 15m) and refresh tokens (7d). Full `AuthService` implementation with `bcrypt` password comparison in [references/advanced-patterns.md](references/advanced-patterns.md).

示例服务会连接 PostgreSQL、MongoDB 和 Redis,并从环境变量读取连接信息及 JWT 密钥。代码没有把这些值上传到固定第三方地址,但采用后会使用应用进程现有的数据库和缓存权限。

查看原文
references/advanced-patterns.md:49来自说明文档打开原文件
  () =>    new Pool({      host: process.env.DB_HOST,      port: parseInt(process.env.DB_PORT || "5432"),      database: process.env.DB_NAME,      user: process.env.DB_USER,      password: process.env.DB_PASSWORD,      max: 20,      idleTimeoutMillis: 30000,      connectionTimeoutMillis: 2000,    }),);
references/advanced-patterns.md:126来自说明文档打开原文件
const connectDB = async () => {  try {    await mongoose.connect(process.env.MONGODB_URI!, {      maxPoolSize: 10,      serverSelectionTimeoutMS: 5000,      socketTimeoutMS: 45000,    });
references/advanced-patterns.md:318来自说明文档打开原文件
const redis = new Redis({  host: process.env.REDIS_HOST,  port: parseInt(process.env.REDIS_PORT || "6379"),  retryStrategy: (times) => {    const delay = Math.min(times * 50, 2000);    return delay;  },});

示例涵盖实际改变业务数据的创建、更新、删除和订单事务操作。这些是文档中的示例而非已经执行的操作;一旦复制到路由中,其安全性取决于调用方认证、字段验证和数据库权限。

查看原文
references/details.md:140来自说明文档打开原文件
  async updateUser(req: Request, res: Response, next: NextFunction) {    try {      const { id } = req.params;      const updates: UpdateUserDTO = req.body;      const user = await this.userService.updateUser(id, updates);      res.json(user);    } catch (error) {      next(error);    }  }  async deleteUser(req: Request, res: Response, next: NextFunction) {    try {      const { id } = req.params;      await this.userService.deleteUser(id);      res.status(204).send();    } catch (error) {
references/advanced-patterns.md:191来自说明文档打开原文件
    try {      await client.query("BEGIN");      // Create order      const orderResult = await client.query(        "INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id",        [userId, calculateTotal(items)],      );      const orderId = orderResult.rows[0].id;      // Create order items      for (const item of items) {        await client.query(          "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)",          [orderId, item.productId, item.quantity, item.price],        );        // Update inventory        await client.query(          "UPDATE products SET stock = stock - $1 WHERE id = $2",          [item.quantity, item.productId],        );      }      await client.query("COMMIT");      return orderId;    } catch (error) {      await client.query("ROLLBACK");      throw error;
从这里开始 · 工作说明SKILL.md
nodejs-backend-patterns
连线表示工作说明包含的模块,不是实际运行顺序。点击模块可查看原文。

文件引用关系图

2 处引用
哪些文件发起引用引用了什么
连线表示真实的文件引用,不是运行顺序。点击节点可高亮相关连线,并查看具体文件和原文位置。虚线表示还有文件需要定位。
文件与检查记录3 个文件

检查范围与遗漏

逐文件查看涉及的内容

下方列出本次涉及的原文范围;纳入检查不代表已查清所有问题。

  • SKILL.md已纳入全文
  • references/details.md已纳入全文
  • references/advanced-patterns.md已纳入全文

这份报告只针对上方版本。我们看了拿到的代码和说明文件,没有实际运行 Skill,也没有检查它另外安装的软件包。因此,这不是“保证安全”的承诺;换了版本或使用环境,结果也可能不同。

  • SKILL.md工作说明
  • references/advanced-patterns.md配套文件
  • references/details.md配套文件

代码和说明中提到的操作

读取文件
SKILL.md:23来自说明文档打开原文件
Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
读取密钥或账号配置
references/advanced-patterns.md:50来自说明文档打开原文件
    new Pool({      host: process.env.DB_HOST,      port: parseInt(process.env.DB_PORT || "5432"),
references/advanced-patterns.md:51来自说明文档打开原文件
      host: process.env.DB_HOST,      port: parseInt(process.env.DB_PORT || "5432"),      database: process.env.DB_NAME,
references/advanced-patterns.md:52来自说明文档打开原文件
      port: parseInt(process.env.DB_PORT || "5432"),      database: process.env.DB_NAME,      user: process.env.DB_USER,
读取了多少行
1,077
文件校验值(用于核对版本)
51c768fb943a22ce66759f2d14757f4894dd13df14b5b45504db3e22eae17973