Interdire l'accès aux actions par des url directes

Bonjour.

Je souhaiterais pouvoir empêcher l’accès direct à toutes ou partie des
actions de mes contrôleurs. Par exemple, s’il existe un contrôleur
Item, proposant une méthode/action publique add, je voudrais qu’il
soit impossible pour un utilisateur de déclencher cette action en
validant « à la main » une url du type …/item/add/1234 dans son
navigateur. Seul l’accès interne serait autorisé (par déclenché en
interne, je veux dire, que ce soit en réponse à un clic ou par une
autre méthode, etc.)

L’idée brutale de passer de proche en proche une clé de vérification
me semble assez mauvais, aussi je viens quémander des suggestions :slight_smile:
Peut-être existe t-il un plugin pour ce genre de chose ?

Merci bien.

jd a écrit :

L’idée brutale de passer de proche en proche une clé de vérification
me semble assez mauvais, aussi je viens quémander des suggestions :slight_smile:
Peut-être existe t-il un plugin pour ce genre de chose ?

Tu peux forcer à ce que l’action soit en POST. Ainsi, seul un formulaire
pourra soumettre l’info. Bien-sûr rien ne t’assure que ca soit un de tes
formulaires.


Cyril M.

Salut,
http://apidock.com/rails/ActionController/Filters/ClassMethods

Nicolas.

Before_filter est ton ami: tu définis dedans tes règles d’accès (ici ce
sera
l’existante d’une referrer) puis tu renvoies true pour autorisé, false
pour
recalé.

2009/7/7 jd [email protected]

Pwned :cry:

2009/7/7 slainer68 [email protected]

jd a écrit :

referer=quelquechose dans l’url ne trompe pas Rails.

Dans un contrôleur en particulier,
skip_before_filter :access_control, :only => :index permet par exemple
de ne pas appliquer ce filtre pour l’action index. Ainsi, un
utilisateur pourra accéder à l’index en tapant l’url directe, mais ne
pourra pas déclencher directement une action comme add ou remove, par
exemple. Au programme de donner ou refuser l’accès interne vers tel ou
tel action.

L’ennui, c’est qu’on ne peut que très peu se fier au http_referer

Quand tu testes si un referer existe, il me suffit de créer une page
avec le lien vers ton site inventory/add/123, tu auras une valeur dans
ta variable referer, et ça passera.

Même si tu testes le contenu de ce referer, vu que l’information est
envoyée par le navigateur, l’utilisateur peut te faire croire que
l’action vient bien de ton site en manipulant son navigateur.


Julien.

Merci. J’avais dans l’idée d’utiliser un before_filter mais je ne
connaissais pas (!) l’existence du HTTP_REFERER. La base…

Quelques détails, ça pourra peut-être servir à quelqu’un : je me suis
créé un before_filter :access_control dans l’ApplicationControler
(dont dérivent tous les autres contrôleurs). Exécuté avant toute
action, il vérifie entre autre chose qu’un referer = request.referer
existe. Si oui, c’est que l’action courante a été déclenchée « en
interne » et on ne bloque rien. Si non, c’est qu’on a affaire à une
tentative d’accès direct par l’url. À noter qu’ajouter un ?
referer=quelquechose dans l’url ne trompe pas Rails.

Dans un contrôleur en particulier,
skip_before_filter :access_control, :only => :index permet par exemple
de ne pas appliquer ce filtre pour l’action index. Ainsi, un
utilisateur pourra accéder à l’index en tapant l’url directe, mais ne
pourra pas déclencher directement une action comme add ou remove, par
exemple. Au programme de donner ou refuser l’accès interne vers tel ou
tel action.

C’est un peu plus sévère que le scoping, qui consiste essentiellement
à vérifier qu’un utilisateur a le droit de réaliser une action
(typiquement une requête bdd). Par exemple, la version “scoped” de
@parametres = User.find(params[:id]) serait @truc =
@user.parametres.find(params[:id]) avec un contrôle sur @user
(identifié ou non etc.)

Dans mon cas, en effet, je voulais de façon plus générale contrôler
l’accès via url directe, sans nécessairement considérer l’aspect
authentification. Mais couplé à l’authentification et, par exemple, à
un plugin d’autorisation (gestion de rôles), ça permet de contraindre
assez finement les accès. Par exemple, vous pouvez avoir écrit une
application dans laquelle un utilisateur donné possède un inventaire,
mais ne doit pas pouvoir y ajouter n’importe quel objet en gruikant
avec l’url (« tiens, si j’essayais inventory/add/123 » alors qu’il n’a
pas le droit à l’objet d’id 123, que ce soit en général ou dans un
contexte particulier lié à l’exécution courante du code). Le
contrôleur d’inventaire se charge de savoir, via le model utilisateur
par exemple, quels objets sont susceptibles d’être manipulés librement
par cet utilisateur à cet instant t donné, et la view n’affiche les
actions add que pour ces objets. Le reste devient inaccessible, en
accès direct ou interne.

Si une meilleure façon de faire existe sur ce genre de problème, je
suis preneur :slight_smile:

Merci pour le coup de pouce en tout cas !

jd a écrit :

referer=quelquechose dans l’url ne trompe pas Rails.

Il y a aussi certains navigateurs qui n’envoient pas du tout de
HTTP_REFERER


Julien.

Bien noté toutes les remarques. Je vais regarder ce qui touche au
forgery : j’ai déjà lu quelques trucs sur le sujet, mais ça reste
vague pour moi…

et sinon par le biais de méthode privées ce n’est pas possible ? ça
permettrai de garantir que les méthodes ne sont effectuées que par le
“logiciel” et pas par l’utilisateur.

gUI

Le 7 juillet 2009 17:16, Julien R. [email protected] a écrit :

interne » et on ne bloque rien. Si non, c’est qu’on a affaire à une


Julien.


Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

2009/7/7 jd [email protected]

tentative d’accès direct par l’url. À noter qu’ajouter un ?
C’est un peu plus sévère que le scoping, qui consiste essentiellement
assez finement les accès. Par exemple, vous pouvez avoir écrit une

recalé.

soit impossible pour un utilisateur de déclencher cette action en
validant « à la main » une url du type …/item/add/1234 dans son
navigateur. Seul l’accès interne serait autorisé (par déclenché en
interne, je veux dire, que ce soit en réponse à un clic ou par une
autre méthode, etc.)

L’idée brutale de passer de proche en proche une clé de vérification
me semble assez mauvais, aussi je viens quémander des suggestions :slight_smile:
Peut-être existe t-il un plugin pour ce genre de chose ?

si tu veux être sur de bloquer ca, il te faut en effet vérifier si
l’user a
les bons droits tant pour afficher les liens d’ajout que pour leur
réelle
execution.

Si tu tiens aussi a bloquer les accès ‘brutaux’ en rentrant l’url dans
le
navigateur, je te conseille d’aller plutot du côté du forgery protection
avec toutes tes opérations effectuant une écriture (création,
modification,
effacement) passant obligatoirement en post/put/delete. Tu es sûr à ce
moment là que la page vient bien légitimement de la page qui précède.


http://fabien.jakimowicz.com

Si une action est déclenchée via le navigateur de l’internaute, celui-
ci pourra toujours “tricher” sur les requêtes. Avec les bons plugins
dans Firefox, on peut facilement simuler n’importe quelle requête GET/
POST avec les referer/locale/user-agent… que l’on souhaite.

Même :protect_from_forgery est contournable : j’ai déjà eu affaire à
un bot qui validait mes formulaires.

Donc les seules solutions fiables (comme dit plus haut) :

  1. Limiter les actions des routes, exemple :

Ni création, ni suppression d’objets “Configuration”

map.resources :configurations, :except => [:new, :create, :destroy]

  1. Mettre un système d’authentification : plusieurs gems/plugins sont
    disponibles, autrement ça peut être du fait-maison.

  2. Mettre un système de droits, et resteindre les actions autorisées
    dans les contrôleurs, exemple :

Seul un admin pourra utiliser l’action destroy

before_filter :check_user_is_admin, :only => [:destroy]

  1. Coder RESTful ses actions custom, cela évite beaucoup de fausses
    manipulations. Exemple :

Mon action supprime des choses, donc j’évite à tout prix le :get, je

préfère du :delete
map.remove_item_associations ‘/items/:id/
remove_assocations’, :conditions => { :method => :delete }

Julien

Et en testant request.host_with_port ?

Le 7 juillet 2009 23:37, Meshak [email protected] a écrit :

Une note : j’ai testé l’affaire en suivant les indications de
Securing Rails Applications — Ruby on Rails Guides et
http://baseunderattack.com/2008/04/18/ruby-on-rails-and-csrf-protection/,
et Rails semble bien distinguer ce qui a été déclenché par une url
directe avec manipulation du referer, de ce qui a été déclenché en
interne avec referer « local » à l’application. Comme ce n’est
vraiment pas ma spécialité, je ne saurais dire si ça tient la route,
mais je ne vois pas trop comment faire autrement.

En fait, faute d’avoir réussi à bien l’expliquer, je pense que
personne n’a vraiment compris mon problème :wink: parce que Mais je l’ai
résolu, avec une méthode pas du tout originale, mais je n’y avais pas
pensé plus tôt à force de vouloir faire des trucs RESTful-truc
standard ^^ Plutôt que de contrôler a posteriori l’url, le referer,
etc. sur la base d’info fondamentalement pas fiables, j’ai mis en
place un around_filter :context_checker qui vérifie l’existence d’un «
contexte » d’exécution pour une action donnée. Si le contexte est
présent, l’action est exécutée, sinon, on gère l’erreur. Le contexte
est remis à zéro après chaque action pour laquelle il a été demandé
d’effectuer context_checker.

Typiquement, le contexte est initié par la méthode index d’un
contrôleur (requête auprès du modèle, qui retourne juste ce qu’il
faut), mais tout est possible. Il consiste en un hash associant des id
autorisés à des tokens aléatoires (base64 de
ActiveSupport::SecureRandom). On peut avoir un token commun, ou bien
un token par groupe d’id, ou bien un token par id, selon la finesse de
contrôle souhaitée. Ce hash est disponible dans la view associée à
l’action créant le contexte (index.haml par exemple) : la view se
charge de faire en sorte que chaque lien/boutton (remote ou pas)
renvoyant vers une action contrôlée passe le token associé à l’id
manipulé (params[:context_token]). Les token sont visibles dans les
sources, mais comme ils caractérisent ponctuellement un id
spécifique autorisé (params[:id]) de manière aléatoire, on ne
peut rien en faire à part les utiliser pour faire ce à quoi ils sont
censés servir : donner l’accès ponctuel à une action spécifique pour
un id spécifique. En gros, si, pour un couple params[:id] donné et un
params[:context_token], et à condition que params[:id] soit géré par
le contexte courant, si on matche bien le couple id/token présent dans
le hash de contrôle, l’action est validée.

Si on tente un accès à une action pour laquelle il y a un contrôle du
contexte, que ce soit par url directe sans contexte défini, par url
directe avec un contexte périmé, par url directe avec un mauvais token
et/ou id, ou bien par referer interne avec contexte périmé, ou mauvais
token et/ou id, ça bloque. Pour le moment, ça ne gère pas les routes
imbriquées qui utiliseraient plusieurs id et plusieurs actions, mais
je n’en utilise pas (pas encore) dans mon projet. Au final, rien
d’exceptionnel, mais si ça intéresse quelqu’un, je peux réfléchir à en
faire un plugin par exemple.

Merci pour vos conseils dans tous les cas, j’ai appris des choses ^^

Je ne suis pas sur de comprendre l’idée qu’il y derrière interdire
l’action à des methodes d’un controller :
Si il s’agit de méthode qui ne doivent pas accessible à quiconque,
alors il ne faut pas les présenter ! Il faudrait probalement ajouter
ces méthodes dans la partie privée de ton controller.
Sinon, dans le cas d’un accés filtré, il faudrait probablement …
filtrer avec un système de droit.

Les remarques de Meshak sont justes : limiter les routes et n’exposer
que ce que l’utilisateur a le droit de faire semble logique.

Je ne vois vraiment pas de cas d’usages où les méthodes du controller
ne devant être utilisées que par le serveur lui-même doivent
absolument rester publiques (et par la même correspondre à une URL)

Bon courage

Mathieu