Введение
Некоторое время назад отдел набирал front-end разработчиков, чтобы сделать наш сайт
-
Быстрее
-
более стабильный
-
безопаснее
Я также вкратце спросил кандидатов о трех принципах внешней безопасности, в том числе о знаниях, связанных с XSS-атаками. Ниже представлен процесс нашего интервью.
Я: xxx Вы когда-нибудь слышали о XSS-атаках?
Кандидат: XSS знает, это просто..., но я чувствую, что сейчас мы используем современные фреймворки, такие как React, и нам больше не нужно делать защиту, связанную с XSS, фреймворк уже сделал это за нас.
Я: Можно ли использовать такую структуру, как React, чтобы предотвратить XSS-атаки?
Когда их спрашивают здесь, кандидаты начинают колебаться. Читатели, также спросите себя молча, правда ли, что XSS-атаки не будут происходить при использовании react? Чтобы все могли лучше усвоить знания, связанные с XSS-атаками, автор в основном остановится на следующих моментах.
- Что такое XSS-атака
- Пример XSS-атаки
- XSS-атака в среде реагирования
- Защита от XSS-атак
- Эпилог
Что такое XSS-атака
Полное название атаки XSS — атака с использованием межсайтовых сценариев. Его не следует путать с аббревиатурой Cascading Style Sheets (CSS). Поэтому атака с использованием межсайтовых сценариев сокращенно называется XSS. XSS — это уязвимость компьютерной безопасности в веб-приложениях. Позволяет злоумышленникам вводить код на страницы, предназначенные для использования другими пользователями.
Классификация XSS-атак
-
Светоотражающий
Reflected XSS просто «отражает» данные, введенные пользователем в браузере. Другими словами, хакерам часто необходимо побудить пользователей «щелкнуть» вредоносную ссылку для успешной атаки. Отраженный XSS также называют «непостоянным XSS» (Non-persistent XSS).
-
тип хранения
Сохраненный XSS «хранит» введенные пользователем данные на стороне сервера (база данных). Сохраненный XSS часто также называют «Постоянный XSS» (Persistent XSS).
-
Dom Based XSS
XSS на основе DOM не делится в зависимости от того, «существуют ли данные на стороне сервера», XSS на основе DOM также является отражающим XSS. Отдельно, потому что XSS на основе DOM формируется по особой причине — XSS формируется путем модификации узлов DOM страницы.
Пример XSS-атаки
Теперь, когда мы знакомы с тем, что такое XSS-атака, и с классификацией XSS-атак, давайте будем хакерами! Далее мы будем моделировать три типа атак соответственно.Рекомендуется сначала клонировать мой пример кода.
Код, использованный в статье, см.склад, это каталог security/xss
работающий экземпляр
git clone https://github.com/chenshengshui/Web-Frontend-Study-Map.git
cd security
npm install
npm run xss
Отраженный пример XSS
демо
Откройте браузер и перейдите на сайт угрозы:http://localhost:3000/non-persistent-xss.html
Анализ исходного кода
Целевой сайт выводит введенные пользователем параметры прямо на странице:
/*
* 目标网站
*/
const express = require('express');
const app = express();
app.use(express.static('./xss'));
app.get('/', function(req, res) {
res.setHeader('X-XSS-Protection', 0); // 这个处理是为了关闭现代浏览器的xss防护
res.send('早上好' + req.query['name']);
});
app.listen(3000);
Сайт угрозы:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<a href="http://localhost:3000/?name='<script>alert(/xss/)</script>'"
>点我进入目标网站</a
>
</body>
</html>
Веб-сайт угрозы побуждает пользователей щелкнуть ссылку, но ссылка содержит сценарий атаки.Если мы войдем на наш целевой веб-сайт с веб-сайта угрозы, мы обнаружим, что предупреждение ('xss') выполняется на целевом веб-сайте. Глядя на исходный код, обнаруживается, что тег скрипта в параметре был прописан на странице, что явно не то, что хочет видеть разработчик.
Пример сохраненного XSS
Классический сценарий хранимой XSS-атаки — это когда хакер пишет сообщение в блоге, содержащее вредоносный код Javascript.После публикации статьи все пользователи, посещающие сообщение в блоге, запускают вредоносный код Javascript в своих браузерах. Вредоносный скрипт будет сохранен на сервере, поэтому такая атака XSS называется «хранимой XSS».
демо
Откройте браузер и перейдите на сайт:http://localhost:3000/persistent-xss.html, Введите следующее в текстовое поле:
<div style="color: red">
欢迎大家阅读我的博客,我是WaterMan,喜欢就关注一下呗!
</div>
<script>
alert('哈哈,我窃取了你的token了,下次注意噢' + document.cookie);
</script>
После нажатия кнопки «Сохранить» наши файлы cookie будут украдены.
Анализ исходного кодаСначала взгляните на наш внешний код, так просто, есть только одно текстовое поле и одна кнопка отправки.Нажатие кнопки отправки отправит содержимое, введенное пользователем, в фоновый режим, нет проблем, мы обычно разрабатываем его так.
document.getElementById('submit').onclick = function() {
var content = document.getElementById('textarea').value;
var url = 'http://localhost:3000/postArticle';
easyRequest()
.post(url, {
content: content
})
.then(res => {
alert(res.message);
window.open('http://localhost:3000/persistent-xss');
});
};
function easyRequest() {
return {
post: function(url, data) {
return fetch(url, {
body: JSON.stringify(data),
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'content-type': 'application/json'
},
method: 'POST',
mode: 'cors',
redirect: 'follow',
referrer: 'no-referrer'
}).then(response => response.json());
}
};
}
Давайте посмотрим на наш внутренний код. Внутренний код также очень прост. Он сохранит нашу отправку в файл. Здесь файл используется для имитации общей базы данных.
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require('fs');
app.use(express.static('./xss'));
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
app.use(function(req, res, next) {
// 模拟设置用户tookie
res.setHeader('Content-Type', 'text/html;charset=utf-8');
res.cookie('token', 'zkskskdngqkkkgn245tkdkgj');
next();
});
app.get('/', function(req, res) {
res.setHeader('X-XSS-Protection', 0);
res.send('早上好' + req.query['name']);
});
app.get('/persistent-xss', function(req, res) {
fs.readFile('./xss/data.txt', function(err, data) {
if (err) {
return res.send(err);
}
res.send(data);
});
});
app.post('/postArticle', function(req, res) {
const content = req.body.content + '\n';
fs.writeFile('./xss/data.txt', content, { flag: 'w' }, function(err) {
if (err) {
return res.send({ status: 500, message: err.message });
}
res.send({ status: 200, message: '保存成功' });
});
});
app.listen(3000);
Таким образом, пока любой пользователь посещает веб-сайт с подробной информацией о статье http://localhost:3000/persistent-xss, его токен будет получен.
DOM Based XSS
XSS, сформированный путем изменения узлов DOM страницы, называется XSS на основе DOM. Просто посмотрите на следующий код
<body>
<div id="content"></div>
<input type="text" id="text" value="" style="width: 500px" />
<input type="button" id="submit" value="write" />
</body>
<script>
document.getElementById('submit').onclick = function() {
var str = document.getElementById('text').value;
document.getElementById('content').innerHTML =
"<a href='" + str + "'>testLink</a>";
};
</script>
После нажатия кнопки «написать» на текущую страницу будет вставлена гиперссылка, адрес которой является содержимым текстового поля. Здесь, после запуска события onclick кнопки «запись», DOM-узел страницы будет изменен, и часть пользовательских данных будет записана на страницу в виде HTML через innerHTML, что приведет к XSS на основе DOM. Построить следующие данные
' onclick=оповещение(/xss/)// После ввода код страницы становится таким:
<a href='' onclick=alert(/xss/)//' > testLink</a>
Сначала закройте первую одинарную кавычку href одинарной кавычкой, затем вставьте событие onclick и, наконец, закомментируйте вторую одинарную кавычку комментарием. Нажмите на эту вновь сгенерированную ссылку, и скрипт будет выполнен. Итак, мы запустили XSS-атаку на основе DOM. Вы также можете запустить проект и открыть егоhttp://localhost:3000/dom-based-xss.htmlПопробуйте веб-сайт.
XSS-атака в среде реагирования
Фреймворк React действительно намного безопаснее, чем написание кода с помощью Jquery, потому что React уже сделал за нас много работы. Здесь мы кратко опишем, что именно React делает.
Введение на официальном сайте React, есть такое описание:
It is safe to embed user input in JSX:
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;
По умолчанию React DOM экранирует любые значения, встроенные в JSX, перед их рендерингом. Таким образом, он гарантирует, что вы никогда не сможете внедрить что-либо, что явно не написано в вашем приложении. Все преобразуется в строку перед рендерингом. Это помогает предотвратить XSS (перекрестный -site -scripting) атаки.
Короче говоря, безопасно встраивать пользовательский ввод непосредственно в React, потому что React Dom перед рендерингом закодирует значение в JSX, что гарантирует, что вы не внедрите какой-либо код, который угрожает вашему приложению. Перед рендерингом все преобразуется в строку, что помогает защититься от XSS-атак.
Так как же работает это кодирование сущности HTML?
& becomes &
< becomes <
> becomes >
" becomes "
' becomes '
Конечно, React не конвертирует все сразу, иначе мы не сможем нормально рендеритьВ ожидании метки механизм защиты xss-fitler будет подробно описан здесь позже.
Давайте сначала вернемся к атакам XSS в React. В React есть APIопасноSetInnerHTML, который специально используется для рендеринга HTML.Давайте посмотрим на введение официального сайта:
ОпасноSetInnerHTML — это замена React для использования innerHTML в модели DOM браузера. В целом установка HTML из кода сопряжена с риском, поскольку легко непреднамеренно подвергнуть ваших пользователей атаке межсайтового скриптинга (XSS). Таким образом, вы можете установить HTML непосредственно из React, но вы должны напечатать опасноSetInnerHTML и передать объект с ключом __html, чтобы напомнить себе, что это опасно.Например:
function createMarkup() {
return { __html: 'First · Second' };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
Тем не менее, опасноSetInnerHTML является заменой для innerHtml, но у него все еще есть проблемы с безопасностью.
Запустите пример
Или предыдущий склад, входим в директорию /security/react-xss
cd security/react-xss
npm install
npm start
Анализ исходного кода
class Chat extends Component {
state = {
input: '',
result: ''
};
onChange = e => {
this.setState({
input: e.target.value
});
};
onSubmit = () => {
this.setState({
result: this.state.input
});
};
render() {
return (
<div styleName="container">
<div styleName="msg">
<textarea onChange={this.onChange} placeholder="在此输入内容" />
<button onClick={this.onSubmit}>提交</button>
<div dangerouslySetInnerHTML={{ __html: this.state.result }} />
</div>
</div>
);
}
}
То есть, когда в React используется APIопасноSetInnerHTML, необходимо обратить внимание на то, чтобы легко привести к XSS-атакам.
Мы уже полностью поняли определение и классификацию XSS-атак и поняли, что React не является надежным, так как же мы можем улучшить безопасность нашего веб-сайта? Далее идет главный приоритет этой статьи, я опишу несколько средств защиты от XSS-атак, чтобы сделать ваш сайт более безопасным.
Защита от XSS-атак
Защита от XSS сложна. Ниже я опишу несколько методов защиты от XSS, которые я изучил. Если есть другие методы, которые я не упомянул, читатели могут добавить их в область комментариев. Наша цель - сделать наш сайт более безопасным и улучшить возможности каждого. .
Далее мы в основном сосредоточимся на следующих моментах для описания защиты от XSS-атак:
- Четыре или два куска золота - HttpOnly
- входная проверка
- выходная проверка
- Правильная обработка форматированного текста
Четыре или два куска золота - HttpOnly
Что такое HttpOnly?
HttpOnly — это атрибут, который устанавливает, может ли файл cookie быть прочитан сценарием javasript, браузер запретит Javascript страницы доступ к файлу cookie с помощью атрибута HttpOnly.
Вернемся к коду примера, посмотрим на синтаксис установки куки ниже:
const express = require('express');
const app = express();
app.use(function(req, res, next) {
res.setHeader('Content-Type', 'text/html;charset=utf-8');
// 模拟设置用户tookie
res.cookie('token', 'zkskskdngqkkkgn245tkdkgj');
next();
});
Установленный здесь файл cookie не устанавливает для HttpOnly значение true, поэтому мы можем напрямую использовать document.cookie для получения значения токена. Как показано ниже
Подробную информацию о файлах cookie можно просмотреть на вкладке «Приложение» в консоли.
Чтобы хакеры не могли получить информацию о файлах cookie через сценарии Javascript, мы можем включить HttpOnly.
const express = require('express');
const app = express();
app.use(function(req, res, next) {
res.setHeader('Content-Type', 'text/html;charset=utf-8');
// 模拟设置用户tookie
res.cookie('token', 'zkskskdngqkkkgn245tkdkgj', { httpOnly: true });
next();
});
Если вы не используете такие фреймворки, как экспресс, собственные узлы nodejs можно установить следующим образом.
response.setHeader('Set-Cookie', 'token=zkskskdngqkkkgn245tkdkgj;HttpOnly');
Строго говоря, HttpOnly не предназначен для борьбы с XSS, HttpOnly решает атаку перехвата файлов cookie после XSS.
Расширение знаний о настройке файлов cookieCookie установлен HttpOnly, безопасный атрибут может эффективно предотвратить XSS-атаку, установить ответ X-Frame-Options заголовок, чтобы избежать кликджекинга.
Введение атрибута:
-
Безопасное имущество При значении true это означает, что созданный файл cookie будет передаваться на сервер в защищенном виде (ssl), то есть он может быть передан браузером на сервер только для сеансовой аутентификации в HTTPS-соединении. HTTP-соединение не будет передавать информацию, поэтому конкретное содержимое файла cookie не будет украдено.
-
Свойство HttpOnly Если в файле cookie установлен атрибут «HttpOnly», программа (JS-скрипт, апплет и т. д.) не сможет прочитать информацию из файла cookie, что может эффективно предотвратить атаки XSS.
-
Заголовок ответа X-Frame-Options Заголовок ответа Http X-Frame-Options указывает, разрешена ли страница в
<frame>、<iframe>、<object>
знак показан в . Предотвратите кликджекинг, установив X-Frame-Options, чтобы предотвратить встраивание страниц сайта в другие страницы. X-Frame-Options имеет три значения: OENY: Эту страницу нельзя отображать во фрейме, даже если она вложена в страницы с тем же доменным именем. SAMEORIGIN: Эта страница может отображаться во фрейме той же страницы домена. РАЗРЕШЕНИЕ ОТ uri: страница может отображаться во фрейме указанного источника.
//设置cookie
response.setHeader(
'Set-Cookie',
'JSESSIONID=' + sessionid + ';Secure;HttpOnly'
); //设置Secure;HttpOnly
response.setHeader('x-frame-options', 'SAMEORIGIN'); //设置x-frame-options
входная проверка
Распространенные веб-уязвимости, такие как XSS, SQL Injection и т. д., требуют от злоумышленников создания некоторых специальных символов, которые не могут использоваться обычными пользователями, поэтому необходима проверка ввода.
Проверка ввода, которая часто используется для проверки формата, просто означает добавление белого списка к информации, введенной пользователем. Например, имя пользователя, которое необходимо вводить при регистрации на веб-сайте, должно представлять собой комбинацию букв и цифр. Например, "waterMan1" является допустимым именем пользователя, а "waterMan$^" недопустимо. Проверка ввода должна быть реализована в коде на стороне сервера, потому что проверка ввода с помощью javascript на стороне клиента может быть легко обойдена злоумышленниками. Итак, в качестве переднего плана, можем ли мы засунуть руки в карманы? На самом деле, в настоящее время, когда nodejs популярен, нам часто кажется, что он слишком прост. Мы можем выполнять проверки ввода на стороне узла, чтобы убедиться, что наш сайт безопасен. Отступив на 10 000 шагов назад, мы добавляем в клиентский код проверку ввода, которая также может блокировать большинство обычных пользователей, которые неправильно работают, тем самым экономя ресурсы сервера и повышая порог атаки хакеров.
Так как же мы выполняем проверку ввода?
грубая практика
Самый эффективный способ проверки ввода — фильтровать или кодировать некоторые специальные символы, например
& becomes &
< becomes <
> becomes >
" becomes "
' becomes '
Тем не менее, из-за отсутствия контекста такая насильственная обработка действительно безопасна, но она также сделает некоторые обычные вводимые данные трудными для понимания и сведет пользователей с ума.
Например:
1 + 2 < 4 becomes 1 + 2 < 4
Такой результат явно не то, что хочет видеть пользователь.
Умный xss-фильтр
Фильтр XSS получает переменные, когда пользователь отправляет данные, выполняет проверку XSS и выполняет более интеллектуальную «проверку входных данных» для соответствия характеристикам XSS. Спроектировать такую интеллектуальную библиотеку XSS-фильтра очень сложно, поэтому в следующей статье мы подробно опишем реализацию библиотеки XSS-фильтра. Приглашаю всех стать со мной свидетелями процесса создания библиотеки XSS-фильтров с нуля.
выходная проверка
По сути, проверка вывода и проверка ввода делают одно и то же, но проверка вывода будет проще, потому что проверку вывода можно комбинировать с конкретным контекстом, но рабочая нагрузка проверки вывода будет относительно большой, на самом деле React сам фреймворк уже сделал для нас много проверок вывода. На самом деле, проверка вывода также может быть единообразно перехвачена на интерфейсе, что согласуется с проверкой ввода.
Обработка форматированного текста
Обработка форматированного текста согласуется с проверкой ввода. Мы добавим белый список к информации, введенной пользователем, например, разрешить пользователю ввод в форматированном тексте.<img >
т.д. теги, ввод не разрешен<script>
и другие теги, здесь также задействована библиотека xss-filter, о которой будет подробно рассказано в следующей статье.
Эпилог
Это снова конец, и я очень рад, что все смогли выстоять до конца. Защита от XSS по-прежнему достойна глубокого изучения, одним словом, я надеюсь, что мы все сможем извлечь уроки из моей статьи. Наконец-то всем хороших выходных!
@Author: WaterMan