web-dev-qa-db-fra.com

Meilleure façon de nettoyer les entrées utilisateur dans rails

J'ai beaucoup lu à ce sujet et je sais qu'il y a beaucoup de questions connexes ici, mais je n'ai pas pu trouver de guide définitif sur la façon de tout nettoyer. Une option consiste à désinfecter sur l'insert, par exemple j'ai le suivant dans mon modèle

before_validation :sanitize_content, :on => :create
def sanitize_content
  self.content = ActionController::Base.helpers.sanitize(self.content)
end

Dois-je l'exécuter sur chaque champ de chaque modèle? Je suppose que le: on =>: create devrait également être supprimé afin qu'il s'exécute également lors des mises à jour?

L'autre option consiste à filtrer lorsque les données sont affichées dans les vues, en utilisant simple_format, ou .html_safe ou sanitize (fieldname). Dois-je désinfecter toutes mes vues pour chaque champ, ainsi que sur l'insert? Devoir le faire manuellement partout ne semble pas très compliqué

Merci pour toute aide

27
Dave

TL; DR
Concernant la saisie et les requêtes des utilisateurs: Assurez-vous de toujours utiliser les méthodes de requête d'enregistrement actif (telles que .where), et évitez de passer des paramètres en utilisant l'interpolation de chaînes; transmettez-les sous forme de valeurs de paramètre de hachage ou sous forme d'instructions paramétrées.

Concernant le rendu du contenu html/javascript généré par l'utilisateur potentiellement dangereux: À partir de Rails 3, le texte html/javascript est automatiquement correctement échappé de sorte qu'il apparaisse en texte brut sur la page, plutôt que d'être interprété en html/javascript, vous n'avez donc pas besoin de nettoyer explicitement (ou d'utiliser <%= h(potentially_unsafe_user_generated_content)%>

Si je vous comprends bien, vous n'avez pas à vous soucier de la désinfection des données de cette manière, tant que vous utilisez correctement les méthodes de requête d'enregistrement actif. Par exemple:

Disons que notre mappage de paramètres ressemble à ceci, à la suite d'un utilisateur malveillant saisissant la chaîne suivante dans le champ user_name:

:user_name => "(select user_name from users limit 1)"

La mauvaise façon (ne faites pas ça):

Users.where("user_name = #{params[:id}") # string interpolation is bad here

La requête résultante ressemblerait à:

SELECT `users`.* FROM `users` WHERE (user_name = (select user_name from users limit 1))

L'interpolation directe de chaînes de cette manière placera le contenu littéral de la valeur du paramètre avec la clé :user_name Dans la requête sans nettoyage. Comme vous le savez probablement, l'entrée de l'utilisateur malveillant est traitée comme du SQL simple et le danger est assez clair.

La bonne façon (faites cela):

Users.where(id: params[:id]) # hash parameters

OU

Users.where("id = ?", params[:id]) # parameterized statement

La requête résultante ressemblerait à:

SELECT `users`.* FROM `users` WHERE user_name = '(select user_name from users limit 1)'

Donc, comme vous pouvez le voir, Rails en fait le nettoie pour vous, tant que vous passez le paramètre en tant que hachage ou paramètre de méthode (selon la méthode de requête que vous utilisez).

Le cas de la désinfection des données lors de la création de nouveaux enregistrements de modèle ne s'applique pas vraiment, car les méthodes new ou create attendent un hachage de valeurs. Même si vous essayez d'injecter du code SQL non sécurisé dans le hachage, les valeurs du hachage sont traitées comme des chaînes simples, par exemple:

User.create(:user_name=>"bobby tables); drop table users;")

Résultats dans la requête:

INSERT INTO `users` (`user_name`) VALUES ('bobby tables); drop table users;')

Donc, même situation que ci-dessus.

J'espère que ça aide. Faites-moi savoir si j'ai raté ou mal compris quelque chose.

Edit En ce qui concerne l'échappement html et javascript, la version courte est que ERB "échappe" le contenu de votre chaîne pour vous afin qu'il soit traité comme du texte brut. Vous pouvez le faire traiter comme du HTML si vous le voulez vraiment, en faisant your_string_content.html_safe.

Cependant, faire simplement quelque chose comme <%= your_string_content %> Est parfaitement sûr. Le contenu est traité comme une chaîne sur la page. En fait, si vous examinez le DOM en utilisant Chrome Developer Tools ou Firebug, vous devriez en fait voir des guillemets autour de cette chaîne.

68
Paul Richter

Parce que j'apprécie toujours quand je trouve la source de connaissances et de code sur n'importe quelle SO, je fournirai cela pour cette question.

ActiveRecord et ActionController fournissent des méthodes pour assainir l'entrée sql.

Plus précisément de ActiveRecord::Sanitization::ClassMethods vous avez sanitize_sql_for_conditions et ses deux autres alias: sanitize_conditions et sanitize_sql . Les trois font exactement la même chose.

sanitize_sql_for_conditions

Accepte un tableau, un hachage ou une chaîne de conditions SQL et les nettoie en un fragment SQL valide pour une clause WHERE .

Cependant, dans ActiveRecord vous avez également

sanitize_sql_for_assignment lequel

Accepte un tableau, un hachage ou une chaîne de conditions SQL et les nettoie en un fragment SQL valide pour une clause SET .

  • Notez que ces méthodes sont incluses dans ActiveRecord :: Base et sont donc incluses par défaut dans tout modèle ActiveRecord .

En revanche, dans ActionController vous avez ActionController::Parameters qui vous permet de

choisissez les attributs qui doivent être ajoutés à la liste blanche pour la mise à jour en masse et évitez ainsi d'exposer accidentellement ce qui ne devrait pas l'être. Fournit deux méthodes à cet effet: nécessite et autorise .

params = ActionController::Parameters.new(user: { name: 'Bryan', age: 21 })
req  = params.require(:user) # will throw exception if user not present
opt  = params.permit(:name)  # name parameter is optional, returns nil if not present
user = params.require(:user).permit(:name, :age) # user hash is required while `name` and `age` keys are optional

La magie des paramètres s'appelle Strong Parameters, docs here .

J'espère que cela aide n'importe qui, ne serait-ce que pour apprendre et démystifier Rails! :)

11
Bryan Dimas