Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «JSON.stringify() і його небезпека». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`JSON.stringify()`** не призначений для безпечної вставки в HTML, тому що він коректний як JavaScript-синтаксис, але не враховує особливості HTML-контексту, де послідовність `</script>` може передчасно закрити тег і дозволити виконання зловмисного скрипту. **Ключове:** перед вставкою в `<script>` потрібно екранувати `<`, `>`, `&`, ` `, ` ` або використовувати готові серіалізатори чи `<script type="application/json">`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Проблема: впровадження неекранованого JSON в HTML Під час SSR часто потрібно **передати дані з сервера на клієнт**, щоб React (або інший фреймворк) міг "гідрувати" сторінку, не завантажуючи їх повторно. Типовий приклад ```javascript // На сервері: 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); ``` На перший погляд - все ок. Але якщо `data` містить **користувацькі дані**, проблема не змусить себе чекати --- ## Що може піти не так Якщо всередині `data` є **спеціальні символи HTML або JS**, `JSON.stringify()` вставить їх *як є* в HTML, без екранування. Приклад користувацького вводу: ```javascript const data = { user: { name: '</script><script>alert("XSS!")</script>' } }; ``` Після `JSON.stringify(data)` ми отримаємо: ```javascript <script> window.__INITIAL_DATA__ = {"user":{"name":"</script><script>alert(\"XSS!\")</script>"}}; </script> ``` Що відбудеться: - браузер **закриє перший** `<script>` **тег** (`</script>`) - і **виконає зловмисний скрипт** `alert("XSS!")`. Вітаємо - класичний **XSS через SSR**. --- ## Чому це відбувається `JSON.stringify()` **не призначений** для безпечної вставки в HTML. Він коректний як JavaScript-синтаксис, але **не враховує особливості HTML-контексту**, де `<script>` може бути передчасно закритий. --- ## Що конкретно небезпечно | Небезпечна послідовність | Чому | Приклад | |---|---|---| | `</script>` | Закриває тег `<script>` | `"</script><script>alert(1)</script>"` | | `<!--` або `-->` | Може почати або закрити HTML-коментар | `"<!--evil-->"` | | `<script`, `<img`, `onerror=` | Може ініціювати новий тег | `"<img src=x onerror=alert(1)>"` | | Символи `</`, `<`, `>` | Порушують HTML-структуру | `"Hello </div>"` | --- ## Як правильно ### 1. Безпечно екранувати JSON перед вставкою Приклад (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> `; ``` Тепер навіть якщо користувач введе `</script>`, браузер отримає `</script>`, а це **не закриє тег** і **не виконає скрипт**. --- ### 2. Використовувати готові утиліти Фреймворки на кшталт **Next.js** чи **Nuxt** роблять це автоматично: ```javascript // приклад Next.js <NextScript /> // генерує безпечну вставку initialProps, з екрануванням спецсимволів ``` Якщо пишеш SSR вручну - використовуй готові бібліотеки: - `serialize-javascript` - `safe-json-stringify` ```javascript import serialize from 'serialize-javascript'; const html = `<script>window.__DATA__ = ${serialize(data, { isJSON: true })}</script>`; ``` --- ### 3. Ізолювати дані від коду Краще **не вставляти JSON напряму** в `<script>`, а віддавати його, наприклад, через `<script type="application/json">`: ```javascript <script id="initial-data" type="application/json"> {"user": {"name": "Tim"}} </script> ``` А на клієнті: ```javascript const data = JSON.parse(document.getElementById('initial-data').textContent); ``` Так браузер **ніколи не виконає** вміст цього скрипту як JS-код. --- ## Короткий підсумок | Що | Опис | |---|---| | Проблема | `JSON.stringify()` вставляє "сирий" JSON в HTML, дозволяючи XSS | | Загроза | Зловмисник може впровадити `<script>` або інші небезпечні теги | | Рішення | Екранувати `<`, `>`, `&`, ` `, ` ` | | Альтернатива | Використовувати `serialize-javascript` або `<script type="application/json">` | --- ## Висновок При SSR: > Ніколи не вставляй результат `JSON.stringify()` напряму в HTML > Завжди екрануй спецсимволи або використовуй безпечні серіалізатори Це **одна з найчастіших XSS-вразливостей у SSR-застосунках**, її можна запобігти одним рядком `.replace(/</g, '\\u003C')`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.