И е изчерпателен урок за междусайтови скриптове.
Част първа: преглед
Какво е XSS?
Скриптове между сайтове ( Английски Междусайтови скриптове) Това е атака с инжектиране на код, която позволява на нападателя да изпълни злонамерен JavaScript в браузъра на друг потребител.
Нападателят не атакува директно жертвата си. Вместо това той използва уязвимост в уебсайта, който жертвата посещава, и инжектира злонамерен JavaScript код. Браузърът на жертвата показва злонамерен JavaScript като легитимна част от уебсайта, а самият уебсайт действа като пряк съучастник на нападателя.
Инжектиране на злонамерен JavaScript код
Единственият начин нападателят да стартира злонамерен JavaScript в браузъра на жертвата е да го инжектира в една от страниците, които жертвата изтегля от уебсайта. Това е възможно, ако уебсайтът позволява на потребителите да въвеждат данни в неговите страници и нападателят може да вмъкне ред, който ще бъде открит като част от кода в браузъра на жертвата.
Примерът по-долу показва прост скрипт от страна на сървъра, който се използва за показване на последния коментар на сайт:
отпечатай " "
печат "Последен коментар:"
отпечатайте база данни.последнияComment
отпечатай ""
Скриптът приема, че коментарът се състои само от текст. Въпреки това, тъй като директното въвеждане на потребителя е активирано, нападателят може да остави този коментар: "". Всеки потребител, който посети страницата, вече ще получи следния отговор:
Последен коментар:
Когато браузърът на потребителя зареди страницата, той ще изпълни всичко, включително JavaScript, съдържащ се в етикетите ... Това показва, че самото присъствие на нападател, инжектиращ скрипт, е проблем, независимо от това какъв конкретен код на скрипта се изпълнява в действителност.
Част втора: XSS атака
Участници в XSS атака
Преди да опишем подробно как работи една XSS атака, трябва да идентифицираме участниците в XSS атаката. Като цяло има трима участници в XSS атака: уеб сайт, жертва, и крекер.
- уеб сайтсъздава HTML страници за потребители, които ги поискат. В нашите примери той се намира на http: // уебсайт /.
- База данни на уебсайтовее база данни, която съхранява част от данните, въведени от потребителите на страниците на сайта.
- ЖертваРедовен потребител на уебсайт, който иска страници от него чрез своя браузър.
- АтакуващЕ нападател, който възнамерява да започне атака срещу жертвата, като използва XSS уязвимост на сайта.
- Сървър за кракерТова е уеб сървър под контрола на нападател с единствената цел да открадне поверителната информация на жертвата. В нашите примери той се намира на адрес http: // атакуващ /.
Примерен сценарий за атака
Този скрипт ще създаде HTTP заявка към различен URL, който ще пренасочи браузъра на потребителя към сървъра на нападателя. URL адресът включва бисквитките на жертвата като параметър на заявка, когато HTTP заявка пристигне на сървъра на нападателя, нападателят може да извлече тези бисквитки от заявката. След като нападателят получи бисквитките, той може да ги използва, за да се представя за жертва и да започне последваща атака.
Оттук нататък горният HTML код ще бъде извикан злонамерен низили злонамерен скрипт... Важно е да се разбере, че самият низ е злонамерен само ако в крайна сметка се обработва като HTML в браузъра на жертвата, което може да се случи само ако има XSS уязвимост в уебсайта.
Как работи този пример за атака
Диаграмата по-долу показва пример за нападател, извършващ атака:
- Нападателят използва една от формите на уебсайта, за да вмъкне зловреден низ в базата данни на уебсайта.
- Жертвата иска страница от уебсайт.
- Сайтът включва злонамерен низ от базата данни в отговора и го изпраща на жертвата.
- Браузърът на жертвата изпълнява злонамерения скрипт в отговора, изпращайки бисквитката на жертвата до сървъра на нападателя.
Типове XSS
Целта на XSS атака винаги е да се изпълни злонамерен JavaScript скрипт в браузъра на жертвата. Има няколко принципно различни начина за постигане на тази цел. XSS атаките често се категоризират в три типа:
- Съхранен (постоянен) XSSкъдето злонамереният низ произхожда от базата данни на уебсайта.
- Отразен (променлив) XSSкъдето злонамереният низ се създава от заявката на жертвата.
- DOMs XSSкъдето уязвимостта се появява в кода от страна на клиента, а не в кода от страна на сървъра.
Предишният пример показва съхранена XSS атака. Сега ще опишем два други типа XSS атаки: Reflected XSS и DOM XSS.
Отразен XSS
При отразена XSS атака, злонамереният низ е част от заявката на жертвата към уебсайта. Сайтът приема и вмъква този злонамерен низ в отговора, който изпраща обратно на потребителя. Диаграмата по-долу илюстрира този сценарий:
- Жертвата е измамена от нападателя да изпрати URL заявка до уебсайта.
- Сайтът включва злонамерен низ от URL адреса на заявката в отговора на жертвата.
- Браузърът на жертвата изпълнява злонамерения скрипт, съдържащ се в отговора, изпращайки бисквитката на жертвата до сървъра на нападателя.
Как да се защитим успешно от XSS атаки?
Отразената XSS атака може да изглежда безобидна, тъй като изисква от жертвата да изпрати заявка от тяхно име, която съдържа злонамерен низ. Тъй като никой няма да се самонапада доброволно, изглежда, че няма начин действително да се извърши атаката.
Както се оказва, има поне два често срещани начина да накарате жертвата да започне отразена XSS атака срещу себе си:
- Ако потребителят е конкретно лице, нападателят може да изпрати злонамерен URL адрес на жертвата (например чрез имейл или месинджър) и да ги подмами да отвори връзката, за да посети уебсайта.
- Ако целта е голяма група потребители, нападателят може да публикува връзка към злонамерен URL (например на собствен уебсайт или социална мрежа) и да изчака посетителите да щракнат върху връзката.
И двата метода са сходни и и двата могат да бъдат по-успешни чрез използване на услуги за съкращаване на URL адреси за маскиране на злонамерения низ от потребители, които могат да го идентифицират.
XSS в DOM
DOM XSS е вариант както на съхранена, така и на отразена XSS атака. При тази XSS атака злонамереният низ не се обработва от браузъра на жертвата, докато не се изпълни действителният JavaScript на уебсайта. Диаграмата по-долу илюстрира този сценарий за отразена XSS атака:
- Нападателят създава URL, съдържащ злонамерен низ и го изпраща на жертвата.
- Жертвата е измамена от нападателя да изпрати URL заявка до уебсайта.
- Сайтът приема заявката, но не включва злонамерения низ в отговора.
- Браузърът на жертвата изпълнява легитимния скрипт, съдържащ се в отговора, в резултат на което злонамереният скрипт ще бъде вмъкнат в страницата.
- Браузърът на жертвата изпълнява злонамерения скрипт, вмъкнат в страницата, изпращайки бисквитката на жертвата до сървъра на нападателя.
Каква е разликата между XSS в DOM?
В предишните примери за съхранени и отразени XSS атаки, сървърът инжектира злонамерен скрипт в страницата, който след това се препраща в отговор на жертвата. Когато браузърът на жертвата получи отговор, той приема, че злонамереният скрипт е част от легитимното съдържание на страницата и автоматично го изпълнява по време на зареждане на страницата, както всеки друг скрипт.
В примера за DOM XSS атака злонамереният скрипт не се вмъква като част от страницата; единственият скрипт, който се изпълнява автоматично по време на зареждане на страницата, е легитимната част от страницата. Проблемът е, че този легитимен скрипт директно използва въвеждането на потребителя, за да добави HTML към страницата. Тъй като злонамереният низ се вмъква в страницата с помощта на innerHTML, той се анализира като HTML, което кара злонамерения скрипт да се изпълни.
Това разграничение е малко, но много важно:
- В традиционния XSS злонамереният JavaScript се изпълнява при зареждане на страницата като част от HTML, изпратен от сървъра.
- В случай на XSS в DOM, злонамереният JavaScript се изпълнява след като страницата се зареди, което кара тази легитимна JavaScript страница да има достъп до въвеждането на потребителя (съдържащ злонамерения низ) по небезопасен начин.
Как работи XSS в DOM?
В предишния пример JavaScript не се изисква; сървърът може сам да генерира целия HTML. Ако кодът от страна на сървъра не съдържаше уязвимости, уебсайтът нямаше да бъде уязвим към XSS уязвимостта.
Въпреки това, тъй като уеб приложенията стават по-напреднали, все повече и повече HTML страници се генерират с помощта на JavaScript от страна на клиента, а не на сървъра. По всяко време съдържанието трябва да се промени, без да се опреснява цялата страница, това е възможно с помощта на JavaScript. По-специално, това е случаят, когато страницата се обновява след AJAX заявка.
Това означава, че XSS уязвимостите могат да присъстват не само от страна на сървъра на кода на вашия сайт, но и от страна на JavaScript на клиента на вашия сайт. Следователно, дори и с напълно безопасен код от страна на сървъра, клиентският код все още може да не е безопасен за включването на въвеждане от потребителя, когато DOM се актуализира след зареждане на страницата. Ако това се случи, тогава кодът от страна на клиента ще позволи XSS атака не по вина на кода от страна на сървъра.
Базираният на DOM XSS може да не се вижда от сървъра
Има специален случай на DOM XSS атака, при която злонамереният низ никога не се изпраща до сървъра на уебсайта: това се случва, когато злонамереният низ се съдържа в фрагмента на URL идентификатора (всичко след знака #). Браузърите не изпращат тази част от URL адреса на сървъра, така че уебсайтът не може да бъде достъпен чрез код от страна на сървъра. Кодът от страна на клиента обаче има достъп до него и по този начин е възможно да се проведе XSS атака чрез несигурна обработка.
Този случай не се ограничава до идентификатора на фрагмента. Има и други потребителски данни, които са невидими за сървъра, като например нови HTML5 функции като LocalStorage и IndexedDB.
част трета:
Предотвратяване на XSS
Техники за превенция на XSS
Като напомняне, XSS е атака с инжектиране на код: въвеждането на потребителя погрешно се интерпретира като злонамерен код. Необходима е безопасна работа с въвеждането, за да се предотврати този тип инжектиране на код. За уеб разработчик има два фундаментално различни начина за извършване на защитена обработка на входа:
- Кодиранее начин, който позволява на потребителя да въвежда данни само като данни и не позволява на браузъра да се обработва като код.
- Валидиранее начин за филтриране на въведеното от потребителя, така че браузърът да го интерпретира като код без злонамерени команди.
Въпреки че това са коренно различни методи за превенция на XSS, те имат няколко общи неща, които е важно да разберете, когато използвате някой от тях:
Контекст Безопасната обработка на входа трябва да се извършва по различен начин в зависимост от това къде се използва въвеждането на потребителя на страницата. входящ/изходящ Сигурна обработка на входа може да се извърши или когато вашият сайт получи вход (входящ трафик), или точно преди сайтът да вмъкне въведени от потребителя в съдържанието на страницата (изходящ). Клиент/сървър Защитената обработка на входа може да се извърши или от страна на клиента, или от страна на сървъра, всяка от които е необходима при различни обстоятелства.
Преди да обясним подробно как работи кодирането и валидирането, ние описваме всяка една от тези точки.
Обработка на въвеждане от потребителя в контексти
Има много контексти на уеб страница, където може да се приложи въвеждането на потребителя. За всеки от тях трябва да се спазват специални правила, така че въвеждането на потребителя да не може да „избяга“ от неговия контекст и да не може да бъде интерпретирано като злонамерен код. Следните са най-често срещаните контексти:
Колко важни са контекстите?
Във всички описани контексти може да възникне XSS уязвимост, ако въвеждането на потребителя е било вмъкнато преди първото кодиране или валидиране. Нападателят може да инжектира злонамерен код, просто като вмъкне затварящ разделител за този контекст, последван от злонамерен код.
Например, ако в даден момент уебсайт включва въвеждане от потребителя директно в HTML атрибут, нападателят може да инжектира злонамерен скрипт, като започне въвеждането им с кавички, както е показано по-долу:
Това можеше да бъде предотвратено чрез просто премахване на всички кавички във въвеждането на потребителя и би било добре, но само в този контекст. Ако входът е бил вмъкнат в различен контекст, затварящият разделител ще бъде различен и инжектирането ще бъде възможно. Поради тази причина безопасното боравене с въвеждане трябва винаги да бъде адаптирано към контекста, в който ще бъде вмъкнат потребителският вход.
Обработка на входящ/изходящ потребителски вход
Инстинктивно може да изглежда, че XSS може да бъде предотвратен чрез кодиране или валидиране на всички потребителски данни, веднага щом нашият сайт го получи. По този начин всички злонамерени низове вече ще бъдат неутрализирани, когато са включени в страницата, а скриптовете за генериране на HTML няма да се притесняват за безопасното обработване на въведеното от потребителя.
Проблемът е, че, както беше описано по-рано, въвеждането на потребителя може да бъде вмъкнато в множество контексти на страницата. И няма лесен начин да се определи кога въведеното от потребителя влиза в контекста - как в крайна сметка ще бъде вмъкнато, и един и същ потребителски въведен често трябва да бъде вмъкнат в различни контексти. Като разчитаме на обработката на входящия вход, за да предотвратим XSS, ние създаваме много крехко решение, което ще бъде податливо на грешки. (Наследените PHP магически кавички са пример за такова решение.)
Вместо това, обработката на изходящ вход трябва да бъде вашата основна линия на защита срещу XSS, тъй като може да вземе предвид специфичния контекст на това, което потребителски вход ще бъде вмъкнат. До известна степен входящата валидация може да се използва за добавяне на вторичен слой на защита, но повече за това по-късно.
Където е възможно безопасно да се обработва въвеждането на потребителя
В повечето съвременни уеб приложения, въвеждането на потребителя се обработва както в кода от страна на сървъра, така и от кода от страна на клиента. За да се защити срещу всички видове XSS, сигурната обработка на входа трябва да се извършва както в код от страна на сървъра, така и от клиентски код.
- За да се защити от традиционния XSS, сигурната обработка на въвеждане трябва да се извършва в код от страна на сървъра. Това се прави с помощта на език, поддържан от сървъра.
- За да се защити срещу XSS атака в DOM, където сървърът никога не получава злонамерен низ (като атаката с фрагмент на идентификатор, описана по-рано), трябва да се извърши защитена обработка на входа в код от страна на клиента. Това се прави с помощта на JavaScript.
След като обяснихме защо контекстът има значение, защо разликата между входящата и изходящата обработка на входа е важна и защо безопасната обработка на входа трябва да се извършва както от страна на клиента, така и от страна на сървъра, можем да продължим да обясняваме как двата типа защитени обработката на входа (кодиране и валидиране) реално се извършват.
Кодиране
Кодирането е изход от ситуация, при която е необходимо браузърът да интерпретира въведеното от потребителя само като данни, а не като код. Най-популярният тип кодиране в уеб разработката е HTML маскирането, което преобразува знаци като напр < и > v < и > съответно.
Следният псевдокод е пример за това как въвеждането на потребителя (потребителското въвеждане) може да бъде кодирано с помощта на HTML маскиране и след това вмъкнато в страница с помощта на скрипт от страна на сървъра:
отпечатай " "
печат "Последен коментар:"
печат encodeHtml (userInput)
отпечатай ""
Ако потребителят въведе следния ред, полученият HTML ще изглежда така:
Последен коментар:
Тъй като всички знаци със специално значение са маскирани, браузърът няма да анализира никоя част от въведеното от потребителя като HTML.
Кодиране от страна на клиента и сървъра
Когато се изпълнява кодиране от страна на клиента, винаги се използва JavaScript, който има вградени функции, които кодират данни за различни контексти.
Когато правите кодиране във вашия сървърен код, вие разчитате на функциите, налични във вашия език или рамка. Поради големия брой налични езици и рамки, този урок няма да обхване подробностите за кодирането на конкретен сървър или език на рамката. Въпреки това, функциите за кодиране на JavaScript от страна на клиента се използват и при писане на код от страна на сървъра.
Кодиране от страна на клиента
Когато кодирате потребителски вход от страна на клиента с JavaScript, има няколко вградени метода и свойства, които автоматично кодират всички данни в контекстно чувствителен стил:
Последният контекст, споменат по-горе (стойности в JavaScript) не е включен в този списък, тъй като JavaScript не предоставя вграден начин за кодиране на данни, които биха били включени в изходния код на JavaScript.
Ограничения за кодиране
Дори при кодиране е възможно да се използват злонамерени низове в някои контексти. Отличен пример за това е, когато въвеждането на потребителя се използва за предоставяне на URL, като в примера по-долу:
document.querySelector ("a"). href = userInput
Въпреки че посочената стойност в свойството на елемента href автоматично го кодира, така че да се превърне в нищо повече от стойността на атрибута, това само по себе си не пречи на нападателя да вмъкне URL, който започва с "javascript:". Когато се щракне върху връзката, независимо от конструкцията, вграденият JavaScript в URL адреса ще бъде изпълнен.
Кодирането също не е ефективно решение, когато искате потребителите да могат да използват някои от HTML кодовете на страницата. Пример би била страница с потребителски профил, където потребителят може да използва персонализиран HTML. Ако този обикновен HTML е кодиран, страницата на профила може да се състои само от обикновен текст.
В ситуации като тази кодирането трябва да бъде допълнено от валидиране, с което ще се запознаем по-нататък.
Валидиране
Валидирането е актът на филтриране на въведеното от потребителя, така че всички злонамерени части от него да бъдат премахнати, без да се налага премахване на целия код в него. Един от най-използваните видове валидиране в уеб разработката ви позволява да използвате някои HTML елементи (напр. и ), но забрана на други (напр.