Статьи

Ваш контрольный список подготовки для вашего приложения Rails

Перед тем, как мы углубимся в этот расширенный контрольный список подготовки к производству приложения Rails, вы можете спросить: « Достаточно ли Бормана и постоянных проверок запросов на получение запросов?»

Конечно, проблемы с внедрением SQL могут быть обнаружены с помощью автоматических инструментов, и это здорово. Они все лучше и лучше. Но они все еще могут пропустить некоторые проблемы. Например, они не могут найти уязвимости в логике приложения. Так разве не было бы здорово быть более уверенным в безопасности, используя Brakeman, проверяя наличие логических уязвимостей и разрабатывая общие правила, чтобы прошлые проблемы не повторялись?

В этом руководстве рассматриваются последние два пункта, чтобы пролить свет на 10 основных уязвимостей, обнаруженных в проекте Open Web Application Security Project (OWASP). Некоторые из перечисленных вопросов уже рассмотрены в Brakeman, но я включил несколько реальных примеров и предложений для общей политики кодирования для большей безопасности. Конечно, если вы ищете быстрые выигрыши, а не стратегии, следите за разделами «Действия» ниже.

A1 SQL-инъекция

Вот уязвимость SQL-инъекции из проекта с открытым исходным кодом, которая теперь исправлена:

comments, emails = params[:id].split("+") 
Comment.update_all("state = '#{params[:state]}'", "id IN 
(#{comments})") unless comments.blank?

Это уязвимо для внедрения SQL через оба параметра: params[:state]и params[:id]. Злоумышленник может обновить comments.user_idсвой собственный идентификатор пользователя, чтобы он владел всеми ими и, таким образом, мог, вероятно, прочитать их все:

http://localhost:3000/timeline?id=1&type=&state=2', user_id = '1

Это напоминает нам о двух ключевых политиках, к которым мы всегда должны стремиться:

  • Рассмотрим все предоставленные пользователем параметры и атрибуты, которые могут быть вредоносными.
  • Никогда не используйте string inflection ( #{...}) в строках SQL, даже если вы абсолютно уверены, что введенное значение безопасно. Также избегайте внутренних областей или методов модели, где весь ввод контролируется вами. Это предотвращает путаницу в отношении того, кто отвечает за экранирование пользовательского ввода, который всегда должен быть методом, который в конечном итоге помещает строку в окончательный SQL.

действия

  • Узнайте о внедрении SQL в менее известных методах ARel , как в User.order("#{params[:sortby]} ASC").
  • Выполните ручной поиск по всему вашему проекту и найдите методы в этой шпаргалке . Используют ли они предоставленные пользователем значения напрямую?
  • Используйте форму Hash или Array для условий SQL, преобразуйте ожидаемые целые числа в целые числа, а также внесите в белый список или очистите другие параметры. Используйте эти контрмеры, даже если вы уверены, что у пользователя нет возможности повлиять на параметр. Это вторая линия защиты, которая делает код ориентированным на будущее. Другие разработчики могут использовать ваш метод через шесть месяцев с предоставленными пользователем данными.

A1 Другие инъекции

Давайте посмотрим на другой фрагмент из того же коммита :

model = params[:type].camelize.constantize 
item = model.find(params[:id]) 
item.update_attribute(:state, params[:state])

Это уязвимо для инъекций класса Ruby. Злоумышленник может определить произвольный params[:type]класс Ruby , который отвечает на find()и update_attribute()методы , а затем обновить состояние произвольного объекта с помощью params[:state].

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

Другой тип инъекции командной строки , которая может произойти в методах Rails командной строки ( %x[], system(), exec(), ). Так что не делай system("ls", params[:options]). Злоумышленник может команды цепи с помощью этих операторов: &, &&, |, ||, и так далее.

действия

  • Ищите использование constantize , классифицировать и safe_constantize . Убедитесь, что имя класса не может быть напрямую затронуто пользователем.
  • Против введения командной строки, сделать то же самое для методов %x[], system(), exec(), и .

Сессии A2 и файлы cookie

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

действия

  • Поиск по всему проекту для cookiesдоступа.
  • Назначьте что-нибудь вроде cookies[:user_name], cookies.signed[:user_id]или cookies.permanent[:login]Hash как cookies[:login] = {value: "user", httponly: true}.

Когда это все сделано, все еще есть вероятность, что пользователь воспроизводит свои собственные куки или изменяет их. Вот почему важно не хранить «состояние» в сеансе или куки. Популярным примером является мастер, в котором вы добавляете одноразовый купон в сеанс на шаге 2. Если пользователь копирует cookie сеанса на шаге 2, он может позже использовать этот одноразовый купон, вставив его обратно в браузер. Обычно это может происходить только в сеансах на основе файлов cookie.

Кроме того, важно помнить это cookies[:user_name]и cookies.permanent[:login]может быть изменено пользователем.

действия

  • Поиск по всему проекту для cookiesи sessionпринадлежностей. Если код хранит что-то там, принесет ли это значение какой-либо вред, если оно будет вставлено позже? Например, на следующем шаге мастера, в следующем сеансе или в совершенно другой учетной записи? И в то же время, проверьте, что значение является или не является секретом. Должен ли пользователь знать об этом?
  • Если это так, то это простой cookie ( cookies[:user_name], cookies.permanent[:login]), подписанный cookie ( cookies.signed[:user_id]) или сеанс не зашифрован, зашифруйте cookie / сеанс или не храните его там.
  • Когда вы читаете значение из cookies[:user_name]или cookies.permanent[:login], вы переоцениваете значение? Возможно, он был изменен пользователем.

Если приложение доступно только по протоколу HTTPS, легко добавить флаг Secure в файл cookie сеанса и все другие файлы cookie, чтобы он не просочился при перенаправлении с HTTP на HTTPS.

действия

  • Поиск по cookiesвновь аксессору и помечать их как «безопасный»:cookies[:login] = {value: "user", httponly: true, secure: true}
  • Добавьте тот же флаг в config / initializers / session_store.rb.

Аутентификация A2

Вы можете сказать: «Я в порядке. Я использую Devise, и все работает хорошо. Это так, но есть еще несколько вещей, которые вы можете проверить, и некоторые дополнительные меры безопасности, которые вы можете добавить.

действия

  • Возможно, у вас есть проверка подлинности в ApplicationController, поэтому каждое новое действие проходит проверку подлинности. Если вы проводите полный аудит, убедитесь, что фильтр аутентификации никогда не пропускается там, где он не должен быть.
  • Пароли могут быть слишком короткими, слишком простыми или, возможно, их нужно менять каждые два месяца, но пользователи всегда переключаются на те, которые использовали ранее. Это небезопасно? Зависит от ваших требований, но это определенно стоит задуматься. Это расширение Devise позволяет вам истечь пароли через некоторое время и архивировать пароли, чтобы они больше не могли быть использованы. Конфигурация Devise также включает опцию минимальной длины пароля. А вот обсуждение и реализация для более сложных паролей.
  • Проверьте свою стратегию против грубой силы. Если возможно, используйте модуль Devise для блокировки пользователей после некоторых неудачных попыток входа в систему. Используйте Rack :: Attack для ограничения количества запросов по вашему выбору. Или добавьте Captchas на страницу входа / регистрации / разблокировки, используя это расширение Devise .

A3 XSS

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

Давайте посмотрим на первый пример из Discourse, который имеет функцию опроса с всплывающими окнами в пользовательском интерфейсе. Этот запрос показывает, что важно знать, кто несет ответственность за побег. Обычно это мнение. Однако в этом случае приложение отображает JSON, который будет экранирован только в соответствии с контекстом JSON. Но сообщения об ошибках будут использоваться в контексте HTML, и кажется, что этот уровень представления не исчезает (или не может).

В следующем примере мы рассмотрим использование html_safeи метод, raw()который ведет себя очень похоже.

Этот код в строке 15 до изменения не ошибается:

= "To: ".html_safe << email.sent_to

Но это требует некоторых знаний о том, как работает SafeBuffer . Каков результат HTML-безопасной строки << небезопасной? Вы можете ожидать небезопасную строку. На самом деле это будет HTML-безопасная строка, но правая часть будет экранирована.

Так что в этом примере все правильно, но немного сложнее, и кто-то может поэкспериментировать с другими .html_safeили raw()звонками в будущем. Поэтому разделение и использование меньших затрат .html_safe— это хорошая инвестиция в ремонтопригодность

Фактическая уязвимость в этом коде была в строке 1 (см. Ниже) в сочетании со строкой 25 :

L1: truncated = truncate(email.body_without_textile.to_s.gsub("\n", " "), :length => 125 - email.subject.to_s.size) 
L25: %tt= (" - " << truncated).html_safe

При этом используется truncate () , который не был сброшен в той версии Rails 3.2.8, но помечал строку как небезопасную HTML. В последней версии Rails он избегает усеченной строки. Строка 25 затем использует <<обозначения, возможно, в надежде, что правая сторона ( truncated) будет экранирована. Однако две сцепленные строки, небезопасные для HTML, приводят к небезопасной строке без экранирования. Затем он был помечен как HTML-безопасный и, следовательно, не избежал в результате.

Из этого мы узнали следующее:

  • Вам понадобится стратегия для raw()метода и .html_safe. Как и где это должно быть разрешено использовать? Вы должны быть на 100 процентов уверены, что строка не содержит код от пользователя. Лучше избегать использования .html_safeили, raw()когда это возможно, и избегать слишком часто.
  • При использовании Rails (или собственных) строковых помощников убедитесь, что вы знаете, что они делают и изменились ли они при переходе на новую версию. Например, сравните truncate()в 3.2.8 и 4.2 .
  • Кто несет ответственность за побег? Обычно это должен быть уровень презентатора, поскольку его необходимо экранировать в соответствии с контекстом (HTML, JSON,…). Однако, если представление не исчезает (или не может выйти), вам, возможно, уже придется скрыться в модели.

действия

  • Найдите проект .html_safeили raw()уменьшите использование везде, где это возможно.
  • Сделайте ваши собственные текстовые помощники экранированными, чтобы вы могли безопасно использовать их в представлении.
  • Добавьте (возможно) неожиданное поведение текстовых помощников в свою центральную политику безопасности, например, в файл SECURITY.md. Кроме того, опишите .html_safeи raw()стратегию использования и стратегию «кто несет ответственность» там.

Авторизация А4

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

Вот пример:

  • Модель Пользователь принимает вложенные атрибуты для разрешения пользователя: accepts_nested_attributes_for :permission.
  • Модель Разрешение имеет флаги для того, что разрешено пользователем, например , add_users.
  • Контроллер использует User.update_attributes(params[:user])в действии обновления, которое предназначено как для администраторов, так и для обычных пользователей.
  • Даже если в пользовательском интерфейсе нет флажка, обычный пользователь может просто добавить форму, чтобы получить это право администратора. Это означает, что вы должны авторизовать изменения, которые поступают через .<input type="checkbox" name="user[permission_attributes][add_users]"
    value="1" checked="checked" />
    params[:user][:permission_attributes]

Как бы ни было очевидно в этом примере, эта проверка часто забывается. Поэтому аудит вашего кода на наличие подобных проблем — это хорошая инвестиция.

действия

  • Ищите свой код, accepts_nested_attributes_forкоторый часто вызывает проблемы авторизации как это. Старайтесь избегать использования этого метода, когда вы можете.
  • Это также иногда происходит в дочерних объектах (скажем, в комментарии к сообщению) в сочетании с двумя пользователями с одинаковой ролью. В CommentsController я также должен убедиться, что этот комментарий действительно принадлежит этому сообщению (так же как сообщение принадлежит этому пользователю).
  • Важно, чтобы авторизация была понятной и поддерживаемой. В качестве упражнения представьте, что вы начинаете работу в новой компании и смотрите на центральный файл аутентификации . Это понятно и понятно для вас? Если нет, сравните его с вашей схемой авторизации и, возможно, немного поделите.
  • Является ли ваш фильтр авторизации центральным методом перед каждым действием (например, load_and_authorize_resource в ApplicationController) или что-то, что разработчик должен добавить вручную для каждого действия? Последнее иногда увеличивает риск того, что оно не будет добавлено к новым «внутренним» действиям.
  • Планирование наихудшего сценария: кто-то получает доступ к учетной записи администратора, прослушивая cookie-файл сеанса или даже пароль. Убедитесь, что злоумышленник не может сделать слишком много в приложении в этом случае. Например, требуется повторно ввести пароль или одноразовый защитный код ( например , через :paranoid_verification).

A5 Неправильная настройка безопасности

Будет ли ваше Rails-приложение неправильно настроено или нет, во многом зависит от того, что оно делает. Но есть несколько общих параметров конфигурации, которые вы можете проверить.

действия

  • По умолчанию Rails будет отправлять новые заголовки HTTP для большей безопасности . В старых версиях Rails вы можете использовать гем SecureHeaders, чтобы сделать то же самое, и даже в ваших современных Rails вы можете добавить еще больше заголовков. Полезно знать, что делают по умолчанию и другие, поэтому обратитесь к странице с гемом для более подробного объяснения.
  • Возможно, вы знаете массив Rails.application.config.filter_parameters . Потратьте минуту или две, чтобы посмотреть, что действительно все чувствительные параметры добавлены здесь. По закону может потребоваться хранить личные сообщения или данные пациента в как можно меньшем количестве мест или, по крайней мере, внутри страны (если вы хотите использовать службы агрегации журналов из других стран). Обратите внимание, что :passwordэто также будет фильтровать :password_confirmation, поэтому вам не нужно перечислять все варианты.
  • Если приложение перенаправляет на адрес, который включает токены, вы также должны добавить их в массив Rails.application.config.filter_redirect .
  • Кроме того, вы должны убедиться, что ваши источники драгоценных камней HTTPS, а не git://или :github. Смотрите здесь для более подробной информации.

A6 Чувствительная передача данных

Кто-то рядом с пользователем в кафе может подслушать передачу данных. Так что если можете, сделайте все приложение только для SSL. Если доступны как HTTPS, так и HTTP-URL-адреса, посредник может удалить все защищенные ссылки в фоновом режиме, чтобы жертва оставалась в версии HTTP.

Другая проблема заключается в том, что браузер не знает, что ваше приложение только для SSL. Поэтому более искушенный злоумышленник может предоставить пользователю версию HTTP, перенаправив весь трафик на версию HTTPS в фоновом режиме. Вот почему существует HSTS, и вы, возможно, читали об этом на странице гема SecureHeaders в разделе A5 выше.

действия

  • Также пометьте все файлы cookie как «Безопасные», чтобы браузер пользователя не отправлял (сеансовый) файл cookie в незащищенном HTTP-запросе (который затем перенаправит ее на версию HTTPS).
  • Установите напоминание, чтобы повторно проверять безопасность TLS / SSL на наличие последних уязвимостей.
  • Это также относится и к безопасному хранению, поскольку сохраняемые объекты могут быть переданы позже, например, через службу агрегирования журналов или в резервных копиях. Поэтому шифруйте личную информацию на уровне приложения, где вы можете, например, использовать attire_encrypted .
  • Выключите автозаполнения в чувствительных форм входов: .<input
    type="email" name="email" autocomplete="off" />

A7 Отсутствует контроль доступа на уровне функций

Примечание. Этот раздел в первой десятке OWASP напоминает нам о том, что мы уже упоминали в разделах A2 и A4: перепроверяйте аутентификацию и авторизацию в каждом действии, особенно в скрытых или внутренних действиях.

A8 Подделка межсайтовых запросов (CSRF)

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

Предоставляете ли вы функцию аутентификации «помни меня»? Давайте проверим, что произойдет, если вы войдете в систему с установленным флажком «запомнить меня» и инцидентом с CSRF. Перейдите к форме, которая создает что-то, заполните ее и удалите маркер подлинности с помощью консоли разработчика (первый элемент в форме, или, если это удаленная форма, метатег с именем csrf-token), отправьте ее и посмотрите на бревне рельсов. Следует сказать «не удалось проверить токен подлинности», но также не создавать этот объект. Тем не менее, если объект из формы был создан, вы обнаружили довольно распространенную уязвимость CSRF.

Что произошло? Неправильный токен заставил Rails выйти из текущего пользователя, очистив или возобновив сеанс (зависит от protect_from_forgeryконфигурации в ApplicationController, protect_from_forgery with: :exceptionведет себя по-разному). Но функция «запомнить меня» в отдельном файле cookie снова зарегистрировала вас, а затем запустила действие.

действия

  • Перезапись наhandle_unverified_request метод в ApplicationController.
  • Запустите исходную реализацию ( например , с помощью super), но также удалите этот файл cookie «запомнить меня».

Это вторая обычная проблема: что касается API, в документации Rails говорится: «Предположительно, у вас там будет другая схема аутентификации». Хороший вопрос, поэтому давайте удостоверимся, что пользовательский сеанс основного приложения не работает в API, где, по-видимому, отсутствует защита CSRF. Если нет, войдите в основное приложение, введите URL-адрес API в браузере и посмотрите, аутентифицирует ли вас API.

Если это работает, злоумышленник может запустить CSRF-атаку на API. Используйте инструмент REST для тестирования действий API, не относящихся к GET, а затем используйте другую схему аутентификации для API, которая не использует куки.

действия

  • Я знаю, что вы уже проверили это, но давайте потратим минуту, чтобы убедиться, что никакие маршруты в выходных данных не rake routes | grep GETменяют состояние приложения. Если так, возможно ли преобразовать их в не-GET, чтобы использовать защиту CSRF от Rails?
  • Документация Rails немного вводит в заблуждение относительно запросов для разных форматов. По умолчанию они также будут проверены в основном приложении. Поэтому перед вами убедитесь, что действие не изменяет, не удаляет и не создает что-либо (существенное).skip_before_action
    :verify_authenticity_token

A9 Использование компонентов с известными уязвимостями

У нас не всегда есть время, чтобы немедленно обновить программное обеспечение. Неделя со стратегией безопасности Rails может быть спасением.

действия

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

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

    redirect_to Hash[params].merge(:cookie_test => "true")

    Есть веская причина, которая redirect_to paramsбольше не допускается. Если передана строка, он будет перенаправлен на этот URL. Если передан Hash, он все еще уязвим, потому что url_for предоставляет :hostопцию.

    действия

    • Если вы проверяете URL-адреса, например, для веб-сайта пользователя, убедитесь, что вы отфильтровали некоторые менее известные схемы: data:text/html;charset=utf-8,://<script>alert(1)</script>в качестве ссылки будет запускаться этот HTML / JS и проверяется как URL-адрес, если вы просто проверяете включение ://. То же самое относится и к этой ссылке: javascript:alert('://').
    • Перенаправления разрешают полные URL-адреса в качестве параметра, поэтому проверьте строки перенаправления. Тем не менее, redirect_to params[:return_to] unless params[:return_to] =~ /\Ahttp/пытается запретить внешние URL, но это позволит относительные схемы URL, как //attacker.com. Эта проверка может помочь: params[:return_to] =~ %r(\A#{root_url}).
    • Проверьте все redirect_tos, которые принимают Hash, где пользователь может передать :hostопцию.

    Вывод

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

    Мы также разработали общие правила кодирования, чтобы прошлые проблемы не повторялись. Опишите эти правила кодирования и наиболее распространенные атаки и уязвимости, относящиеся к вашему приложению, в центральном местоположении, например, в файле SECURITY.md. В этом файле также добавьте методы Rails, которые требуют дополнительного внимания, поскольку они ведут себя немного иначе, чем ожидалось. Не забудьте описать свою стратегию для метода html_safe (см. A2) и против внедрения SQL в этом файле. Еще одна хорошая стратегия — задавать вопросы при непосредственном использовании пользовательского ввода, например: «Какое может быть это значение?» Может ли это быть строка инъекции, неожиданный (тип) объект, идентификатор другого пользователя, ноль, 0, пустая строка, хэш или массив?

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