Безопасные URL-адреса скрепки только для защищенных страниц

Я пытаюсь найти лучший способ сделать URL-адреса скрепки безопасными, но только для защищенных страниц.

Например, главная страница, на которой показаны изображения, хранящиеся в S3, имеет вид http://mydomain.com, а URL-адрес изображения — < a href="http://s3.amazonaws.com/mydomainphotos/89/thisimage.JPG?1284314856" rel="noreferrer">http://s3.amazonaws.com/mydomainphotos/89/thisimage.JPG?1284314856< /а>.

У меня есть защищенные страницы, такие как https://mydomain.com/users/my_stuff/49, на которых есть изображения хранится в S3, но протокол S3 — это http, а не https, поэтому пользователь получает предупреждение от браузера о том, что некоторые элементы на странице не защищены, бла-бла-бла.

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

Один (вероятно, плохой) способ - создать новый метод URL-адреса, например:

def custom_url(style = default_style, ssl = false)
  ssl ? self.url(style).gsub('http', 'https') : self.url(style)
end

Следует отметить, что я использую плагин ssl_requirement, поэтому может быть способ связать его с ним.

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


person Shagymoe    schedule 22.09.2010    source источник
comment
Привет Shagymoe ... Мне очень интересно узнать, какое было ваше окончательное решение :)   -  person zetarun    schedule 29.09.2011
comment
Проблема github Paperclip здесь: github.com/thoughtbot/paperclip/issues/387   -  person swrobel    schedule 01.03.2012


Ответы (4)


Если кто-то наткнется на это сейчас: в Paperclip есть решение, так как апрель 2012 г.! Просто напишите:

Paperclip::Attachment.default_options[:s3_protocol] = ""

в инициализаторе или используйте опцию s3_protocol внутри вашей модели.

Спасибо @Thomas Watson за инициативу.

person emrass    schedule 14.06.2012
comment
Теперь это официальный способ создания URL-адресов без схемы. - person Karew; 10.10.2012
comment
Убедитесь, что вы используете версию 3.1.4 или выше. В более ранних версиях есть ошибка, включающая двоеточие протокола, которое прерывает все ваши ссылки на изображения. Кроме того, обязательно перезагружайте сервер при обновлении версий. - person vansan; 30.07.2013
comment
См. также rubydoc.info/github/thoughtbot/paperclip/Paperclip/Storage/ S3 - person Topher Hunt; 20.11.2014
comment
Это сработало для меня с скрепкой 3.0.4, поместив строку в инициализатор скрепки, но не в модель. - person Nick Ellis; 29.03.2018
comment
Спасибо за отзыв, @NickEllis. Я подожду, пока кто-нибудь подтвердит, а затем обновлю ответ, если потребуется. - person emrass; 03.04.2018

Если вы используете Rails 2.3.x или новее, вы можете использовать промежуточное ПО Rails для фильтрации ответа перед отправкой обратно пользователю. Таким образом, вы можете определить, является ли текущий запрос HTTPS-запросом, и соответствующим образом изменить вызовы s3.amazonaws.com.

Создайте новый файл с именем paperclip_s3_url_rewriter.rb и поместите его в каталог, который загружается при запуске сервера. Каталог lib будет работать, но многие предпочитают создать каталог app/middleware и добавить его в путь загрузки приложения Rails.

Добавьте в новый файл следующий класс:

class PaperclipS3UrlRewriter
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    if response.is_a?(ActionController::Response) && response.request.protocol == 'https://' && headers["Content-Type"].include?("text/html")
      body = response.body.gsub('http://s3.amazonaws.com', 'https://s3.amazonaws.com')
      headers["Content-Length"] = body.length.to_s
      [status, headers, body]
    else
      [status, headers, response]
    end
  end
end

Затем просто зарегистрируйте новое промежуточное ПО:

Rails 2.3.x: добавьте приведенную ниже строку в environment.rb в начале блока Rails::Initializer.run.
Rails 3.x: добавьте приведенную ниже строку в application.rb в начале класса Application.

config.middleware.use "PaperclipS3UrlRewriter"

ОБНОВЛЕНИЕ:
я только что отредактировал свой ответ и добавил проверку для response.is_a?(ActionController::Response) в операторе if. В некоторых случаях (возможно, связанных с кэшированием) объект ответа представляет собой пустой массив (?) и, следовательно, терпит неудачу, когда к нему вызывается request.

ОБНОВЛЕНИЕ 2: я отредактировал приведенный выше пример кода Rack/Middleware, чтобы также обновить заголовок Content-Length. В противном случае тело HTML будет обрезано большинством браузеров.

person Thomas Watson    schedule 05.01.2011
comment
Кажется неудачным вводить средство перезаписи URL-адресов S3 в каждый отдельный запрос, который обслуживает ваше приложение, вместо того, чтобы инкапсулировать логику для создания правильных URL-адресов в первую очередь. - person Winfield; 26.10.2011
comment
Действительно, @Winfield, оценка несвязанного регулярного выражения в каждом теле ответа выполняется в обратном порядке. Используйте решение, которое в первую очередь генерирует правильные URL-адреса. - person Mars; 10.08.2015

Используйте следующий код в классе контроллера:

# locals/arguments/methods you must define or have available:
#   attachment - the paperclip attachment object, not the ActiveRecord object
#   request - the Rack/ActionController request
AWS::S3::S3Object.url_for \
  attachment.path,
  attachment.options[:bucket].to_s,
  :expires_in => 10.minutes, # only necessary for private buckets
  :use_ssl => request.ssl?

Конечно, вы можете красиво обернуть это в метод.

person yfeldblum    schedule 05.01.2011

К сведению: некоторые из приведенных выше ответов не работают с Rails 3+, потому что ActionController::Response устарел. Используйте следующее:

class PaperclipS3UrlRewriter
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    if response.is_a?(ActionDispatch::BodyProxy) && headers && headers.has_key?("Content-Type") && headers["Content-Type"].include?("text/html")
    body_string = response.body[0]
    response.body[0] = body_string.gsub('http://s3.amazonaws.com', 'https://s3.amazonaws.com')
    headers["Content-Length"] = body_string.length.to_s
    [status, headers, response]
  else
    [status, headers, response]
  end
end

конец

И убедитесь, что вы добавили промежуточное ПО в правильное место в стеке (я добавил его после Rack::Runtime).

config.middleware.insert_after Rack::Runtime, "PaperclipS3UrlRewriter" 
person Eyal Kedem    schedule 20.02.2014