Полное руководство по XSS

Руководство по атакам с использованием межсайтовых сценариев. Как они работают? Как их предотвратить?

Что такое XSS, или межсайтовый скриптинг?

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

По сути, злоумышленник (злоумышленник) может тем или иным образом внедрить JavaScript на наш сайт, воспользовавшись уязвимостью, которую мы оставили в нашем коде.

Используя эту уязвимость, они могут украсть информацию пользователя.

В зависимости от того, как используется уязвимость XSS, мы можем выделить 3 основных типа XSS-атак:

  • постоянный XSS
  • отраженный XSS
  • XSS на основе DOM

Чем опасен XSS?

Представьте, что у вас есть веб-сайт. Злоумышленник может каким-то образом внедрить код JavaScript, обслуживаемый вашим веб-сайтом, и выполнить его - без вашей воли и без вашего ведома - браузерами вашего пользователя.

Это очень опасно.

Из-за вашей халатности при устранении уязвимости XSS ваш сайт может быть использован в качестве вектора атаки, а информация ваших пользователей находится под угрозой.

В частности, когда кто-либо может внедрить JavaScript на страницу, он может иметь доступ к файлам cookie пользователя, связанным с веб-сайтом, и читать любую информацию, содержащуюся в них. И отправьте это на свои серверы. Они могут прослушивать события клавиатуры и получать доступ к любому пользователю, введенному на страницу, и отправлять его на серверы злоумышленника с помощью fetch или XHR. Например, имена пользователей и пароли. Они также могут манипулировать DOM и с этой силой могут совершать много плохих вещей.

XSS - это проблема внешнего интерфейса или внутреннего интерфейса?

И то, и другое. Это проблема архитектуры веб-сайта, которая затрагивает как интерфейс, так и серверную часть.

Пример XSS-атаки

XSS в основном включается, когда вы разрешаете пользователю вводить информацию, которую вы сохраняете (в своем бэкэнде), а затем возвращаете.

Допустим, у вас есть блог, и вы разрешаете пользователям оставлять в нем комментарии. Если вы слепо принимаете любой контент, вводимый пользователем, злоумышленник может попытаться ввести фрагмент кода JavaScript в его самой простой форме, заключенной в<script></script>. Например<script>alert('test')</script>.

Вы можете сохранить этот комментарий в своей базе данных, и при перезагрузке страницы - опять же, если у вас нет никаких мер защиты - все пользователи, загружающие эту страницу, будут запускать этот фрагмент JavaScript.

Я использовал простойalert()вызовите, чтобы сделать пример, но, как указано выше, пользователь может ввести любой сценарий. На этом этапе сайт взломан.

Что такое постоянный XSS?

Постоянный XSS - это один из 3-х видов XSS, которые мы находим в дикой природе. Это тот, который я описал выше в примере сообщения в блоге.

В этом случае код уязвимости хранится в базе данных или в другом источнике, который размещен вами.

Как только кто-то может ввести фрагмент кода JavaScript, он автоматически обслуживается вашим сайтом без каких-либо дополнительных действий.

Что отражается XSS?

Reflected XSS - это способ оперативно использовать уязвимость на вашем сайте, предоставляя конечному пользователю ссылку, внутри которой находится скрипт.

Таким образом злоумышленник предоставляет ссылку, похожую на

yoursite.com/?example=<script>alert('test')</script>

If your site uses the example GET variable to perform something and display it on the page, and you don’t check and sanitize its value, now that script will be executed by the user’s browser.

A typical example is a search form. It might live in the /search URL and you might accept the search term using the GET term variable.

You might display the You searched for <term> string when someone searches for it. Now, if you didn’t sanitize the value, you now have a problem.

Spam/phishing emails are a common medium for this XSS attack. Of course, the bigger and more important the site, the more frequently hackers will try to hack it.

What is DOM-based XSS?

With persistent XSS, the attacking code must be sent to the server, where it can be (and hopefully it is) sanitized. With reflected XSS, the same is true.

DOM-based XSS is a kind of XSS where the malicious code is never sent to the server. It’s common for this to happen by using the fragment part of a URL, or by referencing document.URL/document.location.href. Some examples you find online don’t really work any more because modern browsers automatically escape JS in the address bar for us. They only work if you unescape it, which is kind of scary (don’t do it!).

Here’s a simple working example. Say you have a page listening on http://127.0.0.1:8081/testxss.html. Your client-side JavaScript looks at the test variable passed in the fragment part of the URL:

http://127.0.0.1:8081/testxss.html#test=something

The #test=something value is never send to the server. It’s only local. Persistent/reflected XSS would not work. But say your script accesses that value using:

const pos = document.URL.indexOf("test=") + 5;
const value = document.URL.substring(document.URL.indexOf("test=") + 5, document.URL.length)

and you write it directly into the DOM:

document.write(value)

All is fine, until someone calls the URL like this:

http://127.0.0.1:8081/testxss.html#test=

Now, thanks to the automatic escaping that happens by referencing document.URL nothing should happen in this specific case.

You’d get

%3Cscript%3Ealert('x')%3C/script%3E

printed to the page. The value is escaped, so it’s not interpreted as HTML.

But if for some reason you unescape the document.URL value, you have a problem now, as the JavaScript is run. Any JS can be run on your users browsers.

On older browser, this was a much bigger problem, since they didn’t auto-escape JS put into the address bar.

Are static sites vulnerable to XSS?

Yes! Any kind of site, actually. Because being static does not mean there is no information loaded from other sources. For example you might roll your own form or comments, even without a database.

Or, we might have a search functionality that accepts input from an HTTP GET or POST variable. You are not immune just by not having a database.

How can we prevent XSS?

There are 3 techniques we can use:

  • encoding
  • validation
  • CSP

Encoding is done to escape the data. Doing so, the browser will not interpret the JavaScript because, for example, <script> will be encoded to %3Cscript%3E.

Encoding, as a general rule, should be always done.

Server-side frameworks commonly provide helper functions to provide this functionality to you.

In client-side JavaScript we use a different encoding mechanism depending on the use case.

If you need to add content to an HTML element, the best way is to assign the user-generated input to that element using the textContent property. The browser will do all the escaping for you:

document.querySelector('#myElement').textContent = theUserGeneratedInput

If you need to create an element use document.createTextNode():

const el = document.createTextNode(theUserGeneratedInput)

If you need to add content to an HTML attribute, use the setAttribute() method of the element:

document.querySelector('#myElement').setAttribute('attributeName', theUserGeneratedInput)

If you need to add content to the URL, use the window.encodeURIComponent() function:

window.location.href = window.location.href + '?test=' + window.encodeURIComponent(theUserGeneratedInput)

Validation is usually done when you cannot use escaping to filter the input. A common example is a CMS that lets the user define the content of the page in HTML. You can’t escape that.

You either use a blacklisting or whitelisting strategy for validation. The difference is that with blacklisting you decide which tags you want to disallow. With whitelisting you decide which tags you want to allow. Whitelisting is safer because blacklisting is prone to errors, complex and also not future-proof.

CSP means Content Security Policy. It’s a new standard implemented by browsers to enforce only executing JavaScript code coming from secure and trusted sources, and you can disallow running inline JavaScript in your code. The kind of JavaScript that allowed the above XSS exploits, for example.

CSP is enabled by the Web Server, by adding the Content‑Security‑Policy HTTP Header when serving the page.


More security tutorials: