TypeScript 应用安全:十大常见漏洞实战解析
TypeScript 是 JavaScript 的超集,增加了静态类型检查。许多开发者错误地认为强类型会自动使应用变得安全。事实并非如此。TypeScript 编译器会检查类型,但无法防御逻辑错误、注入攻击或认证问题。在本文中,我们将分析 TypeScript 应用中最常见的 10 个漏洞,展示代码示例,并提供具体的防护建议。无论您是初学者还是经验丰富的开发者,本文都将对您有所帮助。
1. 因输入处理不当导致的跨站脚本攻击 (XSS)
XSS 仍然是最危险的漏洞之一。TypeScript 不会自动转义输出。如果您将用户数据直接插入 HTML 模板而不进行清理,就等于为攻击敞开了大门。
存在漏洞的代码
function renderComment(comment: string): string { return `<div>${comment}</div>`;}
// 攻击者传入:<script>alert('XSS')</script>修复后的代码
import { escape } from 'lodash';
function renderComment(comment: string): string { const safeComment = escape(comment); return `<div>${safeComment}</div>`;}建议:始终使用转义库(如 DOMPurify、lodash.escape)或具有自动转义功能的模板引擎(如 React JSX、Handlebars)。
2. 通过字符串模板导致的 SQL 注入
TypeScript 允许使用模板字符串构建 SQL 查询。如果您直接拼接用户数据,这将是 SQL 注入的直接途径。
存在漏洞的代码
async function getUser(id: string): Promise<User | null> { const query = `SELECT * FROM users WHERE id = '${id}'`; const result = await db.query(query); return result.rows[0];}修复后的代码
async function getUser(id: string): Promise<User | null> { const query = 'SELECT * FROM users WHERE id = $1'; const result = await db.query(query, [id]); return result.rows[0];}建议:使用参数化查询或 ORM(如 TypeORM、Prisma、Sequelize)。切勿拼接 SQL 字符串。
3. 不安全的密码比较
密码比较实现中的错误可能导致时序攻击。TypeScript 开发者经常使用 === 运算符比较哈希值,这是不安全的。
存在漏洞的代码
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { const inputHash = await bcrypt.hash(inputPassword, 10); return inputHash === storedHash; // 漏洞:时序攻击}修复后的代码
import bcrypt from 'bcrypt';
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { return bcrypt.compare(inputPassword, storedHash);}建议:使用哈希库(如 bcrypt.compare、argon2.verify)内置的比较函数。它们能防御时序攻击。
4. 通过递归合并对象导致的原型污染
TypeScript 应用经常使用深度拷贝或对象合并函数。如果不控制键名,攻击者可能会污染 Object.prototype 原型。
存在漏洞的代码
function merge(target: Record<string, any>, source: Record<string, any>) { for (const key in source) { if (typeof source[key] === 'object') { target[key] = merge(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target;}
// 攻击者传