Skip to content

JavaScript and TypeScript Security Cheat Sheet / JavaScript和TypeScript安全速查表

Introduction / 介绍

This cheat sheet lists secure development practices for JavaScript and TypeScript that apply everywhere the language runs: browsers, hybrid apps, server-side Node.js, and any other runtime that implements the language (MDN JavaScript). It covers language-level pitfalls (dynamic code execution, prototype pollution, regular expressions), client-side sinks, and TypeScript-specific false confidence. Rules that are equally valid on the client and the server are stated once here; backend and runtime hardening stays in the Node.js Security Cheat Sheet, which this sheet links to instead of duplicating.

此速查表列出了适用于JavaScript和TypeScript的 secure development practices,这些实践适用于语言运行的所有环境:浏览器、混合应用、服务器端Node.js以及任何实现该语言的运行时(MDN JavaScript)。它涵盖了语言级别的陷阱(动态代码执行、原型污染、正则表达式)、客户端接收点和TypeScript特定的错误信心。在此处一次声明在客户端和服务器上同样有效的规则;后端和运行时加固保留在Node.js安全速查表,此速查表通过链接而不是重复来引用它。

Dangerous Language Surface / 危险语言表面

eval, Function, and with / eval, Function, 和 with

Never build code from strings. See MDN's eval guidance. eval and new Function execute arbitrary code with the current runtime's privileges, so any attacker-influenced input reaching them is code injection (CWE-94). In browsers, setTimeout and setInterval with a string argument compile it the same way through implied eval.

切勿从字符串构建代码。请参阅MDN的eval指南。eval和new Function会以当前运行时的权限执行任意代码,因此任何攻击者影响的输入到达它们时都是代码注入(CWE-94)。在浏览器中,带有字符串参数的setTimeout和setInterval会通过隐含的eval以相同的方式编译。

  • Parse data with JSON.parse, never eval.
    中文:使用JSON.parse解析数据,切勿使用eval。
  • Replace dynamic dispatch with static maps of functions instead of constructing calls from names.
    中文:用函数的静态映射代替动态分发,而不是从名称构造调用。
  • Do not use the with statement. It makes scope unpredictable, is forbidden in strict mode, and blocks engine optimizations.
    中文:不要使用with语句。它使作用域不可预测,在严格模式下被禁止,并阻止引擎优化。
  • Enforce this statically with the no-eval, no-implied-eval, and no-new-func lint rules.
    中文:通过no-eval、no-implied-eval和no-new-func代码检查规则静态地强制执行此规则。
  • In browsers, block string compilation with an enforced Content Security Policy whose script-src directive, or default-src fallback, omits both 'unsafe-eval' and 'trusted-types-eval'. A report-only policy does not block execution (CSP EnsureCSPDoesNotBlockStringCompilation).
    中文:在浏览器中,使用强制性的内容安全策略(Content Security Policy)阻止字符串编译,其中script-src指令或default-src回退省略了'unsafe-eval'和'trusted-types-eval'。仅报告策略不会阻止执行(CSP EnsureCSPDoesNotBlockStringCompilation)。
  • CSP is a browser-side control and does not apply to server-side runtimes, so the lint rules remain the primary enforcement everywhere.
    中文:CSP是浏览器端控制,不适用于服务器端运行时,因此代码检查规则仍然是所有地方的强制执行主要手段。

Evil Regex (ReDoS) / 恶意正则表达式(ReDoS)

Nested quantifiers such as (a+)+, and repeated ambiguous alternatives such as ^(a|aa)*$, can make matching take exponential time on crafted input, consuming CPU and potentially blocking the executing thread or event loop (CWE-1333). Whether a pattern is actually exploitable depends on the surrounding expression and the failing input, not just the quantified group.

嵌套量词,如 (a+)+,以及重复的歧义性选择,如 ^(a|aa)*$,可能导致在精心设计的输入上匹配时间呈指数级增长,消耗 CPU 资源,并可能阻塞执行线程或事件循环(CWE-1333)。一个模式是否真正可利用取决于周围的表达式和失败的输入,而不仅仅是量词组本身。

  • Prefer well-tested validators over hand-written patterns for emails, URLs, and similar inputs.
    中文:对于电子邮件、URL 和类似输入,优先使用经过良好测试的验证器而不是手写的模式。
  • Keep patterns linear: avoid nested quantifiers and ambiguous alternation over the same characters.
    中文:保持模式线性:避免嵌套量词和相同字符上的歧义性选择。
  • Cap untrusted input length before matching, and reject rather than sanitize when input does not match.
    中文:在匹配之前限制不受信任的输入长度,当输入不匹配时拒绝而不是净化。
  • For the attack mechanics and detection tooling, see the OWASP ReDoS guidance; use linear-time patterns as the primary control and input-length caps as defense in depth.
    中文:关于攻击机制和检测工具,请参阅OWASP ReDoS指南;使用线性时间模式作为主要控制,并将输入长度上限作为深度防御。

Strict Mode / 严格模式

Ship ES modules (strict by default) or declare 'use strict', as described in MDN's strict mode documentation. Strict mode turns silent mistakes into errors: assignment to undeclared variables throws, this stays undefined in plain functions instead of becoming the global object, and with is rejected. It does not fix injection, XSS, or prototype pollution; treat it as hygiene, not a security boundary.

分发 ES 模块(默认为严格模式)或声明 'use strict',如 MDN 严格模式文档 中所述。严格模式将静默错误转换为错误:未声明的变量赋值会抛出异常,this 在普通函数中保持 undefined 而不是成为全局对象,并且 with 被拒绝。它不会修复注入、XSS 或原型污染;将其视为卫生习惯,而不是安全边界。

Object and Property Safety (Including Prototype Pollution) / 对象和属性安全性(包括原型污染)

Prototype pollution (CWE-1321) occurs when attacker-controlled keys such as __proto__, constructor, or prototype reach a recursive merge or path setter and modify an object's prototype. This can alter the behavior of objects that inherit from that prototype. These are the JavaScript-specific essentials only; the full protection guidance lives in the dedicated Prototype Pollution Prevention Cheat Sheet.

原型污染(CWE-1321)发生在攻击者控制的键,如 __proto__、constructor 或 prototype 达到递归合并或路径设置器并修改对象的原型时。这可能会改变继承自该原型的对象的行为。这些只是 JavaScript 特定的基本要点;完整的保护指南可以在专门的 原型污染预防备忘单 中找到。

  • Never pass untrusted input to a recursive merge or set-by-path helper, and reject the key segments __proto__, constructor, and prototype before writing.
    中文:切勿将不受信任的输入传递给递归合并或 set-by-path 辅助函数,并在写入之前拒绝 __proto__、constructor 和 prototype 等键段。
  • Use a Map with set, get, and has for untrusted dictionary keys, keeping entries separate from object properties (MDN prototype pollution defenses). If an object is required, create it with Object.create(null) so it has no prototype.
    中文:对于不受信任的字典键,使用带有set、get和has的Map,将条目与对象属性分开(MDN原型污染防御)。如果需要对象,使用Object.create(null)创建它,使其没有原型。
  • Validate parsed or copied untrusted data against a schema before use, and drop __proto__ keys first: Object.assign applies them through the prototype setter and mutates the target's prototype, while spread creates a silent own property.
    中文:在使用之前,请根据模式验证解析或复制的不可信数据,并首先删除 __proto__ 键:Object.assign 通过原型设置器应用它们并修改目标的原型,而展开则创建静默的自身属性。

DOM Sinks and Output Context / DOM 溢出点和输出上下文

Injecting attacker-controlled strings into HTML, script, or URL contexts is cross-site scripting (CWE-79). Follow the Cross Site Scripting Prevention Cheat Sheet for per-context encoding and the DOM based XSS Prevention Cheat Sheet for client-side sources and sinks.

将攻击者控制的字符串注入到 HTML、脚本或 URL 上下文中是跨站脚本 (CWE-79)。遵循 跨站脚本预防备忘单 进行上下文编码,以及 基于 DOM 的 XSS 预防备忘单 进行客户端源和汇。

  • Build DOM with textContent and createElement instead of innerHTML or insertAdjacentHTML.
    中文:使用 textContent 和 createElement 而不是 innerHTML 或 insertAdjacentHTML 构建 DOM。
  • When HTML must be rendered, sanitize with a maintained allowlist sanitizer and enforce Trusted Types with no permissive default policy.
    中文:当必须渲染HTML时,使用维护的允许列表清理器进行清理,并使用没有宽容默认策略的Trusted Types强制执行。
  • Framework auto-escaping has explicit bypasses that must never receive untrusted input: React dangerouslySetInnerHTML, Vue v-html, and Angular bypassSecurityTrustHtml and its siblings.
    中文:框架自动转义具有显式绕过机制,绝不能接收不受信任的输入:React dangerouslySetInnerHTML、Vue v-html 和 Angular bypassSecurityTrustHtml 及其兄弟。

Async Error Handling, Messaging, and Origin Checks / 异步错误处理、消息传递和来源检查

Not every promise failure is a defect. An expected failure is part of a function's contract, such as a lookup that returns null for a missing key or a parse that throws on malformed input; handle it where the caller decides what to do, with a try/catch around await or a .catch on the terminal link of the chain, and return a typed result or a documented error. An unhandled rejection is a promise that rejects with no handler attached, so the failure propagates past the call site and is usually a bug: browsers report it in the console and fire an unhandledrejection event, while Node.js behavior depends on its unhandled-rejection mode and may terminate the process (MDN promise rejection events). Keep promise chains flat with a terminal error handler so genuinely unexpected rejections stay visible, and do not silence them with an empty catch. Server-side specifics live in the Node.js Security Cheat Sheet and are not repeated here.

并非每个承诺失败都是缺陷。一个 预期的 失败是函数合约的一部分,例如返回 null 的查找或对格式错误的输入抛出的解析;在调用者决定如何处理的地方处理它,使用 await 或链末端的 .catch 附近的 try/catch,并返回一个类型化的结果或文档化的错误。一个 未处理的 拒绝是一个没有附加处理器的承诺,因此失败会传播到调用点,通常是一个错误:浏览器在控制台中报告它并触发一个 unhandledrejection 事件,而 Node.js 的行为取决于其未处理的拒绝模式,并且可能会终止进程 (MDN 承诺拒绝事件)。通过终端错误处理器保持承诺链平坦,以便真正意外的拒绝仍然可见,并且不要通过空的 catch 来静默它们。服务器端的具体内容位于 Node.js 安全备忘单 中,此处不再重复。

For postMessage, the receiver must verify event.origin against an explicit allowlist, check event.source when a conversation partner is expected, and validate event.data against the expected schema before acting on it. The sender must pass an exact targetOrigin, never "*" for sensitive data (MDN postMessage). Prefer narrow MessageChannel ports over broadcast messaging where the design allows it.

对于 postMessage,接收方必须将 event.origin 与显式的允许列表进行验证,当预期有会话伙伴时检查 event.source,并在采取行动之前对 event.data 进行与预期模式的有效性验证。发送方必须传递确切的 targetOrigin,对于敏感数据绝不能使用 "*"(MDN postMessage) 。当设计允许时,优先使用窄 MessageChannel 端口而不是广播消息。

TypeScript-Specific Caveats / TypeScript 特殊注意事项

Types are erased at runtime, so TypeScript alone enforces nothing against a malicious or malformed caller.

类型在运行时被擦除,因此 TypeScript 本身无法防止恶意或格式错误的调用者。

  • Treat any as a hole in every check. unknown is the safe default for values of unknown type, because it forces you to narrow before use, whereas any disables type checking for that value (TypeScript Handbook: unknown).
    中文:将any视为每个检查中的漏洞。unknown是未知类型值的默认安全选择,因为它强制你在使用之前进行缩小,而any则禁用了该值的类型检查(TypeScript手册:unknown)。
  • Type assertions (as) and non-null assertions (!) silence the compiler without changing runtime values; do not use them on untrusted data.
    中文:类型断言(as)和非空断言(!)会关闭编译器而不改变运行时值;不要在不受信任的数据上使用它们。
  • Enable strict in tsconfig.json (plus noUncheckedIndexedAccess where affordable) to catch accidental unsafety in your own code. It is a code-quality control, not a trust boundary.
    中文:在 tsconfig.json 中启用 strict(以及当可行时加上 noUncheckedIndexedAccess)以捕获自己代码中的意外不安全性。这是一个代码质量控制,而不是信任边界。
  • Validate at every trust boundary (network responses, postMessage payloads, storage reads) with a runtime schema validator. The validated type should flow from the schema, not from a parallel hand-written interface.
    中文:在每一个信任边界(网络响应、postMessage 负载、存储读取)处使用运行时模式验证器进行验证。验证过的类型应从模式中流动,而不是从并行手写的接口中流动。

Linting and Dependency Tooling / 代码审查和依赖工具

  • Lint with typescript-eslint recommended sets plus no-eval, no-implied-eval, and no-new-func. Keep dependencies updated so new rules apply.
    中文:使用typescript-eslint推荐的集合进行代码检查,包括no-eval、no-implied-eval和no-new-func。保持依赖项更新,以便应用新规则。
  • Pin versions with a lockfile, review dependency changes, and monitor advisories. Frontend dependency-review specifics overlap with the Node.js Security Cheat Sheet.
    中文:使用锁文件固定版本,审查依赖变更,并监控安全通告。前端依赖审查的具体内容与 Node.js 安全备忘录 相重叠。
  • For third-party scripts loaded from a CDN, pin a Subresource Integrity hash with crossorigin handling per the Third Party JavaScript Management Cheat Sheet.
    中文:对于从CDN加载的第三方脚本,根据第三方JavaScript管理作弊表中的说明,使用crossorigin处理来固定子资源完整性哈希。

References / 参考