2 votes

Rails : Obfuscating Image URLs on Amazon S3 (problème de sécurité)

Pour faire court, il suffit de dire que mon application Rails permet aux utilisateurs de télécharger dans l'application des images qu'ils souhaitent conserver. en l'application (c'est-à-dire pas de hotlinking).

J'essaie donc de trouver un moyen d'obscurcir les URL des images de sorte que l'adresse de l'image dépende du fait que l'utilisateur soit connecté ou non au site, de sorte que si quelqu'un essayait de faire un hotlinking vers l'image, il obtiendrait une erreur 401 d'accès refusé.

Je pensais que si je pouvais acheminer la requête à travers un contrôleur, je pourrais réutiliser une grande partie de l'autorisation que j'ai déjà intégrée à mon application, mais je suis bloqué.

Ce que je souhaite, c'est que mes images soient accessibles via une URL vers l'un de mes contrôleurs, par exemple :

http://railsapp.com/images/obfuscated?member_id=1234&pic_id=7890

Si l'utilisateur clique avec le bouton droit de la souris sur l'image affichée sur le site web et sélectionne "Copier l'adresse", puis la colle, il s'agira de la MÊME URL (c'est-à-dire qu'elle ne trahira pas l'endroit où l'image est hébergée).

L'image réelle se trouve sur une URL comme celle-ci :

http://s3.amazonaws.com/s3username/assets/member_id/pic_id.extension

Est-ce possible ? Peut-être en utilisant la fonction render méthode ? Ou autre chose ? Je sais qu'il est possible pour PHP de renvoyer les bons en-têtes pour faire croire au navigateur qu'il s'agit d'une image, mais je ne sais pas comment faire cela dans Rails...

MISE À JOUR : Je veux que tous les utilisateurs de l'application puissent voir les images si et SEULEMENT s'ils sont actuellement connectés au site. Si l'utilisateur n'a pas de session active sur le site, l'accès direct aux images doit produire une image générique ou un message d'erreur.

2voto

Andrew Aylett Points 16469

S3 vous permet de construire des chaînes de requête pour les demandes qui permettent un téléchargement limité dans le temps d'un objet autrement privé. Vous pouvez générer l'URL de l'image de manière unique pour chaque utilisateur, avec un court délai d'attente pour éviter la réutilisation.

Voir la documentation Pour plus d'informations, consultez la section "Query String Request Authentication Alternative" (alternative à l'authentification par la chaîne de requête). Je ferais bien un lien direct, mais le javascript qui détruit les cadres l'empêche.

2voto

mar Points 256

Les images doivent-elles être accessibles uniquement à cet utilisateur ou voulez-vous les rendre accessibles à un groupe d'utilisateurs (amis) ?

En tout état de cause, si vous souhaitez mettre fin au hotlinking, vous devez pas stocker les fichiers images sous DocumentRoot de votre serveur web.

Dans le premier cas, vous pouvez stocker l'image sur le serveur en tant que MD5(nom_du_fichier_comme_exposé_à_l'utilisateur + nom_d'utilisateur_logué_de_la_cookie). Lorsque l'utilisateur demande image_nom_du_fichier_comme_exposé_à_l'utilisateur, dans votre application rails, construisez le nom de fichier de l'image comme indiqué précédemment, puis ouvrez le fichier dans l'application rails et écrivez-le (après avoir défini de manière appropriée le Content-Type dans l'en-tête de la réponse). Ceci est sécurisé par conception.

Si l'image est destinée à être partagée avec des amis, vous ne devez pas incorporer le nom d'utilisateur dans le nom de fichier construit, mais le reste des conseils devrait fonctionner.

2voto

JGW Maxwell Points 21

Il est tard pour répondre, mais une autre option serait de stocker les fichiers dans le GridFS de MongoDB, servis par une partie du Middleware Rack qui nécessite la transmission d'une authentification. C'est à peu près aussi sûr que vous le souhaitez, et les URL n'ont même pas besoin d'être obscurcies.

L'autre avantage réside dans la disponibilité des fichiers et l'évolutivité future du système.

1voto

neezer Points 6779

Merci pour vos réponses, mais je reste sceptique quant à l'efficacité du "timing out" de l'URL d'Amazon.

J'ai mis à jour ma question ci-dessus pour être un peu plus clair sur ce que j'essaie de faire et d'éviter.

Après quelques expériences, j'ai trouvé un moyen de faire ce que je veux dans mon application Rails, bien que cette solution ne soit pas sans inconvénients. En fait, ce que j'ai fait, c'est de construire mon fichier image_tag avec une URL qui pointe vers un contrôleur, et prend un élément path paramètre. Ce contrôleur vérifie d'abord si l'utilisateur est autorisé à voir l'image, puis il récupère le fichier contenu de l'image dans une demande séparée, et stocke la contenu dans une variable d'instance, qui est ensuite transmise à un repond_to pour renvoyer l'image, ce qui permet d'obscurcir l'URL de l'image (puisque cette requête est faite séparément).

Cons :

  1. Ajoute du temps à la demande (j'estime que le temps supplémentaire nécessaire pour effectuer cette double demande est acceptable compte tenu de la protection de la vie privée que cette méthode m'offre).
  2. Ajoute un peu d'encombrement aux vues et aux itinéraires (une petite quantité, peut-être un peu plus que je ne le souhaiterais)
  3. Si l'utilisateur est autorisé et tente d'accéder directement à l'image, celle-ci est téléchargée immédiatement au lieu d'être affichée dans le navigateur (quelqu'un sait-il comment résoudre ce problème ? Modifier les en-têtes HTTP ? Cela ne semble possible qu'avec l'option jpg Mais...)
  4. Vous devez créer une vue distincte pour chaque format de fichier que vous avez l'intention de servir (deux pour moi, jpg y png )

Y a-t-il d'autres inconvénients ou considérations dont je devrais tenir compte avec cette méthode ? Jusqu'à présent, je peux vivre avec ce que j'ai énuméré...

(Refonte bienvenue.)


application_controller.rb

class ApplicationController < ActionController::Base

  def obfuscate_image
    respond_to do |format|
      if current_user
        format.jpg { @obfuscated_image = fetch_url "http://s3.amazonaws.com/#{Settings.bucket}/#{params[:path]}" }
      else
        format.png { @obfuscated_image = fetch_url "#{root_url}/images/assets/profile/placeholder.png" }
      end
    end
  end

  protected

  # helps us fetch an image, obfuscated
  def fetch_url(url)
    r = Net::HTTP.get_response(URI.parse(url))
    if r.is_a? Net::HTTPSuccess
      r.body
    else
      nil
    end
  end
end

views/application/obfuscate_image.png.haml & views/application/obfuscate_image.jpg.haml

= @obfuscated_image

routes.rb

map.obfuscate_image 'obfuscate_image', :controller => 'application', :action => 'obfuscate_image'

config/environment.rb

Mime::Type.register "image/png", :png
Mime::Type.register "image/jpg", :jpg

Appeler une image obscurcie

= image_tag "/obfuscate_image?path=#{@user.profile_pic.path}"

0voto

John Topley Points 58789

Le problème que vous rencontrez est que, pour autant que je sache, les images sur S3 doivent être lisibles dans le monde entier pour être accessibles. À un moment donné du processus, un HTTP GET devra être exécuté pour récupérer l'image, ce qui exposera l'URL réelle aux outils qui peuvent renifler HTTP, tels que Firebug.

Par ailleurs, 37signals ne considère pas qu'il s'agisse d'un problème majeur, car si je visualise une image dans mon compte Backpack privé, je peux voir l'URL S3 publique dans la barre d'adresse du navigateur. Votre kilométrage peut varier...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X