Защищаем сайт от атак на примере ХабраХабра



    Рано утром Хабр «выкатил» своё новое обновление, и я с чистой совестью достаю эту статью из черновиков.
    Вчера у меня случился epic fail и этот топик частично, включая строчку об апдейте выше, попал в паблик на пару секунд. За эти секунды топик успело плюсануть несколько человек.
    Ещё раз, теперь публично, прошу прощения у администрации!
    Совет остальным — НИКОГДА не храните в черновиках информацию вроде этой.


    В последнее время в сети Интернет можно найти очень много пособий для «Начинающих хакеров», в которых подробно описываются все основные методы взлома сайтов. Думаете, веб-разработчики стали от этого умнее и предприняли все возможные методы для защиты? Я так не думаю.

    В настоящей статье я хочу ещё раз поведать разработчикам о том, как ломают сайты, а чтобы вам не было скучно, я попутно буду ломать Хабр и подробно описывать, как я это делал. Мы рассмотрим такие интересные штучки, как «Активная XSS в профиле», «Бесконечное обнуление кармы», «Публикация топиков со значком 'Из песочницы'», «CSRF через Flash и дыру в Internet Explorer 6» и многое другое.

    Все уязвимости уже исправлены. Ну или почти все. Поэтому, если вы найдёте очередную дыру, то пишите на support@habrahabr.ru — миф о том, что эту почту никто не читает всего лишь миф.

    Вместо вступления


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

    Во-первых, кто такие взломщики и что им нужно (давайте не будем называть «взломщиков» словом «хакеры», потому что это неправильно и хакеры никого не ломают). Не хочу никого обидеть, но в большинстве случаев это подростки 15-17 лет, главная цель которых не сколько получить деньги, сколько просто взломать систему и получить чувство некого самоудовлетворения.

    Часто разработчик думает, что ломать его сайт никому и в голову не придёт, поскольку там нет ничего интересного. Но взломщикам только что описанного типа это и не нужно, поэтому они легко находят уязвимость и делают дефейс (как правило, размещение своего сообщения вместо главной страницы, Чёрный Властелин уже делал так пару раз с ХабраХабром :-). Иногда же они даже не могут найти и простую уязвимость, поэтому используют готовые эксплоиты (таких людей ещё называют «Скрипт-кидди»). Таким образом, я уже могу дать вам несколько советов:
    • Не ленитесь фильтровать входные данные [об этом пойдёт речь дальше], даже если ваш сайт «никому не нужен».
    • Ставить самые свежие версии ПО иногда опасно, но и на старых сидеть не стоит, потому что вас сможет взломать любой человек, умеющий пользоваться поиском и скачивать специальные программы «для взлома сайтов».

    Теперь давайте представим, что наш «персонаж» вырос и начал искать работу. Чем он будет скорее всего заниматься? Правильно, взломом за деньги.

    Такой тип уже не представляет особой опасности для сайтов, где в самом деле нет ничего интересного. Работает же он теперь, в основном, «по заказу», часто в небольших группах.

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

    Кстати, подобные «Эксперты» — это, как правило, всё тот же взломщик, который ушёл на пенсию и теперь зарабатывает на жизнь вполне мирным способом.

    XSS или «Межсайтовый скриптинг»


    Очень часто используемый тип уязвимостей. Для его использования взломщику нужно лишь иметь базовые знания HTML и JavaScript, вследствие чего применяется всеми типами описанных ранее «персонажей». Хочется отметить, что в данном случае сервер взламывать никто и не собирается, а атака, по сути, направлена на пользователей уязвимого сайта.

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

    XSS делится на две основные группы: «Активная XSS», это которая лежит где-то на сайте и ждёт свою жертву, а также «Пассивная XSS», которую взломщик посылает жертве, используя социальную инженерию.

    Посмотрим, чем нас может «обрадовать» Хабр. Британские учёные установили, что 95% взломщиков называют себя

    " onmouseover="alert('XSS')" style="

    Идём в настройки профиля и вводим эту фразу в поле «Настоящее имя». Теперь жмём «Сохранить» и обновляем страницу. Находим мышку и пробуем ей повозить возле почему-то пустого поля — появилось окошко? Поздравляю, мы нашли уязвимость!



    Подобные уязвимости часто вылезают, когда система не фильтрует или фильтрует частично приходящую от пользователя информацию. Если вы пишете на PHP, то в нём есть очень хорошая функция htmlspecialchars, которая позволяет решить проблему почти полностью.
    Смысл такой, что не нужно давать пользователю возможность использовать все теги, нужно также тщательно проверять, не вставил ли он что-то в разрешённый тег и не вышел ли он за пределы поля, как в примере выше.

    Найти пример пассивной XSS на Хабре мне не удалось, потому опишу её суть на словах.

    Самое частое место, где их можно найти — это поиск. Попробуйте «поискать» у себя на сайте что-то вроде

    <script>alert(1)</script>

    или

    "> <script>alert(1)</script> <!--

    Если выскочило окошко с цифрой «1», то ваш сайт подвержен таким атакам. Теперь взломщику достаточно послать вам особую ссылку, перейдя по которой вы отдадите ему полный доступ к своему аккаунту. Смысл тот же, что и в «Активной XSS», только теперь взломщику нужно «скормить» вам ссылку!

    Проверяем другую принимаемую информацию


    Фильтровать нужно не только HTML-теги и кавычки, но также и другую полученную от пользователя информацию.Особенно важно следить за логикой и никогда не верить пользователю! За примером далеко ходить не надо — заветная мечта любого тролля на Хабре, «Бесконечно обнуляемая карма», была вполне реализуема до сегодняшнего дня :-)

    Если вы уже обнуляли карму и хотите сделать это ещё раз, то нужно с помощью «средств разработчика» вашем браузере просто сделать видимой скрытую форму и нажать «Обнулить»!

    Для примера я создал «виртуальчика», слил ему карму, обнулил, опять слил, опять обнулил и так далее…





    Справедливости ради стоит заметить, что мой милый виртуальчик был за это переведён в режим «Read-Only». Хорошо, что сам аккаунта не лишился.

    Приведу ещё очень интересный пример. Когда публикуем новую статью или редактируем старую, то замена поля «topic_type» на «sandbox» позволяет получить плашку «Из песочницы».



    Зачем это вам нужно? Ну не знаю, допустим, такие топики чаще плюсуют…

    Что я могу посоветовать в данном случае? Ну конечно же, контролировать то, что вам шлют пользователи! Если вам шлют запрос на обнуление кармы, то проверьте, не обнулял ли пользователь её раньше, а если пользователь пытается опубликовать топик «из песочницы», то проверьте, в самом ли деле он оттуда.

    SQL-инъекции


    Такие уязвимости очень часто встречаются у начинающих веб-разработчиков. Найти такую на Хабре я не смог, но один хабропользователь утверждал мне в личке, что она есть. Я повторил его эксперимент, но ничего не вышло — наверное, закрыли.

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

    CSRF-уязвимости


    Если Бубмбурум начинал своё покорение Хабра с супер-флешки, то я за ночь попал в ТОП5 пользователей благодаря найденной мною в прошлом году CSRF-уязвимости.

    Прочитать мой топик можно тут. Сейчас же я пишу это потому, что недавно нашел ещё одну такую же и она могла приглашать на Хабр моих виртуальчиков с помощью пользователей, которые пользовались Internet Explorer 6. Стоит заметить, что таковых оказалось не много.

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

    Ссылки по теме


    Википедия — XSS, SQL-инъекция, CSRF.

    Еще интересная на мой вгляд статья, в которой описаны все основные уязвимости вместе.
    Поделиться публикацией

    Комментарии 82

      +5
      Мне кажется что на многих сайтах имеет место уязвимость, связанная с авторизацией через социальные сети — вероятно многие не фильтруют, например, имена пользователей из соц сетей при их авторизации на различных порталах, которые потом без обработки вставляются в код страницы.
        +1
        Да, вы правы. Тут один пользователь — chelovekdimka, кажется — подробно описал это и даже снял видео
          +2
          Благодарю, как-то мимо меня прошла статья.
          +1
          php сайтах, да. в рельсах xss вообще не существует сейчас )
            +1
            Из-за рельсов вроде недавно Гитхаб взломали, не? Не с помощью xss конечно, но стоит помнить, что сам не без греха :)
              +17
              лол взломал то я и ответственно заявляю что rails сам ый секьюрный фреймворк. На эту тему как раз готовлю «разоблачительный» пост а то после бурления говна все всё не так поняли
                +2
                О сорри, не посмотрел на никнейм :)
          +2
          Отличная статья!
            +3
            «Баги» просто удачные, а вот статья не очень :-(
            +9
            Так вот из за кого я всю ночь не мог почитать Хабр :=)
              +7
              Апдейт Хабра был плановый, но вчера я очень испугался, когда статья почему-то опубликовалась
                +2
                Да, что-то долго закрывали… Пришлось даже спать лечь((
                  +1
                  Ну апдейт большой очень был — там же не только уязвимости закрывали, но еще и топики пересортировывали.
                    +2
                    А сколько всего конвертировали…
                      +5
                      В итоге на почту приходят неверные ссылки на топики :)
              • НЛО прилетело и опубликовало эту надпись здесь
                  +2
                  Сейчас все закрыли, но отвечу честно — воспользовался одним из багов, чтобы сделать себе возможность обнулить карму когда-нибудь… На всякий случай :-)
                    +2
                    Т.е. я могу обнулить свою карму еще один раз, хотя давно уже делал это :-)
                      +4
                      сделал запрос на «Я передумал»?
                        +1
                        да
                  +5
                  Не думал, что на хабре будут такие детские уязвимости
                    +3
                    Потому и были, что никому даже в голову не приходило их искать на таком ресурсе!
                      +3
                      Я предполагаю, что нет ни единого ресурса, в котором не было бы хоть одной уязвимости. Если же вы автор такого — напишите, пожалуйста, статью, станете в один ряд с автором этого топика.
                        +2
                        Конечно, уязвимости есть везде. Но тотальная фильтрация всего ввода и ВСЕГДА контроль клиентских действий на стороне сервера — это то что я делаю на всех сайтах, без исключения.
                        Первое и главное правило: код на стороне клиента — код в руках врага.
                          +1
                          Ах, да, еще все формы укрываю CSRF-кодами
                          0
                          выглядят по детски. а встерчаются везде — тот же гитхаб
                          +10
                          Самое интересное что я находил это, как я его назвал — Вор инвайтов)
                          Можно было подбирать id недавно выданных инвайтов и отменять их у себя в профиле. Таким образом инвайт возвращался мне, а не тому, кто его отправил :)
                            +5
                            Интересно, но какой логикой руководствовался программист, когда делал это? Ведь у каждого инвайта должен быть хозяин — ему то и надо возращать инвайт!
                            0
                            Трололо негодует, вы сломали их мечту :)
                              +8
                              >Для защиты нужно фильтровать кавычки и прочие спецсимволы, которые могут нарушить логику вашего запроса. Также, когда у вас есть число, обязательно явно приводите его к числу.

                              Никогда не стоит ставить костыли, когда есть принципиальное решение проблемы.
                              Принципиальное решение — НИКОГДА не использовать конкатенацию запроса и пользовательской переменной. Только prepared statements является решением проблемы, которое невозможно сломать by design.
                                +1
                                использовать нормальную ORM
                                  0
                                  ну, там как раз это и используется.
                                    0
                                    Я лично еще два года назад несмотря на использование JPA (Java) использовал конкатенацию и не понимал, почему же это плохо. Боже, каким же дубиной я был.
                                    +3
                                    Prepared statements доступно и без ORM. ORM — это уже больше к логической организации данных из БД в памяти приложения, она уместно далеко не всегда.
                                    +1
                                    Не любая база данных поддерживает prepared statements.
                                    Кроме того, бывают (редко, правда) еще и запросы вида
                                    SELECT * FROM Table1 WHERE Id in (<3000 записей>)
                                      0
                                      три тысячи записей в вебе?
                                      у меня есть ощущение, что вы что-то делаете не так.
                                        0
                                        Это — кустарная репликация базы, запускаемая по таймеру. 3000 записей бывает только при запуске сервера (т.е. раз в неделю/год в зависимости от воли уборщицы), потому и не оптимизируем.
                                          0
                                          но, я правильно понимаю, что пользовательского ввода там нет?
                                            0
                                            Да, пользовательского ввода там нет.

                                            Но когда программист пишет «where id=» + $id, он порой тоже думает, что $id никак не является пользовательским вводом…

                                            Это я к тому, что ваше НИКОГДА является слишком категоричным.
                                              0
                                              Да, возможно, я был слишком категоричен, но с другой стороны, такую конкатенацию в общем случае использовать нельзя. Ваш случай — скорее исключение и довольно-таки слабо связан с уязвимостями веба (По крайней мере, я не вижу способа все сломать как-либо)
                                                0
                                                Туда можно sleep() запихнуть.
                                                То есть запрос вида
                                                select data1, data2, data3 from tbl where id=sleep(100) limit 10
                                                подвешивает базу данных на тысячу секунд. Используя конструкцию if, взломщик может вытащить что-нибудь из базы данных, используя отклик «уснуло — не уснуло».
                                                Трудоёмко, но чего только не сделают, чтобы удовлетворить любопытство.
                                                  0
                                                  Извините, но в данном конкретном приведенном mayorovp случае такое сделать нельзя. Автоинкрементный id (Насколько я понял) со значением sleep(1000) меня бы насторожил.
                                                    0
                                                    И выборка трех тысяч записей не самое быстрое удовольствие, как и последующая их передача (У нас же репликация написана)
                                        0
                                        для таких запросов можно сделать так (php): stackoverflow.com/a/920523
                                          0
                                          Не знаю, как ваша любимая СУБД, а SQL Server Express имеет ограничение на 2048 параметров запроса.
                                            0
                                            Аааа, Вы об этом… Я Вас неправильно понял.
                                      0
                                      По себе помню, что новичка абстрактное описание раздражает. Может стоит все-таки более предметно?

                                      При пожелании фильтрации xss привести пример фильтра, от SQL инъекций подсказать использовать placeholders, с перечислением расширений, например, PDO для php. Или хотя бы ссылки на хорошие статьи по теме.
                                        0
                                        Спасибо, сейчас занят, но ближе к вечеру допишу
                                          0
                                          Приводить пример XSS-фильтра — опасная практика, в том же codeigniter он состоит более чем из сотни строк, к тому же, я не уверен на 100%, что он защитит сайт от более-менее серьезного взломщика.
                                            0
                                            Согласен, ссылок на мат.часть не хватает.
                                            Можно, конечно, поискать в сети, но ведь приятнее, когда всё в одном месте (:
                                              0
                                              Хорошо, сейчас добавлю
                                                +1
                                                XSS — не единственная беда и в одном месте все возможные баги не соберешь. Вот взять те же рельсы. Казалось бы магия везде, ОРМ, фильтрации по-умолчанию и т.д. Но недавно увидел у знакомого кусок кода вроде этого:
                                                Comment.create! { user_id: current_user.id }.merge params[:comment]

                                                Вроде бы все хорошо, но если вдруг кто-то передаст параметр user_id в форму (мало ли, код увидит или еще что), то можно отправлять комментарии от имени другого пользователя. А нужно было всего лишь сделать мерж в другую сторону:
                                                Comment.create! params[:comment].merge({ user_id: current_user.id })

                                                Вывод — очень много зависит от логики.
                                                  0
                                                  старый добрый mass assignment. кор уже всерьез за него взялся
                                                    0
                                                    Я ж написал, это не мой код :) а про attr_protected знаем.
                                              +1
                                              С обнулением кармы сильно.
                                                +1
                                                хех twitter.com/#!/homakov/status/177839734754770944
                                                у вм так же работает верификация смс
                                                +3
                                                > Думаете, веб-разработчики стали от этого умнее и предприняли все возможные методы для защиты? Я так не думаю.
                                                Вот для того, чтобы веб-разработчики стали умнее, они должны пройти через путь тех самых скрипт-киддисов :) Сначала киддисы ломают сайты по чужим рецептам, потом им хочется начать писать свои, а для этого нужно очень много всего изучить. Начинают изучать языки программирования, протоколы, какие-то вспомогательные технологии и т.д. Причем нужно изучать, вдаваясь во все подробности, иначе баги не так-то просто будет откопать.
                                                И когда уже есть багаж знаний, они понимают, что лучше создавать, чем ломать :) Но такие люди уже будут создавать сайты, предугадывая большинство вариантов взлома.
                                                  +1
                                                  Хочу спросить: как при вводе «alert('Bug')» получается диалог с кнопочкой ОК и надписью «XSS»? Или это так специально сделано, чтобы юные пионеры ждали именно «XSS»? :)
                                                    0
                                                    Когда делал скриншот, то ввел «XSS», но в топике написал «Bug».
                                                      0
                                                      По-моему, это лишает наглядности. Я могу подключить Cisco, а сфоткаю Juniper.
                                                        0
                                                        Исправлю текст в топике — устроит?
                                                          0
                                                          Я больше писал для информации для тех, кто, возможно, столкнется и не поймет, а не как претензию.
                                                          Если вам не сложно, исправьте пожалуйста.
                                                            0
                                                            Давно же вроде уже исправил…
                                                    +2
                                                    сегодня утром еще немножко потестил гитхаб
                                                    1 homakov.github.com/ghfollow.html#youfollowme
                                                    2. github.com/youfollowme/followers
                                                    уже пофикшено.
                                                      0
                                                      Нда уж — Хабр хотя бы реферер проверял
                                                        0
                                                        У меня до сих пор работает — вы добавились ко мне :-)
                                                          0
                                                          Упс — не работает, я не заметил, что вы ссылку на чей-то профиль дали :-)
                                                        0
                                                        Еще в качестве совета можно предложить использование HttpOnly для всех cookie (в этом случае их нельзя будет получить через XSS).
                                                          0
                                                          Cамый безопасный способ, который я видел — у vk.
                                                          Собственно, вся авторизация — на login.vk.com, там хранится мастер-куки, назову его так.
                                                          При авторизации на любом сайте или сервисе идет редирект на login.vk.com, где проверяется мастер-куки, и делается редирект на страницу, на которой ставится куки-токен для нужного сайта.
                                                          Мастер-куки имеет долгий срок жизни, не зависит от ip или чего-либо еще. Куки-токен при смене IP или чего-либо еще сбрасывается. То есть кража куки-токена через XSS ничего не даст, при этом доступа к мастер-куки нет. Единственное, что может быть — xss на login.vk.com, но ИМХО гораздо проще защитить одну страницу, чем весь сайт.
                                                            0
                                                            Сервисы google работают аналогично
                                                          0
                                                          В общем, советы те же, только теперь вам стоит немного потратиться на человека, который за деньги проверит ваш сайт на наличие уязвимостей и сообщит вам результаты.
                                                          Хочу предложить Вам в помощь сайт для проверки защиты вашего ресурса — по сути фриланс биржа для людей обладающих умением взлома. От вас требуется разместить проект, указать тип уязвимости и бюджет. Дальше просто — ждать предложения выполнить проект от экспертов взлома. Проект молодой и ждёт своих клиентов!
                                                          hackmysite.ru/
                                                            0
                                                            Ну а вот от такого рода «атак» защитится, как мне кажется, не представляется возможным.
                                                              0
                                                              Если претендуете на легальность, то добавьте простенький функционал подтверждения прав на сайт (как у гугл или яндекс) — например, чтобы заказчик разместил на корне текстовый файл с вашим кодом.
                                                                0
                                                                Если бы вы создали проект, то убедилиь что данная функция реализована в первую очередь, называется она верификация или подтверждения прав на сайт, дабы защитить сайты от заказа со стороны конкурентов. Спасибо за интерес к нашему сервису!
                                                                  0
                                                                  И выражаю огромную благодарность хабре за более 200 человек целевой аудитории!
                                                              0
                                                              Спасибо, буду теперь активнее искать на хабре уязвимости. Все подробности буду отсылать по указанной почте)
                                                                0
                                                                Не обязательно для XSS использовать JavaScript. Например, если на сайте разрешена публикация картинки, то взломщик в качестве src может указать что-нибудь вроде «mysite.com/logout» или повесить менее безобидный action. При закрузке страницы браузер попытается загрузить данный image и вызовет action внутри сессии пользователя.

                                                                Хорошего способа от этого взлома нет: либо убрать полностью GET с сайта, либо производить жесткую валидацию внутренних URL-ей, либо вообще при отправке сервер пытается загрузить header имиджа.
                                                                  +2
                                                                  Это не XSS, а CSRF
                                                                    0
                                                                    Хорошим способом является динамических токен, хранящийся в сессии пользователя и добавляемый во все формы как скрытый параметр.

                                                                    Если параметра нет — сервер либо игнорирует запрос, либо показывает страницу вида «Вы уверены?»
                                                                      0
                                                                      Между прочим, Хабр режет картинки на внутренние скрипты
                                                                      0
                                                                      От 95% XSS неплохо защищает сборка целиком через DOM, ну или только динамических полей. Да и код чище получается.
                                                                        0
                                                                        htmlspecialchars не защищает от XSS с использованием UTF-7, нужно использовать htmlentities — http://stackoverflow.com/questions/3623236/htmlspecialchars-vs-htmlentities-when-concerned-with-xss

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

                                                                        Самое читаемое