Suggest an editImprove this articleRefine the answer for “JSON.stringify() and its danger”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`JSON.stringify()`** is not designed for safely inserting data into HTML, because it is correct as JavaScript syntax but does not account for the HTML context, where the sequence `</script>` can prematurely close the tag and allow a malicious script to run. **Key point:** before inserting into `<script>` you need to escape `<`, `>`, `&`, ` `, ` `, or use ready-made serializers or `<script type="application/json">`.Shown above the full answer for quick recall.Answer (EN)Image## The problem: injecting unescaped JSON into HTML During SSR you often need to **pass data from the server to the client**, so React (or another framework) can "hydrate" the page without loading it again. A typical example ```javascript // On the server: const data = { user: { name: 'Tim' } }; const html = ` <html> <body> <div id="root">${reactHtml}</div> <script> window.__INITIAL_DATA__ = ${JSON.stringify(data)}; </script> </body> </html> `; res.send(html); ``` At first glance, this looks fine. But if `data` contains **user-supplied data**, the problem will not keep you waiting --- ## What can go wrong If `data` contains **special HTML or JS characters**, `JSON.stringify()` inserts them *as is* into the HTML, without escaping. An example of user input: ```javascript const data = { user: { name: '</script><script>alert("XSS!")</script>' } }; ``` After `JSON.stringify(data)` we get: ```javascript <script> window.__INITIAL_DATA__ = {"user":{"name":"</script><script>alert(\"XSS!\")</script>"}}; </script> ``` What happens: - the browser **closes the first** `<script>` **tag** (`</script>`) - and **runs the malicious script** `alert("XSS!")`. Congratulations, a classic **XSS through SSR**. --- ## Why this happens `JSON.stringify()` is **not designed** for safe insertion into HTML. It is correct as JavaScript syntax, but it **does not account for the HTML context**, where `<script>` can be closed prematurely. --- ## What exactly is dangerous | Dangerous sequence | Why | Example | |---|---|---| | `</script>` | Closes the `<script>` tag | `"</script><script>alert(1)</script>"` | | `<!--` or `-->` | Can start or close an HTML comment | `"<!--evil-->"` | | `<script`, `<img`, `onerror=` | Can initiate a new tag | `"<img src=x onerror=alert(1)>"` | | The characters `</`, `<`, `>` | Break the HTML structure | `"Hello </div>"` | --- ## How to do it correctly ### 1. Safely escape the JSON before inserting it An example (Node.js / Express / Next.js): ```javascript const safeJson = JSON.stringify(data) .replace(/</g, '\\u003C') .replace(/>/g, '\\u003E') .replace(/&/g, '\\u0026') .replace(/ /g, '\\u2028') .replace(/ /g, '\\u2029'); const html = ` <script> window.__INITIAL_DATA__ = ${safeJson}; </script> `; ``` Now even if a user enters `</script>`, the browser gets `</script>`, which **does not close the tag** and **does not run the script**. --- ### 2. Use ready-made utilities Frameworks like **Next.js** or **Nuxt** do this automatically: ```javascript // Next.js example <NextScript /> // generates a safe insertion of initialProps, with special characters escaped ``` If you write SSR by hand, use ready-made libraries: - `serialize-javascript` - `safe-json-stringify` ```javascript import serialize from 'serialize-javascript'; const html = `<script>window.__DATA__ = ${serialize(data, { isJSON: true })}</script>`; ``` --- ### 3. Isolate data from code It is better **not to insert JSON directly** into `<script>`, but to hand it over, for example, through `<script type="application/json">`: ```javascript <script id="initial-data" type="application/json"> {"user": {"name": "Tim"}} </script> ``` And on the client: ```javascript const data = JSON.parse(document.getElementById('initial-data').textContent); ``` This way the browser **never executes** the content of this script as JS code. --- ## Short summary | What | Description | |---|---| | Problem | `JSON.stringify()` inserts "raw" JSON into HTML, enabling XSS | | Threat | An attacker can inject `<script>` or other dangerous tags | | Solution | Escape `<`, `>`, `&`, ` `, ` ` | | Alternative | Use `serialize-javascript` or `<script type="application/json">` | --- ## Conclusion During SSR: > Never insert the result of `JSON.stringify()` directly into HTML > Always escape special characters or use safe serializers This is **one of the most common XSS vulnerabilities in SSR applications**, and it can be prevented with a single line: `.replace(/</g, '\\u003C')`.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.