В InVision наскоро публикувахме Правила за сигурност на съдържанието (CSP) за нашите уеб приложения.

Политиката за сигурност на съдържанието (CSP) е защитен слой, който помага за откриване и смекчаване на определени видове атаки, включително Cross-Site Scripting (XSS) и атаки за инжектиране на данни.

Конфигурирането на политика за сигурност на съдържанието включва създаване на списък с ресурси, към които потребителският агент има право да се свързва или да зарежда за страница, като JavaScript, CSS, шрифтове и iframes. Този списък трябва да се добави към Content-Security-Policy HTTP заглавката или <meta http-equiv="Content-Security-Policy">.

След това браузърът ще се консултира с политиката всеки път, когато трябва да поиска ресурс, за да определи дали има право да го зареди. Ако даден ресурс не може да бъде зареден, браузърът ще хвърли SecurityPolicyViolationEvent, което можете да съберете и изпратите на агрегатор за нарушения (като Sentry или Report-URI) за допълнителен анализ.

Добавянето на политика за сигурност на съдържанието към уеб страница е чудесен начин за подобряване на нейната сигурност, но не всичко е слънчево и дъгово.

Между криптични доклади за нарушения и несъответствия между различни браузъри, открихме, че разбирането на това, което е блокирано на вашия уебсайт (и защо), може да не е толкова лесно, колкото звучи.

В тази публикация искаме да подчертаем няколко проблема, които забавиха внедряването на CSP.

Несъответствия между различни браузъри

Като начало научихме по трудния начин, че има няколко разлики в начина, по който всеки браузър прилага CSP спецификацията, причинявайки нарушение на SecurityPolicyViolationEvent да има различни стойности в зависимост от браузъра, който го е уловил.

Повечето от тези разлики са незначителни, но някои могат да затруднят разбирането защо се случва конкретно нарушение.

За да възпроизведем тези несъответствия във вашия браузър, създадохме примерен HTML файл със следния CSP:

script-src 'unsafe-inline';

Този CSP позволява само вградени ресурси, като вградени <script> елементи, вградени манипулатори на събития и вградени <style> елементи.
В примера зареждаме външен скрипт (JQuery), за да принудим SecurityPolicyViolationEvent.

Основните разлики със събитието за нарушение, което забелязваме:

  • violationEvent.effectiveDirective: е директивата, чието прилагане разкри нарушението.
    Във Firefox и Safari това е директивата на правилата, която е била нарушена. В примера това е зададено наscript-src.
    В Chrome това ще бъде най-„конкретната“ директива, която е била нарушена. В примера това е script-src-elem — дори и да не сме декларирали тази директива — защото тук щеше да възникне нарушението, ако такава директива присъстваше в политиката.
  • violationEvent.blockedURI : е URI адресът на ресурса, който е блокиран, защото нарушава правила.
    В Chrome и Firefox това е пълният URI адрес на ресурса. В примера това е https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js.
    В Safari това е само произходът на ресурса. В примера това е https://ajax.googleapis.com.
  • violationEvent.violatedDirective: е директивата, чието прилагане разкри нарушението. Следвайки спецификацията на CSP3, това е „Копие на свойството effective-directive, запазено по исторически причини“.
    В Chrome и Firefox това е директивата на правилата, която е нарушена. В примера това е script-src.
    В Safari това е директивата и стойността, която е била нарушена. В примера това е script-src 'unsafe-inline'.

Safari и директивата base-uri

Друго незначително несъответствие в браузъра, което открихме, е, че в Safari премахването на елемент <base> от страница с директива CSP base-uri причинява нарушение на CSP — независимо от стойността, зададена в base-uri.

Директивата base-uri ограничава URL адресите, които могат да се използват за указване на основния URL адрес на документа.

Създадохме „малък пример“, който можете да използвате, за да възпроизведете проблема във вашия браузър.

От нашето разбиране за спецификацията на CSP и алгоритъма, дефиниран в спецификацията на HTML5 за получаване на базата на документа, проблемът трябва да е в края на Safari: ако на страницата не е наличен елемент <base>, базата на документа трябва да се върне към URL адреса на документа (вместо да хвърлят нарушение).

Нарушението wasm-eval

Забелязахме, че Chrome съобщава за объркваща грешка на страници, които използват WebAssembly с активиран CSP.

Към днешна дата единственият начин да накарате WebAssembly да работи с CSP е да добавите script-src unsafe-eval към правилата — поне докато не е налична по-добра, специфична за wasm опция (wasm-eval).

Все пак, дори и с активирана клауза unsafe-eval, Chrome отчита следната грешка:

[Само за отчет] Отказано компилиране или инстанциране на модул WebAssembly, тъй като 'wasm-eval' не е разрешен източник на скрипт в следната директива на правилата за сигурност на съдържанието: “script-src 'self' 'unsafe-inline' 'unsafe- eval'

Тази грешка беше много объркваща, защото не означава, че Chrome е отказал да компилира или инстанцира модула WebAssembly — това е просто предупреждение, задействано, защото клаузата wasm-eval все още не се поддържа от Chrome.

Нарушения за ресурси, които са правилно зададени в правилата

Това определено беше най-отнемащият време проблем, който трябваше да отстраним.

Като проверихме докладите за нарушения на CSP, забелязахме, че много от тях са на ресурси, които всъщност разрешихме в нашия CSP.

Например, имахме множество нарушения на ресурса https://px.ads.linkedin.com (използван от инструмента за проследяване на пиксели на LinkedIn), дори ако беше изрично разрешен в нашия списък.

За съжаление, не можахме да възпроизведем нито едно от тези нарушения и допълнителните метаданни за нарушението (потребителски агент, браузър, местоположение и т.н.) не загатваха за обща причина.

Първоначално смятахме, че тези нарушения се задействат от разширения на браузъра (като рекламни блокери), които прилагат по-строг CSP, за да блокират някои ресурси по време на изпълнение. Започнахме ръчно да тестваме безброй разширения на браузъра, но никога не успяхме да възпроизведем тези нарушения дори веднъж – всички разширения на браузъра блокираха ресурси на ниво мрежова връзка.

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

След няколко дни търпението ни се отплати!

Получихме доклад за нарушение, идващо от браузъра на инженер на InVision, така че се сдвоихме и най-накрая открихме основната причина за проблема: пренасочвания!

Това бяха пренасочвания! В случай на нарушения, причинени от пренасочвания, браузърите докладват в blocked-uri първоначалния URI на веригата за пренасочване.

В браузъра на този инженер на InVision https://px.ads.linkedin.com URL адресът се пренасочва към https://px4.ads.linkedin.com (от LinkedIn сървъри).
Тъй като нашите правила позволяват https://px.ads.linkedin.com (но не px4), генерираното нарушение има https://px.ads.linkedin.com като URI, дори ако нарушеният URI е https://px4.ads.linkedin.com.

Това поведение прави отстраняването на грешки още по-предизвикателно, тъй като нямаме подробности за това кой целеви URI на веригата за пренасочване всъщност е нарушил CSP — получаваме само оригиналния URI, който вече е разрешен. Усложнявайки това, това обикновено се причинява от сървъра на ресурсите, крайните манипулатори, проксита и т.н. по много причини - като геолокация, проблеми със сървъра, отхвърлени API и т.н., което ги прави много тромави за прогнозиране и възпроизвеждане, без да знаят причина за пренасочването.

В допълнение към проблемите с пренасочването към различни източници, ние също забелязахме, че нарушение може да бъде предизвикано от несъответствие на типа съдържание между очаквания тип на ресурса и неговия получен тип.
Например, ако мрежова заявка за скрипт върне 404/500 HTML резултат, браузърът задейства нарушение без никаква информация, намекваща за несъответствие на типа съдържание.

Към момента изглежда, че почти нищо не можем да направим, за да улесним изживяването при отстраняване на грешки и е необходимо много дълбоко гмуркане, за да го разберем. Не можахме да намерим много помощ онлайн за тези проблеми по време на нашето пътуване, така че се надяваме тази публикация да помогне на други екипи в тяхното CSP пътуване!

Ако се интересувате да помогнете за решаването на тези вълнуващи предизвикателства, ние винаги наемаме!