Книга: Безопасность веб-приложений. Исчерпывающий гид для начинающих разработчиков
Назад: Сомнительные данные
Дальше: Аутентификация (AuthN)

Идентификация

Как уже говорилось ранее, под идентификацией в компьютерной системе или сети мы имеем в виду способ, которым компьютер распознает пользователя. Проверка компьютером подлинности личности называется аутентификацией (AuthN), а использование личности для проверки разрешения на просмотр и действия в системе – авторизацией (AuthZ). Система предоставления или запрета функциональных возможностей и информации в приложении или сети называется управлением доступом.

Если в системе есть пользователи, придется найти способ их идентифицировать. Если система находится внутри сети, нет необходимости самостоятельно писать функцию идентификации. Лучше использовать уже существующую (если только нет особых бизнес-требований). Наиболее распространенной сетевой системой идентификации является Active Directory компании Microsoft. Тем не менее некоторые провайдеры публичного облака предлагают собственные системы идентификации, а на рынке существует множество других систем, которые также могут выполнять подобные функции. Я не буду предлагать конкретную систему – необходимо выбрать ту, которая лучше всего подходит для вашей организации и ее технических требований. Любая из них будет значительно лучше написанной собственноручно (если только вы не занимаетесь именно этим бизнесом, то есть не являетесь компанией, предоставляющей услуги идентификации или облачного провайдера).

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

Управление сессиями

Как уже говорилось ранее, изначально функция управления сессиями не была заложена в дизайн HTTP-протокола и веб-страницы. Планировалось, что страницы будут статичными (неизменными): люди будут заходить на них, читать информацию и уходить. Однако, как всем известно, они стали совсем другими. Вход пользователей в приложение и переход между страницами называется сессией, и ее необходимо отслеживать. В этом разделе мы обсудим стратегии управления сессиями.

СОВЕТ. Помните правило всегда использовать функции платформы, а не писать свои? Оно применимо и в данном случае! Если в платформе есть функции управления сессиями, используйте именно их, а не пишите собственные с нуля.

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



Рис. 4.2. Пример потока управления сессией





Идентификатор сессии и токен сессии используются как взаимозаменяемые понятия. Идентификаторы сессии передаются в защищенном файле cookie (с использованием настроек, описанных в главе 3) и никогда – в параметрах URL. Они предназначены только для управления сессиями, и использовать их для передачи сторонней информации (например, номера счета) в целях повышения эффективности не рекомендуется. Код бесплатен – просто пишите больше.

Ниже приведены дополнительные рекомендации по управлению сессиями от команды, разработавшей памятки OWASP.

• Идентификаторы сессии должны иметь длину не менее 128 символов.

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

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

• Идентификатор сессии должен передаваться только по зашифрованным каналам.

• Сессия должна быть прервана после выхода пользователя из системы.

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

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

ПРИМЕЧАНИЕ. Если после прочтения этой книги вы захотите получить больше информации о безопасности приложений, я советую присоединиться к местному отделению OWASP.

Проверка границ

В 1996 году компания Aleph One опубликовала потрясшую мир программного обеспечения статью «Разбиение стека ради удовольствия и прибыли» (англ. Smashing the Stack for Fun and Profit). В статье подробно описано, как пользователь может ввести чрезмерное количество данных в программное приложение, разработанное на языке C, перегрузить переменную в памяти, перезаписав стек своими данными. В ней объяснялось, как злоумышленник может ввести шелл-код (машиночитаемые компьютерные инструкции, не требующие компилятора) и таким образом получить контроль над компьютером, на котором было размещено приложение. Эта уязвимость стала возможной из-за того, что язык программирования C не является безопасным для памяти, то есть если вы объявите переменную типа char и отведете ей 20 символов пространства, а затем пользователь введет 30 символов, то язык не сможет обработать выход за установленные пределы. Вы будете сами по себе. Программа сохранит первые 20 символов в месте, выделенном для них в памяти при объявлении переменной, а остальные 10 символов будут записаны в следующие 10 мест в памяти, которые могут предназначаться для дальнейших инструкций программы или другой переменной либо быть неиспользуемым пространством. Независимо от того, что хранится в этих 10 местах в памяти, работа приложения будет некорректной. Продвинутый злоумышленник может воспользоваться такой ситуацией и внедрить собственный код для выполнения, то есть произвести удаленное выполнение кода (RCE, Remote Code Execution). Данная уязвимость перезаписи памяти для выполнения эксплойта называется переполнением буфера и считается критической уязвимостью безопасности системы.

Шло время, языки программирования развивались, и новые создавались уже с учетом требований безопасности для памяти (с обеспечением защиты от подобных ситуаций). Тем не менее многие небезопасные для памяти языки все еще используются в разработке приложений. Может возникнуть вопрос: почему?

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

Тем не менее при работе с небезопасным для памяти языком есть несколько твердых правил, применяемых независимо от ситуации. Необходимо следующее.

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

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

3. Выполнять проверку типа на каждом вводе, а также тщательно тестировать эту функцию.

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

5. По возможности проверять код на наличие надлежащего тестирования для каждого ввода.

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

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

Более продвинутым, дополнительным уровнем защиты будет добавление защиты в реальном времени, например рандомизация компоновки адресного пространства (ASLR, Address Space Layout Randomization), предотвращение выполнения данных (DEP, Data Execution Prevention), защита Stack Canary и так далее. Эти средства защиты меняют способ использования памяти с целью предотвращения связанных с ней атак.

ПРИМЕЧАНИЕ. В качестве дополнительной меры предосторожности рассмотрите возможность реализации программы ответственного раскрытия информации на случай, если вы что-то упустили. Этот совет актуален не только для данной уязвимости, но и в целом для любой программы безопасности.

СОВЕТ. Примером безопасной для памяти альтернативы языкам C и C++ является Rust. Безопасными для памяти языками являются также Java, Net (VB и C#) и Ruby on Rails.

Назад: Сомнительные данные
Дальше: Аутентификация (AuthN)