Правила за защита от XSS атаки

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

Никога не вярвай на потребителя

Винаги валидирайте, нормализирайте и филтрирайте всички входящи данни. Това правило няма изключение и не се отнася само за XSS, а за всичко по принцип. Ето обикновено какво правя аз:

  • Прехвърлям всичко в UTF-8
  • Проверка дали дадена стойност идва
  • Махам интервалите в началото и края (trim)
  • Махам, ако има, повече от 1 интервал между думите $d = preg_replace('/\s\s+/', ' ', $_POST['comment']);
  • Проверявам за минимална/максимална дължина на текста, или големина на числото
  • Ако има проблем до тук слагам стойност по подразбиране.

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

Използвайте bbcode

Ако bbcode е подходящо за вашето приложение, то тогава вземете готова bbcode библиотека, или следвайки идеята на bbcode направете собствена имплементация. Идеята е проста, всичко което постъпва от потребителя минава през strip_tags(), което премахва всички html тагове. По този начин, почти не остава начин за изпълнение на XSS, но пък се губи форматирането. strip_tags() свежда всичко до текст без тагове. Единият вариант е да разрешите серия тагове, като b,i и подобни, но това не е добра идея. Понеже bbcode има собствени тагове, те минават, и в последствие, в сигурна среда тези bb тагове се конвертират в съответните b,i и подобни.

Ако няма да използвате bbcode по някаква причина, то тогава трябва да:

Изчистите най-вероятните причинители на XSS

Задължително трябва да прехвърлите определени знаци, в съответните им HTML entry и да ги изпращате към браузъра само в този вид. Задължително трябва да конвертирате:
& -> &
< -> &lt;
> -> &gt;
“ -> &quot;
‘ -> &#x27;
/ -> &#x2F;
Всеки език има функции или обекти да направи това прехвърляне. Хубавото на това е, че когато браузъра види &lt; ще го визуализира като < тоест <script ще бъде пратено като &lt;script , и бразузъра няма да го изпълни като javascript.

Прекарвайте всичко през подобно филтриране, не само „текстовете“

Примера до тук бе с текстово поле за коментар, но това не значи, че само от там може да се изпълни XSS. На практика, ако позволите на някой да „пише“ по вашия html, без значение дали това е коментар, URL на снимка, CSS дефиниция или каквото и да е, то той може да изпълни XSS атака. Ако сте пропуснали – „Никога не вярвайте на потребителя“

Пазете оригинала на данните

Това не е правило, а по-скоро идея, която аз използвам. Когато обработвам данните от потребителя, ако засека намек за XSS, тоест усетя, че има някой от знаците като <> записвам оригиналните непроменени данни в лог, заедно с данни за потребителя. Така лесно може да се проследи, кой какво и как се опитва да хитрува. Внимавайте колко записвате, ако сайта е посещаван, бързо може да съберете гигабайти. Не слагайте тези неща в базата, по-добре е в текстови лог.

Не слагайте лични данни и „коментари“ на една страница

XSS не е само за крадене на сесия, XSS може да вземе цялата HTML страница, или част от нея, и да я изпрати на някой сървър. Тоест ако на страницата за „профил“ на потребителя имате някакво поле, чийто данни идват от външни потребители, то ако мине XSS атака, този код, ще може да вземе цялата страница с всички данни на потребителя. Ако дизайна или друго ви налага да сте в такава ситуация, то тогава измислете начин да не сте. Опасно е! Ако все пак не можете, намерете начин да объркате показаната информация. Използвайте някой от методите за защита на показан email. Тоест JS прави така, че дадени текстове да се виждат от човек, но да са абсолютно безсмислени за код. Все пак, промяната в дизайна е по-добрата опция.

HTTPOnly флаг на бисквитките

Всеки език позволява да вдигнете този флаг, и повечето браузъри го спазват. Това което прави е, че не позволява на JS да чете тези бисквитки. Тоест ако мине XSS, то кода не може да види бисквитките с този флаг, тоест няма кражба на сесия. Винаги го ползвайте за сесийните бисквитки.

Направете отпечатък на браузъра

Когато стартирате сесия на потребителя направете хеш от вид и версия на ОС и Браузър.

$fingerprint=md5($_SERVER[‘HTTP_USER_AGENT’]);

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

Алгоритъм за разстояние

Използвайте geoIP за да намерите приблизителното местоположение на потребителя. PHP има добра библиотека за целта. При стартиране на сесия засичате потребителя на ниво град, и при всяка следваща заявка проверявате дали е в този район. Не може за 30 минути да отиде от София до Варна. Има доста примери по темата в интеренет. Тази защита се брои за доста напреднала, така че правете я само ако знаете какво правите понеже:

На IP не може да се разчита

Голяма част от хората са с динамични IP адреси, други минават (доброволно или не) през прокси сървъри, и е възможно 2 заявки от един и същи потребител, да дойдат от 2 различни адреса, дори и заявките да са в рамките на секунда. Някой доставчици правят нещата още по-зле, като AOL , понеже при тях всяка заявка е с различно IP, и на всичко отгоре е и от различен град.

Не стряскайте потребителя

Ако засечете нещо, което ви изглежда нередно, и може да е следствие от XSS, не вадете големите червени екрани, и не стряскайте потребителите. Може заради неразбиране, грешка, или извънземните, нещо да се е объркало. В този случай просто помолете отново за парола, както го правят google. Ако е XSS, кракера има сесия, но не и парола. Елегантно и спокойно помолете потребителя да си въведе паролата, и ако е ОК, значи е ОК.  Ако не е ОК, вие вече сте унищожили сесията 🙂

Направете си своя библиотека за филтриране

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

Проверете вашия клас

Може да влезете тук и да намерете списък на почти всички известни XSS „врътки“. Там няма да видите код за чист XSS, а просто техниката, по която може да вкарате JS в браузъра. Тоест, прекарайте примерете през вашия клас, и ако мине, значи имате проблем. Някой от примерите са специфични на конкретен браузър, примерно бъг на IЕ в енкодинга, така че трябва да тествате библиотеката си и през различните браузъри.

51 thoughts on “Правила за защита от XSS атаки

  1. Мартин

    Ето и едно от мен, което не е точно по XSRF.
    При пращане на някакви $_POST $_GET заявки свързани с потребителя, които са от голямо значение, винаги създавайте някакво скрито input золе, което да съдържа даден ключ на рандом принцип и проверявайте ключа. Прочетете информация в интернет и вижте какво е по-горната атака.

  2. gatakka Автор

    Така е, друг вариант на това е, не да се праща някой „ключ“, а имената на самите полета да не са „username“ или подобни, винаги еднакви , а самите имена да са случайни числа, а в сървъра пазите връзката кое число кое поле е.

  3. gatakka Автор

    Ако има интерес ще я разширя. Смятам, че съм доста наясно с тези неща. Има доста какво да се каже.

  4. Данаил

    Значи един вид да си измисл „език“ на който да пиша имената на полетата и само този който знае този „език“ ще може да се възползва от тях.

  5. gatakka Автор

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

  6. Гошо

    Аз имам едно малко въпросче. php prepare statement спират ли sql injection на 100% ?

  7. Гошо

    А по темата. Тея bbcode в момента ми се налага да ги използвам. Ама немога да разбера точно техния смисъл. Това което съм разбрал е ,че текстовете трябва да се филтрират за всичко и в db-то трябва да има само текст и [b][i] ….. и подобни. После с функция те се преобразуват съответно. Това ли е смисъла основно ?

  8. gatakka Автор

    Да, смисъла е да не позволи на някой да бутне вътре какъвто и да е html, js или друг код понеже може да направи атака,или да ти потроши дизайна.
    За това всичко се чисто до чист текст бе никакви тагове и знаци, които значат нещо за браузъра, и после [b] се превръща в HTML, който обаче ти създаваш. Тоест няма опасност да си чупиш дизайна или да се изпълни код.

  9. Николай

    @Гошо точно това е смисъла [b] и [/b] ги правиш на и . Така се предпазваш от зловреден жабаскрипт и такива гадории 😀

  10. dns

    gatakka Светкавична реакция! (все едно сайта има онлайн съпорт )

    „Ако има интерес ще я разширя.“

    Има има 🙂

  11. Peter Bivolarsky

    Евалата, много полезна статия! Продължавай в същия дух, аз разбрах за теб от двете видеа във Фейсбуук и попаднах на блога ти пак някъде от нета… С интерес ще следя! 🙂

  12. Ангел К

    Като използване ббкод, не значи не пак не трябва да се изгърбите от проверки, най-вече заради ‘любимия’ ни браузер, завършващ на 6.0
    hint:
    [img]javascript:alert(1)[/img]

  13. Валентин

    Сега правя 1 сайт,и срещу XSS съм използвал htmlspecialchars(); и съм забранил символите „“ ,това не стига ли ? 🙂

  14. gatakka Автор

    htmlspecialchars() НЕ е достатъчно да спре и половината начини за XSS.
    Както @Ангел К даде пример
    [img]javascript:alert(1)[/img]
    а това си минава през htmlspecialchars

  15. Гошо

    Разглеждах доста варианти из интернет и се спрях на това HTML Purifier. Според вас добро ли е ?

  16. стефан

    хубава статия! А би ли казал как да проверя дали сайта ми е защитен от такъв тип атаки ?

  17. gatakka Автор

    @стефан просто копираш някой от примерите в някоя твоя форма за „контари“ и като отвориш съответната страница, ако ти изкочи един alert() значи хака е минал.

  18. Гошо

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

  19. gatakka Автор

    да htmlentities са мощен инструмент понеже конвертира точно <> и другите значи в съответните им html форми, и така браузъра не ги изпълнява. Все пак има няколко, които ще минат, но ще сработят в определена версия само.
    Примерно на IE6
    javascript:alert(1)
    \“;alert(‘XSS’);//
    ¼script¾alert(¢XSS¢)¼/script¾

  20. svetljjjo

    Ванка дай и някакви примерни кодове на РНР де.
    Хубаво обясняваш ама трябва и малко примерчета написани да има да се види как се реализира на практика.

  21. Анонимен

    А да попитам сега: Какво правим, когато потребител сам сложи Куки. Няма начин, по който да се предотврати. Някакви техники за справяне с този проблем?

  22. gatakka Автор

    HTML Purifier не е съвместим с bbcode в смисъл, че при bbcode ти нямаш html, а html-то при него се генерира в последствие, в контролирана среда.
    html Purifier чисти от xss и оправя неща като незатворени тагове 🙂
    bbcode се използва когато имаш много input от потребителите, но не искаш да имат много силен контрол върху html-то
    html Purifier се ползва с неща като tinyMCE, където хората имат нужда от сериозен контрол върху външният вид.

  23. Awaken

    Ами аз друго те питах. Ползвам tinyMce и той има възможност за bbcode и за html. Та сега работи на html + codeigniter xss защитата + htmlpurifier. Има ли смисъл да минавам на bbcode?

  24. gatakka Автор

    Извинявай, не разбрах въпроса одеве 🙂
    Ако минеш на bbcode ще загубиш огромна част от възможността за форматиране и генериране на съдържание. Комбинацията ти в момента е доста сериозна защита от към xss, така че не смятам, че bbcode ще ти даде нещо повече.

  25. WildBeast

    Много полезна статия! Аз обикновено минавам променливите със следните функции: htmlspecialchars и addslashes. @gattaka, би ли направил урок за „PHP: Prepare Statement”, че ми е интересно това как се използва?

  26. Koko

    Цитат ‘Направете отпечатък на браузъра
    $fingerprint=md5($_SERVER[‘HTTP_USER_AGENT’]);“

    И какво правим сетне с$fingerprint? Присвояваме значението й на променлива от масив $_SESSION ли? И при всеки преход по сайта само за логнати я сравняваме. Така ли?

  27. gatakka Автор

    да, това е доста елементарна техника, но за много неща ще помогне.
    Елементарна е, понеже агента лесно може да се „фалшифицира“
    Принципно се прави отпечатък на повече параметри на браузъра, но колкото повече слагаш, толкова по-голяма е вероятността да получеш грешка.

  28. Koko

    Аха, просто сверих мислите си с твоите, като изцяло те разбирам/разбрах.

    Тук дължа едно откровение. Свалих иконата от стената на Спасителя и сложих твоя лик. Канонизирам те за PHP светец и мой просветител.

    Ще бъда задължен, ако продължиш серията за безопасността. Например, двамата качваме сайт в superduper.com. Разбирам, че имаш файл с права 0777 и хей така за мой кеф ти го изтривам с file_put_content() или нещо от сорта от моя сайт. Физически сме на една машина, всеки в своя директория, обаче PHP ти попелява споменати файл. И,и, и .. чета други автори и те казват, че профито се познава по владеене на ООП и XML. Тук имаш серия уроци, благодарности за тях, но някак си премълчаваш за XML.

    Аз съм Ubuntu (благодарение на теб), но в твоя коментар виждам икона на Windows. Днес не е 1 април, нали?

  29. gatakka Автор

    аз си ползвам win, когато има смисъл да се ползва. Сега се занимавам с нещо, което няма как да стане под линукс (за сега)

  30. някой

    А аз да попитам относно филтрирането на данни за логин система и регистрация вместо да използвам addslashes, htmlspecialchars или тем подобни не е ли по-лесно да си създам регулярен израз, с който да контролирам валидноста примерно на потребителя и паролата и всяко едно „отклонение от правилния път“ да не бъде допускано до базата данни и съответно да съм предпазен от всякви атаки опитващи се да проникнат от тези полета ?

  31. Светлин

    Може ли да даеш пример за своя библиотека за филтриране, понеже не ми стана много ясно

  32. diti

    Един въпрос: Можем ли с JavaScript да достъпим друга бисквитка от диска на потребителя, освен тази която е била създадена от сайта чрез който идва XSS атаката?

  33. diti

    Един въпрос:
    Когато имаме JS който взема стойността на дадена бисквитка (не говоря за XSS атака), тази стойност праща ли се по мрежата до сървара или остава единствено в браузъра на клиента?

  34. ウォーロック

    Всички бисквити от сайта се пращат при всяко зареждане на сайта.

  35. diti

    Когато имаме инсталирано SSL, бисквитките криптират ли се когато се пращат по мрежата?

  36. ウォーロック

    Да… Пусни един tcpflow на port 80 и ще видиш, как се движат бисквитите в некриптиран вариант.
    веднага след
    Accept-Encoding:
    Cookie:

    ––
    tcpflow -i eth0 -C port 80
    или sudo tcpflow -i eth0 -C port 80

    ако си с wlan wlan0 вместо eth0 или там каквото ти се пада лан картата…

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *