Защита содержимого публики/в приложении Rails

Я поддерживаю приложение Rails, которое имеет контент в общедоступной папке, которая теперь должна быть защищена логином. Мы рассматриваем возможность перемещения этих папок с файлами в путь за пределами public/и запись контроллера Rails для обслуживания содержимого.

Прежде чем мы начнем писать это, мне было любопытно, если кто-то еще столкнулся с такой проблемой? Я искал некоторые драгоценные камни/плагины, которые уже могли это сделать, но ничего не нашли. Кто-нибудь создал камень для этого?

Ответ 1

Я сделал это на сайте, где люди платят за скачивание определенных файлов, а файлы хранятся в RAILS_ROOT/private. Первое, что нужно знать, это то, что вы хотите, чтобы веб-сервер обрабатывал отправку файла, иначе ваше приложение будет поддерживать передачу больших файлов, и это быстро остановит ваш сайт, если у вас есть какой-либо объем загрузки. Итак, если вам нужно проверить авторизацию в контроллере, вам также нужен способ передать управление загрузкой обратно на веб-сервер. Лучший способ сделать это (что я знаю) - это заголовок X-Sendfile, который поддерживается Nginx, Apache (с модулем) и другие. При настройке X-Sendfile, когда ваш веб-сервер получает заголовок X-Sendfile из вашего приложения, он берет на себя отправку файла клиенту.

Как только у вас есть X-Sendfile, работающий на вашем веб-сервере, вам может быть полезен метод частного контроллера:

##
# Send a protected file using the web server (via the x-sendfile header).
# Takes the absolute file system path to the file and, optionally, a MIME type.
#
def send_file(filepath, options = {})
  options[:content_type] ||= "application/force-download"
  response.headers['Content-Type'] = options[:content_type]
  response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filepath)}\""
  response.headers['X-Sendfile'] = filepath
  response.headers['Content-length'] = File.size(filepath)
  render :nothing => true
end

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

##
# Private file download: check permission first.
#
def download
  product = Product.find_by_filename!(params[:filename])
  if current_user.has_bought?(product) or current_user.is_superuser?
    if File.exist?(path = product.filepath)
      send_file path, :content_type => "application/pdf"
    else
      not_found
    end
  else
    not_authorized
  end
end

Очевидно, что ваш метод авторизации будет отличаться, и вам нужно будет изменить заголовки, если вы предлагаете файлы, отличные от PDF файлов, или хотите, чтобы файл просматривался в браузере (избавиться от типа содержимого application/force-download).

Ответ 2

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

Проверьте этот URL: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html

Ответ 4

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

Если вы посмотрите на более простой подход входа, вы можете обрабатывать его с помощью протокола HTTP Auth и настроек в вашей среде хостинга (например, с помощью htaccess).

Ответ 5

Предоставление файла с непредсказуемым URL - это простое решение, которое в настоящее время используется в некоторых производственных системах.

Например: GitLab. Следующий образ был загружен в проблему частного репозитория https://gitlab.com/cirosantilli/test-private/issues/1, но вы все еще можете его увидеть:

Обратите внимание, что непокрываемый префикс 90574279de автоматически добавляется в URL-адрес.

Bitbucket (non-Rails) также использует эту технику.