Очевидные 3 правила безопасности

    Правило №1. Делайте все авторизационные куки HttpOnly


    Куки с флагом HttpOnly не видны браузерному коду, а отправляются только на сервер. На практике у вас почти никогда нет необходимости получать их содержимое со стороны клиента (если такая необходимость почему-то у вас возникла — пересмотрите архитектуру авторизации, скорее всего, там что-то не так). А вот злоумышленнику, нашедшему XSS — а XSS так или иначе когда-нибудь где-нибудь найдется — отсутствие HttpOnly на авторизационных куках доставит много радости.

    Правило №2. Выполняйте действия через POST, защищая случайным ключом


    GET — это получение информации. POST — выполнение действия. Это не просто вопрос красоты и кошерности парадигмы, это практический вопрос безопасности, потому что GET выполняется без явного участия пользователя, а на подозрительную отправку POST-запроса браузер обязательно переспросит.

    Например, если у вас логаут выполняется путем захода на сферическое /auth/logout, то каждый юзер, увидев невидимую картинку <img src="/auth/logout">, будет внезапно разлогинен. И это самое безобидное, ведь могут быть картинки "/comment/add", "/item/vote" или даже "/admin/delete-all-these-users". Если ваш язык и фреймворк смешивает данные из POST и GET (а таких много), всегда проверяйте тип запроса для URL действий.

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

    Правило №3. Не доверяйте браузеру


    Со стороны сервера всегда относитесь к вашему javascript-коду так, как будто он весь, от первой до последней буквы написан вашим самым ненавистным врагом, желающим сломать ваш сайт, нарушить целостность ваших данных и продать в рабство вашу жену. Тем более, что иногда это действительно так.

    Как всегда рад помочь,
    ваш К.О.
    Поделиться публикацией

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

      +29
      Иногда такие советы по использованию POST вместо GET внушают разработчику ложное чувство безопасности. От CSRF иногда даже проверка X-Requested-With не спасает.

      По этому, лучше добавьте про CSRF подробнее, где вы приводите пример про выход пользователя.

      А в остальном хоть и мало написано, но про HttpOnly я не знал, даже стыдно немного :-)
        +4
        И не вы один. Случайно наткнулся на httpOnly буквально вчера, разглядывая исходники CodeIgniter на GitHub.
          0
          То есть в CI это из-коробки? Тогда это прекрасно, ибо только этим фреймворком и пользуюсь)
            0
            Возникновение у вас такого вопроса наводит на мысль, что стоит вам чуть больше времени уделять программированию без фреймворков.
        • НЛО прилетело и опубликовало эту надпись здесь
            +1
            Ссылка на первоисточник — голова.
              +2
              Те он сам это придумал?
              0
              «где исчерпывающий сборник источников того, что надо знать для написания хорошего сайта?»
              Тут поможет только специалист, обладающий обширным опытом.
              Я считаю, что некоторым вещам научиться можно, наступая только на собственные грабли.
            +5
            Правило №2. Выполняйте действия только через POST, а не через GET
            Сильно неоднозначное правило.
            С одной стороны то, что /auth/logout сделан post-ом не защищает от атак вида img src, просто они становятся изощреннее (фреймы, яваскрипты, невидимые кнопки и т.д.), поэтому это опасное заблуждение видеть в этом безопасность.
            С другой стороны, все важные действия все равно нужно защищать «кодом» вида /auth/logout/9284hg2og или уникальным или вообще одноразовым. Ситуаций когда защита не нужна почти нет.
              0
              Ничего не защищает ни от чего. Любое правило лишь увеличивает барьер. При отсутствии банальной проверки на POST этот барьер практически нулевой и обходится просто картинкой, которые разрешено постить на любом форуме. А вот постить «фреймы, яваскрипты и невидимые кнопки» не разрешено нигде, и для этого как минимум потребуется свой сайт.
                +2
                А вот постить «фреймы, яваскрипты и невидимые кнопки» не разрешено нигде, и для этого как минимум потребуется свой сайт.
                И свой сайт и размещение картинки — это действия выполняемые на постороннем для атакуемого ресурсе. Токен защищает от всех атак подобного типа, а не просто «немного усложняет задачу».

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

                  –11
                  С помощью терморектального криптоанализа.
                    +2
                    То есть Ваша аргументация сводится к тому, что простые в применении токены защищающие от целого спектра атак (доступных даже школоло) на фиг не нужно применять, ввиду того, что есть терморектальный криптоанализ, против которого они бессильны. Это интересно.
                      0
                      Использовать «токены» (вообще, от CSRF обычно защищаются проверкой текущей сессии, а не так, как вы предлагаете) — это далеко не так просто, для этого надо дописывать почти все запросы с обеих сторон. А для проверки на POST — добавить одну строчку в код. И это отсекает 80% злоумышленников, которым просто лень делать свой сайт для взламывания вашего сайтика.

                      Топик называется «очевидные 3 правила», остальные 837 правил здесь не указаны.
                        +2
                        которым просто лень делать свой сайт для взламывания вашего сайтика

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

                          Да, мой сенсей… :)

                          Напишите другой пост про защиту от CSRF, кто мешает? Проверка на POST обязательна и с этим спорить нет смысла. Я, кстати, дописал уже абзац про проверку сессий.
                              +4
                              Проверка на POST обязательна и с этим спорить нет смысла.
                              При наличии токенов она на фиг не нужна — вот с этим нет смысла спорить:)

                              Использовать «токены» (вообще, от CSRF обычно защищаются проверкой текущей сессии, а не так, как вы предлагаете) — это далеко не так просто, для этого надо дописывать почти все запросы с обеих сторон
                              Проверкой текущей сессии от csrf не защищаются, т.к. в ряде случаев сессия как раз может быть текущей, если Вы именно о сессии.

                              Что касается «не так просто» и «надо дописывать почти все запросы», то это Вы заблуждаетесь. В большинстве случаев это необычайно просто, несложный код строк на 50, почти универсальный для любого движка и всего делов. Задача-то всего лишь — на выходе нужные формы/ссылки на выходе перехватить и дописать к ним токен, а на входе проверить их же. Лезть в сам движок не необходимо. Учитывая малое количество разных типов управляющих форм/ссылок — это редко бывает проблемой. Работы в подавляющем числе случаев — на пару часов максимум, решали эту задачу уже для кучи сайтов, когда нужно было быстрое решение, а не «давайтека перепишем все с нуля по правильному» (с)
                                –3
                                Проверкой текущей сессии от csrf не защищаются, т.к. в ряде случаев сессия как раз может быть текущей, если Вы именно о сессии.

                                Чего-чего? Проверка совпадения ID сессии из запроса с реальной текущей сессией — это основной метод защиты от CSRF всегда был и остается.

                                В большинстве случаев это необычайно просто, несложный код строк на 50

                                Ой ну вот не надо… Вы не та аудитория, для которой я написал этот пост, вот и все. Пост для начинающих программистов, а не для тех, кто «решал уже эту задачу для кучи сайтов».
                                  0
                                  Чего-чего? Проверка совпадения ID сессии из запроса с реальной текущей сессией — это основной метод защиты от CSRF всегда был и остается.

                                  Страннота. Я всегда думал что от CSRF защищаются генерацией уникального токена для каждого действия пользователя.
                        +1
                        Если CSRF-токен — это случайный набор символов (хорошо, псевдослучайный, но это не увеличит ваши шансы практически ни на сколько), то никакие пытки разработчиков (их вы имеете ввиду под терморектальным криптоанализом?) не помогут.
                          –4
                          Не разработчиков. Пользователя. Или его жены.

                          С помощью метода терморектального криптоанализа можно преодолеть любой уровень защиты, это одна из фундаментальных аксиом Вселенной.
                            +1
                            В таком случае, зачем вы написали эту статью?
                              –1
                              Явно не для того, чтобы описать все возможные способы защиты от всех возможных атак. Проверка сессии на каждом запросе — это далеко не очевидное правило. Может, прекратим этот бесполезный спор?
                                +3
                                Проверка сессии на каждом запросе — это далеко не очевидное правило
                                Делать всё post-ом это не защищающее правило. А проверка сессии — защищающее. Какое из них более очевидное на этом фоне — не суть важно:)
                                  –5
                                  Вот ведь мода пошла — диктовать автору поста, каким на самом деле должен был быть его пост. Идите и напишите свой, в чем дело-то?
                                    –1
                                    Хватит травить писателя, топик про _очевидные_ правила, а не про вообще все.
                                +1
                                42 — фундаментальная аксиомв Вселенной.
                                Остальное лишь заблуждение, хватит смотреть фильмы про хакиров, лучше заняться делом, и почитать книжку…
                                  +2
                                  Но мама, ведь я уже сделал уроки :(
                                    0
                                    Смешки смешками, но все же займитесь лучше делом. Вы в этом посте милион коментов написали, а толку? за это время можно было бы уже до 1к а то и больше президентов свободно заработать.
                            +1
                            И свой сайт и размещение картинкиэто действия выполняемые на постороннем для атакуемого ресурсе. Токен защищает от всех атак подобного типа, а не просто «немного усложняет задачу».

                            Речь, как я понял, идет о том, что никакой картинки на самом деле нету :-) Просто злоумышленник оставляет на форуме картинку, в качестве адреса которой указывает что-то вроде "/admin/users/delete/1" или что-нибудь похуже,

                            Каким образом не ломая сайт и компьютер пользователя можно обойти, допустим, одноразовый токен в форме? Вопрос без подколки, правда интересно.

                            Зависит от кривизны рук разработчика. Этот код может быть не случайным, а сгенерированным по какому-то крайне очевидному алгоритму: md5-хэш адреса страницы, например, или еще что-нибудь.
                              0
                              Речь, как я понял, идет о том, что никакой картинки на самом деле нету :-) Просто злоумышленник оставляет на форуме картинку, в качестве адреса которой указывает что-то вроде "/admin/users/delete/1" или что-нибудь похуже,
                              Именно так, конечно.
                        +3
                        Про «HttpOnly» куки как-то не ясно описано, они браузеру видны, просто передаются только через http запросы самим браузером, при этом недоступны, например, через JavaScript.
                          +2
                          «Защита» методом POST защищает строго только от повторной отправки формы.

                          От атак защищает CSRF-токен (сессия и куки не спасут).
                            –5
                            От CSRF как раз спасает проверка сессии и куки.
                              0
                              Это, извините, как? Сессия и куки прекрасно подставятся от самого пользователя без его участия. img.

                              Здесь подробнее от CSRF.

                                0
                                Прочитайте внимательней в это предложение:
                                сравнивать текущий session_id пользователя с переданным в запросе (не в куке).

                                Надо передавать в запросе session_id и на сервере сравнивать его с реальным. Это защищает от CSRF.
                                  +3
                                  session_id в вашем случае и является csrf token, но несет дополнительный функционал, который можно использовать для взлома.

                                  Лучше уж md5(session id).
                                    –3
                                    Не путайте, как раз наоборот. Это случайный токен является мягким вариантом сессии пользователя, которые, скорее всего, уже и так есть. Если уже в наличии есть хороший случайный session_id, то его и достаточно.

                                    Но вообще, это все вопрос вкуса.
                                      0
                                      Блажен, кто верует. Хотя, если брать, например, PHP, то там идентификатор сессии в режиме «по умолчанию» хотя бы не последователен.

                                      Но есть не только PHP, и не только режим по умолчанию.
                                        0
                                        Если в какой-то системе session_id не псевдослучаен, то систему следует выкинуть на помойку.

                                        Вы ведь не будете спорить с тем, что псевдослучайный токен требуется помнить на сервере для каждого клиента? Разве это не получается аналог механизма сессий?
                                          0
                                          Нужно иметь механизм валидации токена, не более того.
                                            0
                                            Тогда это будет не случайный токен, а хэш, который использовать явно менее предпочтительно, чем случайную сессию.
                                              0
                                              Кто вам сказал, что session id в PHP уникален?)

                                              Во-первых, дубликаты возможны.

                                              Во-вторых, он вычисляется (по умолчанию, это можно изменить) с использованием gettimeofday, со всеми вытекающими последствиями (предсказуемость).
                                                0
                                                Кто вам сказал, что я про session id в PHP? Я про псевдослучайную величину и про известный хэш известных данных. Как это реализовано в vanilla php — мне по барабану.
                                                  0
                                                  В таком случае почему вы привязались к session id?

                                                  Например, в большинстве механизмов сессий, завязанных на РСУБД, session id — это просто автоматически инкрементируемое целое число.
                                                    0
                                                    Автоматически инкрементируемое целое число — тоже неплохой вариант. Предугадать его в момент отправки запроса будет непросто.
                                                      0
                                                      Что означает, в переводе, что session id является ослабленной версией random csrf token.

                                                      Даже больше, это потребует гарантий, что сессии подохнут раньше попыток злоумышленников…
                                                        –3
                                                        Я все-таки останусь при мнении, что это запоминаемый random csrf token является лишней избыточной версией сессии пользователя, которая и так уже есть.
                                                      +1
                                                      Более того, зачем привязываться к POST? Это всего-лишь usability.
                                        –1
                                        несет дополнительный функционал, который можно использовать для взлома.

                                        session_id и так хранится в куке клиента вообще-то.
                                          +2
                                          Правило №1. Делайте все авторизационные куки HttpOnly
                                            +2
                                            Кстати да, чтобы слать сессию вместе с запросом, нужно её где-то хранить на странице.
                                              0
                                              Ну необязательно. Сессия не всегда является авторизационной кукой, потому что может использоваться вместе, скажем, с httponly-кукой с хэшем пароля, без которого невалидна.
                                                0
                                                И зачем тогда хранить сессию в куках, если можно ограничиться хешем пароля?
                                                  0
                                                  Ну дык… чтобы отправлять сессию в POST-ах, зачем же еще :)
                                                    0
                                                    Так если она и без того будет в форме, зачем её дублировать в куках? Или Вы хотите сравнивать значение из куки со значением из формы? Вот Вам и решение проблемы
                                                    в вашем случае правильное значение токена надо помнить на сервере

                                                    Поздравляю, Вы изобрели csrf-токен.
                                                      –2
                                                      Или Вы хотите сравнивать значение из куки со значением из формы?

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

                                                      Поздравляю, Вы изобрели csrf-токен.

                                                      Поздравляю, вводя псевдослучайный токен с его запоминанием на сервере, вы изобрели сессию пользователя. Причем вторую, ведь одна у вас наверняка уже есть.
                                                        +2
                                                        Перечитайте сообщение ещё раз. Нет необходимости в запоминании токена на сервере.
                                                        И цель у csrf-токена только одна — подтвердить то, что юзер осознанно совершил действие, в отличие от sid'а.
                                                          0
                                                          Необходимость для запоминания есть, потому что псевдослучайная величина не прогнозируется при проверке запроса, а хэши использовать менее секьюрно.
                                                            0
                                                            Ничего прогнозировать не нужно. Нужно
                                                            сравнивать значение из куки со значением из формы
                                                              0
                                                              В таком случае не надо, да. Но это несколько больше строк кода, чем банальная проверка сессии.
                                                                0
                                                                Несколько — это сколько? Тут сделать-то нужно:
                                                                1. Сгенерировать случайную строчку. 1 строчка кода.
                                                                2. Записать её в куку. 1 строчка кода.
                                                                3. Вывести соответствующий тег в коде страницы. 1 строчка кода.
                                                                4. Сравнить значение из куки со значением из формы. 1 строчка кода.
                                                                  0
                                                                  Хорошо, убедили, это тоже хороший способ :)

                                                                  Там, где я видел раньше, ключ обычно запоминали в кэше на сервере, а не в куках.
                                                              0
                                                              CSRF-токен требует алгоритма валидации, не более. Ему не нужно быть псевдослучайным.

                                                              Обычно он предоставляется для конкретной формы и служит для проверки, что именно эта форма была закоммичена.

                                                              Причем, что вас успокоит, это гарантируется таким механизмом:

                                                              * Код сессии используется для токена;
                                                              * После коммита формы код сессии меняется.

                                                              +2
                                                              Нельзя полностью полагаться на HttpOnly флаг, в старых браузерах (например в IE6) есть баги связанные с возможностью доступа к HttpOnly кукам при отправке запроса через XHttpRequest (а именно так скорее всего и будет отправлен запрос при CSRF атаке). Если есть 2 способа защиты и один из них зависит от версии или вендора браузера у клиента, то стоит выбрать тот, который более универсален. Поэтому лучше хранить токен в сессии пользователя на сервере.
                                                  0
                                                  Строго говоря, вы ограничили область использования вашей статьи областью: PHP в режиме сешн-куков с обычными обработчиками сессий.
                                              +2
                                              Просто artch непонятно изъясняется. Сессию он предлагает слать отдельно POST-запросом и проверять на совпадение ту, что в куках, и ту, что в запросе.
                                                +1
                                                А чем это проще пересылки токена в форме?

                                                <form ...>
                                                <input type=«hidden» name=«csrf_token» value=«session dependent» />
                                                  –3
                                                  Тем, что в вашем случае правильное значение токена надо помнить на сервере, а это получается дублирование функциональности id сессии, который и так, скорее всего, у вас уже есть. А если вы используете не случайное значение, а некий хэш, то, думаю, сами догадаетесь, чем это хуже.
                                                    +1
                                                    Я выше уже писал. md5(session_id) — ничем не хуже. Хотя, конечно, на самом деле надо делать не так. Хотя бы конкатенировать с какой-нибудь константой.
                                                      0
                                                      Ну так конечно, можно и хэш сессии, какая разница? Главное, что не надо помнить дополнительную величину для каждого юзера.
                                                  –3
                                                  Это не я предлагаю, это так делается в куче систем, например, в Битриксе, UMI и других.
                                            +7
                                            Кстати, если кому-то интересно, как включить флаг HttpOnly для cookies, которые устанавливает система авторизации в Django — если вы используете Django 1.4, то вам вообще ничего не надо делать. :)

                                            Можете проверить — достаточно зайти на свой сайт, открыть JavaScript-консоль (или, если в браузере это не запрещено, как в последних версиях Firefox, написать JS-фрагмент в адресной строке) и выполнить:

                                            alert(document.cookie);


                                            Если там будет только csrftoken — значит всё правильно, идентификатор сессии передаётся только в заголовках запросов. Если там будет csrftoken и sessionid — значит, нужно обновить Django, вручную установить настройку или воспользоваться патчем — тут по ситуации (впрочем, нужно учитывать, что библиотека Cookie поддерживает HttpOnly только начиная с версии Python 2.6 — то есть с более старыми версиями Python это не работает).

                                            В конце 2010 в Django добавили настройку SESSION_COOKIE_HTTPONLY, которая определяет, нужно ли для cookies системы авторизации указывать флаг HttpOnly. Вот тикет, если интересно.

                                            Ну, а в Django 1.4 эта настройка по умолчанию ставится в значение True, соответственно, флаг добавляется.

                                            Собственно, тут надо учитывать, что этого синтаксиса нет в стандарте RFC2109, и, соответственно, он не поддерживается некоторыми старыми браузерами. Они игнорируют этот флаг, и устанавливают обычные cookies, доступные через JavaScript.

                                            Кстати, вот ещё одно очень важное правило. Если ваш сайт работает по HTTPS и вы перенаправляете все запросы к HTTP на HTTPS, например так:

                                            server {
                                                listen 80;
                                                if ( $scheme = "http" ) {
                                                    rewrite ^/(.*)$ https://$host/$1 permanent;
                                                }
                                            }


                                            То в этом случае нужно в settings.py указывать такие настройки:

                                            SESSION_COOKIE_SECURE = True
                                            CSRF_COOKIE_SECURE = True

                                            В этом случае при отправке cookies системой авторизации и CSRF middleware для cookies будет выставляться флаг Secure, что указывает браузерам на то, что эти cookies необходимо отправлять только по HTTPS, и не отправлять по HTTP.

                                            По умолчанию обе настройки, естественно, False, поскольку бо́льшая часть сайтов работает по HTTP.

                                            Таким образом, когда авторизованный на сайте пользователь заходит на http://site.com/, то веб-сервер, получая запрос по HTTP, перенаправляет пользователя на ту же самую страницу того же самого сайта, но на HTTPS, и дальше пользователь уже видит запрошенную страницу. Но при этом если эти настройки не были включены, то тогда cookies отправляются на сервер два раза — один раз по HTTP, и второй по HTTPS. Соответственно, если подключение пользователя прослушивается (будь то публичная сеть Wi-Fi или проникновение в проводное соединение), то cookies, переданные в открытом виде, будут легко перехвачены. А, соответственно, если эти настройки активированы, то cookies не будут переданы в открытом виде — браузер в этом случае отправляет их только при подключении к серверу по HTTPS.
                                              +2
                                              А вот злоумышленнику, нашедшему XSS — а XSS так или иначе когда-нибудь где-нибудь найдется — отсутствие HttpOnly на авторизационных куках доставит много радости.
                                              Да, но как говорит википедия:
                                              «This restriction mitigates but does not eliminate the threat of session cookie theft via cross-site scripting (XSS).»
                                              en.wikipedia.org/wiki/HTTP_cookie#HttpOnly_cookie

                                              на подозрительную отправку POST-запроса браузер обязательно переспросит
                                              А вот это сомнительно. Отчего-бы браузеру переспрашивать сабмит формы? Даже если форма сабмитится POST-ом, и даже если на другой домен — вряд-ли браузер будет что-то переспрашивать. Разве только если у вас стоит какой-нибудь специальный XSRF-pervention плагин.
                                                +2
                                                Еще X-Frame-options полезен может быть)
                                                  –1
                                                  Для дополнительной защиты следует проверять также referer

                                                  Абсолютно бесполезная проверка. Такая же как делать trim(htmlspecialchars(strip_tags($_GET['var']))) для защиты от SQL-инъекций и делать addslashes для защиты от XSS-инъекций. Больше доставит головной боли тому кто будет разбираться в коде. Подделать реферер, а так же все что формируется на клиентской стороне, а реферер отправляет именно браузер — вообще не проблема. token'а более чем достаточно. Причем лучше если это будет не session_id а индивидуальный токен на каждый запрос.
                                                    0
                                                    совершенно согласен, знаю как сбросить реферер например
                                                    +3
                                                    Кстати, примечательно, что разработчики крупных сайтов по какой-то причине не руководствуются первым указанным правилом. Пример — vk.com. Или, кстати, сам Хабр.

                                                    Из всех крупных сайтов, которые я проверил (по Рунету), флаг httponly для cookies, содержащих идентификатор сессии, ставит разве что Яндекс. :)
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Многие движки используют довольно старое серверное ПО, например httpOnly для PHP появилось только с версии 5.2.0:

                                                        5.2.0 The httponly parameter was added.

                                                        До этого — можно было использовать костыли.
                                                        0
                                                        … потому как воровать куки это уже прошлый век 8) MitB атаки — самое трешовое зло.
                                                          +7
                                                          Ну и помимо очевидных ещё несколько не очень очевидных:

                                                          3а.
                                                          Со стороны сервера всегда относитесь к вашему javascript-коду так, как будто он весь, от первой до последней буквы написан вашим самым ненавистным врагом, желающим сломать ваш сайт, нарушить целостность ваших данных и продать в рабство вашу жену. Тем более, что иногда это действительно так.

                                                          С любой стороны в любом коде относитесь к нему так. Если в ф-ю или метод должно прийти число — проверьте что это число. Если непустая строка — проверьте что это строка и что она не пустая и т. д…
                                                          Причем:
                                                          if ($n != (int)$n) {
                                                              // это неправильная проверка, проверьте на "123'; DROP DATABASE users; --"
                                                          }
                                                          

                                                          Если что то идет не так — не нужно подавлять ошибки — лучше кричите — trigger_error, user_error,… — отлавливайте их и изучайте.

                                                          4. Инициализируйте переменные и работайте с error_reporting(E_ALL)
                                                          5. Все переменные которые подставляются в SQL запрос должны быть экранированы с помощью mysql_escape_string либо mysql_real_escape_string либо bind либо плейсхолдеры, свои или готовые. Никогда не доверяйте данным, даже если переменная называется $only_fucking_numbers_can_be_in_this_variable все равно не верьте.
                                                          6. Все что выводится в браузер должно пропускаться через htmlspecialchars если только вы намерено не хотите вывести html, но тогда удаляйте там все что не разрешено. Используйте autoescape шаблонизаторов.
                                                          7. Все html-формы должны отправлятся вместе с токеном который генерируется и проверяется на сервере. Запросы на изменение данных не должны отправлятся POST'ом.
                                                          8. Не давайте пользователю передавать имена файлов которые будут впоследствии подключатся.
                                                          9. Не верьте HTTP_REFERER'у, USER_AGENT'у, SCRIPT_NAME, mime-type файлов и т.д… Это все формируется на КЛИЕНТЕ и может быть подделано.
                                                          10. Не храните пароли ни в открытом ни в обратимо-зашифрованном виде — только хеш, а лучше хеш с солью. Пароль знает только владелец, администратор может только сбросить его.
                                                          11. Не пользуйтесь короткими паролями.
                                                          12. Не пользуйтесь FTP, только SFTP.
                                                          13. При передаче приватных данных используйте HTTPS с реальным (а не самоподписанным) сертификатом.
                                                          14. Правильно выставляйте настройки кеширования приватных данных. (Cache-control: ...)
                                                          15. Не ограничивайтесь проверкой данных на клиенской стороне. Всегда проверяйте все данные на сервере.
                                                          16. Пользуйтесь различными сканерами уязвимостей.
                                                          17. Запрещайте все что не разрешено. Не надейтесь что кто то никогда не узнает о чем то. Обо всем все узнают обязательно.
                                                          18. Защищайтесь в первую очередь от себя и от тех кто работает непосредственно с кодом. Самое уязвимое место — человек. 99% самых громких взломов были бы невозможны без социальной инженерии.
                                                          19. Используйте мониторинг — взломать не наследив — не просто.
                                                          20. Пытайтесь сломать сами чужой (ну свой то конечно не сломать) код.
                                                            +2
                                                            if ($n != (int)$n) {
                                                                // это неправильная проверка, проверьте на "123'; DROP DATABASE users; --"
                                                            }
                                                            

                                                            Вот я когда такие советы вижу у меня каждый раз глаз дергается. Понятное дело — is_numeric и прочее, но мультизапросы нужно еще суметь выполнить. Если мы говорим про mysql_ext вам придется постараться, чтобы сработали два запроса. Если же мы говорим о mysqli/pdo это в принципе невозможно из-за подготовленных выражений.

                                                            Пожалуйста, меньше пишите проверки сами, больше перекладывайте на компоненты — любая нормальная обертка над БД избавит вас от миллиона проблем. А мусорить код конструкциями вида:
                                                            $var = $_GET['var']'
                                                            if(is_numeric($var) && is_int($var) && $var<2147483647 &&  $var>-2147483647){
                                                            }
                                                            

                                                            Ну вы поняли. Есть адаптер — попробовали выполнить запрос, не получилось, выбросили исключение и поймали его.
                                                              +1
                                                              Мультизапрос для примера, фантазии взломщика можно только позавидовать. Не обязательно цель удалить данные:
                                                              ' AND BENCHMARK(SIN(1), 100000000000000) --

                                                              вот вам и DDOS.

                                                              А на счет проверок я лишь написал что НУЖНО проверять, а КАК проверять — это уже отдельная тема для разговора.
                                                              0
                                                              > if ($n != (int)$n) {
                                                              > // это неправильная проверка, проверьте на «123'; DROP DATABASE users; --»
                                                              Проверил — вернуло false. ЧЯДНТ? (-:
                                                                0
                                                                Всё так, пример показывает типичную неправильную проверку на тип. Пишут обычно:

                                                                $n = $_GET['n'];
                                                                if ($n != (int)$n) {
                                                                    trigger_error("Invalid number", E_USER_WARNING);
                                                                    return false;
                                                                }
                                                                


                                                                И потом удивляются как их сломали.
                                                                Или вопрос был почему так? При сравнении числа со строкой происходит преобразование к числу обеих переменных. Т.е. (1 == «1abc»)
                                                                  +1
                                                                  > При сравнении числа со строкой происходит преобразование к числу обеих переменных.
                                                                  А, вот оно что.
                                                                  Удивляет сам факт возвращения 1 на (int)«1abc».

                                                                  Похоже даже в javascript какой-нибудь аналогичный способ приведения, вроде «1' abc»*1 вернёт NaN а не 1, и NaN != NaN (пример: if(«1»*1==«1»*1) alert(«ok»); — алерт появится, if(«1abc»*1 == «1abc»*1) alert(«not ok»); — алерт не появляется).

                                                                  То, что с проверкой на равность в PHP не всё просто, видно например из таблички в первом комментарии здесь:
                                                                  stackoverflow.com/questions/80646/how-do-the-equality-double-equals-and-identity-triple-equals-comparis

                                                                  Но вот что приведение к int способно сделать int из любой строчки, начинающейся с числа — это для меня неожиданность.

                                                                  Благо на PHP я не пишу.
                                                                    0
                                                                    Если мы говорим о числах, куда как проще не проверять, а насильственно приводить:

                                                                    $n = isset($_GET['n'])? $_GET['n']*1: false;
                                                                      0
                                                                      Я всегда считал это подавлением ошибки. Если я жду номер страницы а мне приходит строка то мне не нужно молча сделать из строки число, я хочу знать откуда лезут эти строки — либо кто то ломает, либо я где то ссылку неправильно разместил и т.д…

                                                                      Можно просто сделать обертку которая будет делать тривиальные проверки данных типа: знаковое/беззнаковое число, пустая/непустая строка, валидный email, и т.д…

                                                                      Можно занятся сексом и добавить типизацию в пхп:

                                                                      try {
                                                                          $n = Integer::parse($_GET['n']);
                                                                      } catch (ParseException $e) {
                                                                          // blabla
                                                                      }
                                                                      


                                                                      но во первых полноценно на пхп сделать это не получится, а во вторых для этого есть другие языки )
                                                                        0
                                                                        Тогда уже стандартный InvalidArgumentException, и да, поддерживаю вашу позицию.
                                                                          0
                                                                          Почему «подавление ошибки»? Скорее, избегание неприятных последствий неожиданных действий клиента.

                                                                          Приведенный мною подход не обязательно нужен и будет к месту во всех случаях. Но, напр., если речь о передаваемом в GET номере страницы (.../show.php?page=123), для удобства пользователей можно позволить юзеру и самому номер страницы править в строке запроса. В этой ситуации глупо будет выводить по каждому чиху сообщение об ошибке: во-первых, «фича» с изменением номера страницы в URI недокументирована (т.е. пользуется тот, кто понимает логику работы), во-вторых, лучше показать хоть что-то, чем выводить кучу сообщений об ошибке.

                                                                          В конце-концов, наша задача в веб-приложении — не ругаться на всех и вся, а максимально корректно сделать то, что приложение может сделать. Чем меньше мы атакующему расскажем о нашей системе диагностики косяков в данных, тем нам же лучше.
                                                                            0
                                                                            Если пользователь введет .../show.php?page=123blablabal вы можете задать поведение по умолчанию — пусть даже привести к int и показать страницу с id=123 (хотя в данном случае будет корректным показать 404 ошибку). Но если он введет неверные данные — вы как программист должны об этом знать. Ошибки совсем не обязательно, даже вообще не нужно показывать пользователю, но программист должен знать обо всех случаях которые отработали не так как должны были. Все ошибки нужно логировать и анализировать.
                                                                              0
                                                                              Ну тут-то все просто: сравнили значение полученной из GET переменной до и после санации, если отличаются — считаем попыткой атаки, «логируем и анализируем».

                                                                              Добавлю, что многие проверки такого рода можно на плечи mod_security и иже с ним переложить.
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                        0
                                                                        Я так понимаю, что вы имеете в виду случай, когда HTTPS используется на публичном сайте. Если же это какой-либо внутренний ресурс компании, публичное использование которого делать не предполагается, то достаточно будет либо тщательно проверять fingerprint (MD5/SHA1) и, соответственно, не подтверждать сертификат, если он не совпадает с ожидаемым, либо настроить в браузере CAcert.org и использовать их сертификат, либо вообще добавить в браузер собственный CA (и самостоятельно подписывать либо количество сертификатов).

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

                                                                          Что они как правило и делают ) Вы часто проверяете подлинность ключа когда браузер говорит что сертификат неверный? Если да — то респект вам конечно, но далеко не все такие.
                                                                      0
                                                                      Касательно HTTPS у меня есть одна проблемка, которая не дает покоя.

                                                                      Допустим, на сайте существует приватная зона, где редактируется профиль, пароль и т.п.
                                                                      На том же сайте есть каталог производителей и товаров. Я хочу, чтобы на каждой странице каталога пользователь мог оставить комментарий и поставить оценку.
                                                                      Как мне лучше это реализовать с точки зрения безопасности?

                                                                      Вижу 3 варианта:

                                                                      1. Отдавать залогинившимся пользователям все страницы каталога только по https
                                                                          + наилучшая защита
                                                                          -  коллеги по работе, посетители и конкуренты решат, что разработчик - придурок и параноик
                                                                      
                                                                      2. Страницы каталога отдавать по http, для комментариев использовать iframe с https
                                                                          + "нормальный, привычный" вид адресной строки
                                                                          -  считается, что небезопасно смешивать на одной странице данные http/https
                                                                      
                                                                      3. Страницы каталога отдавать по http, комментирующие идентифицируются небезопасной кукой.
                                                                          + наиболее часто используемый вариант, довольно простой
                                                                          -  система комментирования будет подвержена атакам (cookie hijacking и т.п.)
                                                                      

                                                                      Что из этого мне предпочесть? Может есть варианты получше?
                                                                        0
                                                                        Ну наверное нужно сначала определится что защищаем? Обычно защищают пароль или данные кредитных карт, cv2 коды и т.д… — т.е. та информация которая не должна никоим образом хранится на сервере, а следовательно и получить её владея сессией нельзя. Защищается только момент передачи этой информации на сервер.
                                                                        Если вы считаете что данные профиля — (фамилия имя отчество имейл) — это конфиденциальная информация то либо эту информацию не нужно нигде выводить, чтобы владея сессией нельзя было её получить, либо передавать айди сессии всегда по зашифрованному каналу — а значит шифровать весь трафик.

                                                                        Есть ещё один вариант — когда переходите на секьюрную страницу — запрашивать пароль. Поднимать новую сессию и там уже показывать нужную информацию. Т.е. как бы отдельный сайт с отдельной авторизацией, только с таким же дизайном.
                                                                      +3
                                                                      goo.gl/6gmMd
                                                                      Хм…
                                                                        +1
                                                                        Ну там хотя бы редирект. Но вообще, да…
                                                                          +3
                                                                          Предупрежу: ссылка, похоже, разлогинивает пользователя из Google-аккаунта.
                                                                            0
                                                                            Поздно ))
                                                                              0
                                                                              В смысле «Поздно сказали».
                                                                          +3
                                                                          Могу порекомендовать две отличных книги: «The Tangled Web: A Guide to Securing Modern Web Applications» (http://nostarch.com/tangledweb.htm), покрывающую большую часть client-side атак и «Web Application Hacker's Handbook» (http://www.amazon.com/gp/product/1118026470), покрывающую практически все.

                                                                          После прочтения хотя бы первой, хотя бы по-диагонали, приходит понимание того, насколько глубока эта кроличья нора. Хочу также предупредить, что мысли о том, будто есть какое-то конечное число универсальных правил, следуя которым можно построить защищенное by-design web-приложение — отпадут сами собой ;)
                                                                            +1
                                                                            При установке кукисов в Rails до 3-й версии был флаг
                                                                            :http_only => true
                                                                            В Rails 3 и выше поменяли на
                                                                            :httponly => true
                                                                              0
                                                                              чувствуется рука профессионалов…
                                                                              –2
                                                                              На практике у вас почти никогда нет необходимости получать их содержимое со стороны клиента (если такая необходимость почему-то у вас возникла — пересмотрите архитектуру авторизации, скорее всего, там что-то не так).

                                                                              Бред.
                                                                              Использую на своём сайте куку не http only и доволен.
                                                                              Это вы пересмотрите свой мозг, господин К.О. — возможно, там у вас что-то не так.
                                                                                +3
                                                                                Может быть этот первый совет и был бы оправдан в эпоху дремучего web 1.0, но в эпоху web2.0 мы отправляем запросы яваскриптом, а, значит, нам необходима на странице яваскриптовая переменная с каким-либо тайным уникальным ключом.
                                                                                +1
                                                                                Второй и третий советы правильные.
                                                                                Упомянуть ещё можно, что гугл может зайти на Get-ссылку /delete-article в вашей самописной википедии.
                                                                                  0
                                                                                  Радует, что в последнее время часто на хабре вижу статьи, которые упоминают о csrf уязвимостях. По-моему, дыра не менее важная чем sql inj, о которой почти все знают, в отличии от csrf.
                                                                                    +1
                                                                                    Все-таки, менее. С помощью полноценной sql-инъекции можно сделать практически что угодно, а csrf позволяет лишь выполнить уже существующий запрос, который на сервере в любом случае проверяется и обдумывается.
                                                                                      0
                                                                                      Ну да, sql injection таки инструмент более низкоуровневый, однако csrf легче эксплуатировать если смотреть со стороны незнания архитектуры субд/приложения.
                                                                                    0
                                                                                    Обнаружил, что httponly куки не возвращаются в php который крутится как fastcgi php-fpm получающий данные с апача.

                                                                                    <?php
                                                                                    ob_start();
                                                                                    
                                                                                    
                                                                                    var_dump($_COOKIE,$_SERVER);
                                                                                    setcookie("some",time(),0,"/","*",false,true); // set http only cookie
                                                                                    
                                                                                    ob_end_flush();
                                                                                    ?>


                                                                                    Поправьте плиз, если туплю где-то.
                                                                                      0
                                                                                      php стоит версии 5.4.0
                                                                                        0
                                                                                        Чёрт, ошибочка. Вместо domain "*", надо ""

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

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