[{"data":1,"prerenderedAt":173},["ShallowReactive",2],{"article-url-encode-xss-html-entity-defense":3},{"article":4,"relatedArticles":129},{"slug":5,"category":6,"date":7,"readingMinutes":8,"tags":9,"relatedTool":14,"cn":15,"en":64,"author":113,"reviewedBy":116,"reviewedDate":118,"updatedDate":118,"sources":119,"noIndex":128},"url-encode-xss-html-entity-defense","最佳实践","2026-07-28",6,[10,11,12,13],"XSS Defense","URL Encode","HTML Entity","Security","/tools/dev/url-encode",{"title":16,"description":17,"intro":18,"sections":19},"前端 XSS 防御与上下文编码：URL Percent Encoding 与 HTML Entity 实体转义边界","分析 HTML 文本、属性、JS 上下文与 URL Sink (如 `\u003Ca href=\"...\">`) 的差异，澄清 URL 编码无法替代 XSS 防御的误区并给出多层防御规范。","将未经过滤的 URL 参数渲染到 DOM 节点中是引发反射型 XSS 攻击的常见风险。许多开发者误以为只要对 URL 做了 `encodeURI` 就能彻底免疫 XSS。实际上，不同 DOM 输出上下文（HTML 文本、属性、JS 变量、href 链接）对应的安全转义机制截然不同。本文阐明上下文编码与 XSS 防御规范。",[20,24,29,33,37,42,46,50,54,60],{"heading":21,"paragraphs":22},"一、问题概述：URL 编码无法替代 XSS 防御的常见误区",[23],"一种危险的误区是认为“全量 URL 编码能代替 XSS 转义”。例如，当恶意攻击者传入 `javascript:alert(1)` 作为链接时，即便对其进行全量 `encodeURI` 编码，当代码被插入到 `\u003Ca href=\"...\">` 并被用户点击时，浏览器渲染引擎依然会解包并执行 `javascript:` 伪协议中的恶意脚本。URL 编码的目的是遵守 URI 语法，而不是净化 XSS 攻击载荷。",{"heading":25,"paragraphs":26,"code":28},"二、最小复现：伪协议注入与属性边界打破",[27],"下面的 HTML 示例展示了单纯依赖 URL 编码在不同的 DOM 输出 Sink 下失效的情况：","\u003C!-- 漏洞 1：伪协议注入 (URL 编码合法，但在 href Sink 中被执行) -->\n\u003Ca href=\"javascript:alert(document.cookie)\">点击领取礼品\u003C/a>\n\n\u003C!-- 漏洞 2：HTML 属性上下文打破 (缺少 HTML 实体转义) -->\n\u003Cinput type=\"text\" value=\"https://example.com?a=1\" onfocus=\"alert(1)\">",{"heading":30,"paragraphs":31},"三、根因分析：URL 解析、协议策略与输出上下文是三件事",[32],"HTML 文本和属性需要由模板系统做上下文转义；URL Sink 还必须校验解析后的协议。对完整 URL 再调用 `encodeURI` 不能移除 `javascript:`，手工把 `&` 变成 `&amp;` 后再赋给 DOM 属性反而会改变真实查询字符串。若使用 Vue 的 `:href` 属性绑定，应传入经过协议校验的普通 URL 字符串，让框架负责属性层转义。",{"heading":34,"paragraphs":35},"四、推荐方案：用 URL 解析器做协议白名单，再交给框架绑定",[36],"1. 用 `new URL(raw, trustedBase)` 解析后检查 `url.protocol`，通用链接只允许 `http:` 与 `https:`。2. 内部链接只接受单斜杠开头，并确认解析后仍与可信 Base 同源，避免 `//evil.example` 和反斜杠变体。3. 在 Vue/Nuxt 中使用 `:href`，不要拼接 HTML 或使用 `v-html`。DOMPurify 适用于确实要渲染的富文本 HTML，不是 URL 协议校验器。",{"heading":38,"paragraphs":39,"code":41},"五、完整代码：适用于 Vue `:href` 的协议安全 URL 函数",[40],"函数返回普通 URL 字符串或 `null`，不做 HTML 实体替换。组件应仅在结果非空时渲染链接，并通过属性绑定交给 Vue。","function safeHref(raw: string, trustedBase: string): string | null {\n  const value = raw.trim();\n  const isRootRelative = /^\\/(?![\\\\/])/.test(value);\n  const isAbsolute = /^[a-z][a-z\\d+.-]*:/i.test(value);\n  if (!isRootRelative && !isAbsolute) return null;\n\n  try {\n    const base = new URL(trustedBase);\n    const url = new URL(value, base);\n    if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n\n    if (isRootRelative) {\n      if (url.origin !== base.origin) return null;\n      return url.pathname + url.search + url.hash;\n    }\n    return url.href;\n  } catch {\n    return null;\n  }\n}\n\nconst base = \"https://jsonutils.com\";\nconsole.log(safeHref(\"javascript:alert(1)\", base)); // null\nconsole.log(safeHref(\"//evil.example/path\", base)); // null\nconsole.log(safeHref(\"/articles/example?a=1&b=2\", base)); // 保留真实 & 字符\nconsole.log(safeHref(\"https://example.com/search?q=hello\", base));",{"heading":43,"paragraphs":44},"六、常见错误方案",[45],"误以为 `encodeURI` 能自动防范所有 XSS 攻击；直接使用 `v-html` 渲染未经协议校验的动态 URL 字符串；在 JS 上下文中直接拼接未经 Unicode 编码的 URL 参数。",{"heading":47,"paragraphs":48},"七、边界条件：`data:text/html` 协议与 SVG 内部的 Script 标签",[49],"`data:image/svg+xml` 和 `data:text/html` 格式的 URL 均能包含可执行的 XSS 脚本；在协议白名单中必须严格禁止 `data:` 协议用于通用链接。",{"heading":51,"paragraphs":52},"八、如何验证 XSS 防御安全性",[53],"单元测试覆盖大小写和空白变体的 `javascript:`、`data:`、`vbscript:`、协议相对 URL、反斜杠、控制字符、正常 HTTPS 与内部相对路径；组件测试应确认最终 DOM 属性值正确且没有使用 `v-html`，再配合 CSP 与浏览器 XSS 测试。",{"heading":55,"paragraphs":56},"九、FAQ",[57,58,59],"问：为什么 `encodeURI('javascript:alert(1)')` 仍不安全？答：因为 encodeURI 保留了字母与括号，生成的字符串依然是合法的 javascript: 伪协议，在 href 中会被浏览器执行。","问：Vue / React 模板会自动防止 `href` XSS 吗？答：不会！框架自带的转义只防范 HTML 标签/属性打破，无法感知 `href='javascript:'` 协议安全，必须手动校验协议。","问：HTML 实体转义和 URL 编码可以互相替代吗？答：不能！HTML 实体转义用于解析 HTML 文本/属性，URL 编码用于解析 URI 组件，必须按上下文使用。",{"heading":61,"paragraphs":62},"十、总结",[63],"URL 编码用于维持 URI 语法完整性，不能替代 XSS 安全防御。将动态 URL 渲染至 DOM 时，必须实施“协议白名单校验 + 上下文相关实体编码”的双层防护。",{"title":65,"description":66,"intro":67,"sections":68},"Frontend XSS Defenses and Contextual Output Encoding: Percent Encoding vs HTML Entity Escape Boundaries","Analyze HTML text, attribute, JS, and URL sink differences (\u003Ca href>), clarifying why URL encoding cannot replace XSS defense with multi-layered rules.","Rendering raw URL parameters into DOM nodes introduces reflected XSS vulnerabilities. A common misconception is that applying `encodeURI` makes a URL immune to XSS. In reality, different DOM output contexts (HTML text, attributes, JS variables, href sinks) require distinct encoding rules.",[69,73,78,82,86,91,95,99,103,109],{"heading":70,"paragraphs":71},"1. Problem: the misconception that URL encoding replaces XSS defense",[72],"Thinking 'full URL encoding replaces XSS escaping' is dangerous. For instance, if an attacker supplies `javascript:alert(1)` as a link, even after applying `encodeURI`, when inserted into an `\u003Ca href=\"...\">` tag, clicking it triggers the `javascript:` pseudo-protocol. URL encoding preserves URI syntax; it does not sanitize XSS payloads.",{"heading":74,"paragraphs":75,"code":77},"2. Minimal reproduction: pseudo-protocol injection and attribute boundary breakouts",[76],"The HTML snippet below shows how relying solely on URL encoding fails across different DOM output sinks:","\u003C!-- Defect 1: Pseudo-protocol injection (valid URL encoding, executed in href Sink) -->\n\u003Ca href=\"javascript:alert(document.cookie)\">Claim Reward\u003C/a>\n\n\u003C!-- Defect 2: HTML attribute context breakout (missing HTML entity escaping) -->\n\u003Cinput type=\"text\" value=\"https://example.com?a=1\" onfocus=\"alert(1)\">",{"heading":79,"paragraphs":80},"3. Root cause: URL parsing, protocol policy, and output encoding are separate",[81],"Template systems handle contextual escaping for HTML text and attributes, while a URL sink also requires validation of the parsed protocol. Calling `encodeURI` on a complete URL does not remove `javascript:`. Conversely, replacing `&` with `&amp;` before assigning a DOM property changes the actual query string. With Vue `:href`, pass a protocol-validated ordinary URL string and let Vue handle attribute escaping.",{"heading":83,"paragraphs":84},"4. Recommendation: parse and whitelist protocols, then bind through the framework",[85],"1. Parse with `new URL(raw, trustedBase)` and allow only `http:` and `https:` for general links. 2. Accept internal paths only when they begin with one slash and remain same-origin after parsing, rejecting protocol-relative and backslash variants. 3. Bind with Vue/Nuxt `:href`; do not construct HTML or use `v-html`. DOMPurify is for rich HTML that must be rendered, not for URL protocol validation.",{"heading":87,"paragraphs":88,"code":90},"5. Complete code: protocol-safe URL values for Vue `:href`",[89],"The function returns an ordinary URL string or `null`; it deliberately performs no HTML entity replacement. Render the link only when the result is non-null and pass it through property binding.","function safeHref(raw: string, trustedBase: string): string | null {\n  const value = raw.trim();\n  const isRootRelative = /^\\/(?![\\\\/])/.test(value);\n  const isAbsolute = /^[a-z][a-z\\d+.-]*:/i.test(value);\n  if (!isRootRelative && !isAbsolute) return null;\n\n  try {\n    const base = new URL(trustedBase);\n    const url = new URL(value, base);\n    if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n\n    if (isRootRelative) {\n      if (url.origin !== base.origin) return null;\n      return url.pathname + url.search + url.hash;\n    }\n    return url.href;\n  } catch {\n    return null;\n  }\n}\n\nconst base = \"https://jsonutils.com\";\nconsole.log(safeHref(\"javascript:alert(1)\", base)); // null\nconsole.log(safeHref(\"//evil.example/path\", base)); // null\nconsole.log(safeHref(\"/articles/example?a=1&b=2\", base)); // preserves the real ampersand\nconsole.log(safeHref(\"https://example.com/search?q=hello\", base));",{"heading":92,"paragraphs":93},"6. Incorrect approaches",[94],"Assuming `encodeURI` automatically protects against all XSS attacks. Using `v-html` to render unvalidated URL strings. Concatenating raw URL parameters directly inside JavaScript blocks without Unicode escaping.",{"heading":96,"paragraphs":97},"7. Boundaries: data:text/html protocols and inline SVG script tags",[98],"Both `data:image/svg+xml` and `data:text/html` URLs can execute XSS scripts. Protocol whitelists must strictly disallow `data:` URLs for general links.",{"heading":100,"paragraphs":101},"8. Verification",[102],"Test case and whitespace variants of `javascript:`, `data:`, and `vbscript:`, plus protocol-relative URLs, backslashes, control characters, valid HTTPS, and internal paths. Component tests should inspect the final DOM property and verify no `v-html` path exists; complement them with CSP and browser XSS tests.",{"heading":104,"paragraphs":105},"9. FAQ",[106,107,108],"Why is `encodeURI('javascript:alert(1)')` still unsafe? encodeURI preserves letters and parentheses, leaving a valid javascript: pseudo-protocol that browsers execute in hrefs.","Do Vue/React templates protect href XSS automatically? No! Framework escaping prevents HTML attribute breakouts but cannot validate href protocol safety.","Can HTML entity escaping and URL encoding replace each other? No! HTML entity escaping processes HTML text/attributes, while URL encoding processes URI components.",{"heading":110,"paragraphs":111},"10. Summary",[112],"URL encoding maintains URI syntax integrity but cannot replace XSS security defenses. When rendering dynamic URLs into the DOM, enforce protocol whitelist checks paired with context-aware entity encoding.",{"name":114,"url":115},"Tiny's Tool Editorial Team","https://tinystool.org/about",{"name":117,"url":115},"Tiny's Tool Technical Review","2026-08-08",[120,124],{"title":121,"url":122,"publisher":123},"Cross Site Scripting Prevention Cheat Sheet","https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html","OWASP",{"title":125,"url":126,"publisher":127},"URL Living Standard","https://url.spec.whatwg.org/","WHATWG",false,[130,145,158],{"slug":131,"noIndex":132,"category":133,"date":7,"updatedDate":118,"reviewedDate":118,"readingMinutes":134,"tags":135,"relatedTool":14,"cn":139,"en":142},"url-encode-encodeuri-vs-encodeuricomponent",true,"踩坑避坑",5,[11,136,137,138],"encodeURI","encodeURIComponent","JavaScript",{"title":140,"description":141},"深入对比 encodeURI 与 encodeURIComponent：RFC 3986 保留字符与场景落地","对比 JavaScript 原生 `encodeURI` 与 `encodeURIComponent` 在 RFC 3986 保留字符（如 `?`, `=`, `/`, `&`）处理上的本质不同，列举完整 URL、路径段与查询参数的场景编码规范。",{"title":143,"description":144},"Deep Dive into encodeURI vs encodeURIComponent: RFC 3986 Reserved Characters and Real-World Scenarios","Compare JavaScript `encodeURI` vs `encodeURIComponent` for RFC 3986 reserved characters like `?`, `=`, `/`, `&`, detailing rules for full URLs, paths, and queries.",{"slug":146,"noIndex":132,"category":147,"date":7,"updatedDate":118,"reviewedDate":118,"readingMinutes":134,"tags":148,"relatedTool":14,"cn":152,"en":155},"url-encode-query-plus-sign-space-bug","错误排查",[11,149,150,151],"Plus Sign","Space","Query Parameter",{"title":153,"description":154},"URL Query 参数中加号 + 被后端误解析为空格的原理分析与 `%2B` 编码防御","分析传统 `application/x-www-form-urlencoded` 规范中将空格转为 `+` 导致的加号被误解析为空格漏洞，对比 `%20` 与 `%2B` 编码映射机制。",{"title":156,"description":157},"Analysis of Plus Sign + Becoming Space in URL Queries: Causes and %2B Percent-Encoding Defenses","Analyze how traditional application/x-www-form-urlencoded specs convert spaces to + causing plus signs to parse as spaces, detailing %20 vs %2B.",{"slug":159,"noIndex":128,"category":133,"date":7,"updatedDate":118,"reviewedDate":118,"readingMinutes":160,"tags":161,"relatedTool":166,"cn":167,"en":170},"regex-redos-cpu-100-percent-vulnerability",7,[162,163,164,165,13],"ReDoS","Regex","Performance","CPU 100%","/tools/dev/regex",{"title":168,"description":169},"小心 ReDoS 攻击！写错正则表达式导致 CPU 100% 爆表的原因与防范","剖析正则表达式拒绝服务攻击 (ReDoS) 底层机制，解释 NFA 引擎灾难性回溯 (Catastrophic Backtracking) 原理，提供安全正则改写与超时隔离防护手段。",{"title":171,"description":172},"Preventing ReDoS Attacks: How Catastrophic Backtracking Causes 100% CPU Spikes","Examine Regular Expression Denial of Service (ReDoS) mechanics, detailing NFA engine catastrophic backtracking and offering safe rewriting & timeout safeguards.",1786725508504]