Sécurité
Si vous pensez que la technologie peut résoudre vos problèmes de sécurité alors vous n'avez rien compris aux problèmes ni à la technologie. (Bruce Schneier)
Utilisateurs à privilèges
Limiter les rôles attribuables
Les utilisateurs avec accès au back-office ne doivent pas pouvoir modifier les utilisateurs avec des droits supérieurs aux leurs. Un non admin qui a le droit de modifier le rôle d’un user peut modifier le code html de son formulaire pour attribuer un rôle superadmin - même si le rôle n’est pas affiché par défaut dans le formulaire. Il faut donc vérifier qu’un user a bien le droit de donner le rôle qu’il veut donner.
Avec une méthode listant les rôles gérés, par exemple:
class User
attr_accessor :modified_by
before_validation :check_modifier_role
def roles_managed
[:admin, :editor, :translator]
end
protected
def check_modifier_role
errors.add(:role, 'cannot be set to this role') if modified_by && !modified_by.get_roles_managed.include?(self.role)
end
end
Evidemment, la vraie méthode prendrait les infos en db.
Il suffit d’ajouter dans le controller:
def update
@user.modified_by = current_user
...
end
Interdire l’édition d’un rôle supérieur
Un utilisateur admin ne doit pas pouvoir éditer un superadmin.
can :manage, User, role: user.roles_managed
Injection javascript
Nettoyer les champs de saisie AVANT l’entrée en base de donnée
Pour les champs importants, notamment ceux saisissables en front
before_validation :sanitize_fields
def sanitize_fields
full_sanitizer = Rails::Html::FullSanitizer.new
# Only text allowed
self.email = full_sanitizer.sanitize(self.email)
self.name = full_sanitizer.sanitize(self.name)
self.firstname = full_sanitizer.sanitize(self.firstname)
self.gender = full_sanitizer.sanitize(self.gender)
self.mobile = full_sanitizer.sanitize(self.mobile)
self.pos = full_sanitizer.sanitize(self.pos)
self.address = full_sanitizer.sanitize(self.address)
self.zipcode = full_sanitizer.sanitize(self.zipcode)
self.city = full_sanitizer.sanitize(self.city)
end
TODO (gem?)
sanitize_fields :email, :name, :firstname, :gender, :mobile, :pos, :address, :zipcode, :city
Eviter des méthodes to_s trop ouvertes
On peut sanitize aussi la méthode to_s des modèles pour éviter l’injection XSS, si une donnée hackée a réussi à rentrer dans la base de données.
def to_s
sanitize("#{first_name} #{last_name}")
end
Sanitize par sécurité les affichages dans les vues
<%= sanitize model.method %>
Authentification
Eviter l’usurpation de cookies de session
Par défaut lorsqu’on se déconnecte avec devise le dernier identifiant de session stocké en cookie reste valable. Donc si on “copie” cet identifiant avant de se dé-logger et qu’on modifie après le cookie de session pour remettre l’identifiant copié, on se retrouve connecté.
Potentiellement si un utilisateur distant récupère cet id de session il peut usurper une identité.
Pour éviter ça on ajoute au “salt” de devise un token unique, qu’on invalide à la déconnexion du user.
De plus on ajoute une déconnexion automatique de la session en cas d’inactivité prolongée.
Cf méthode 2 ici
Paramétrer les cookies par défaut pour renforcer la sécurité
Il faut mettre une durée par défaut aux cookies, sinon ils sont définis en “jusqu’à la fin de la session”, et avec les navigateurs qui maintiennent ouvertes les sessions ils n’expirent plus jamais.
Il faut aussi les paramétrer pour qu’ils soient “secure”, çàd forcer leur utilisation en https.
Attention en développement (http) on ne peut pas utiliser de cookies secure.
Dans /config/initalizers/session_store.rb :
if Rails.env == 'development'
Rails.application.config.session_store :cookie_store, key: "_{PROJECT}_session", expire_after: 1.days
else
Rails.application.config.session_store :cookie_store, key: "_{PROJECT}_session", expire_after: 1.days, secure: true
end
Paramétrer correctement Devise
On doit activer au minimum les modules :timeoutable (pour gérer la déconnexion automatique en cas d’inaction), :lockable (pour gérer les X tentatives infructueuses). Evidemment il faut faire le setup qui va avec dans l’initializer de Devise.
Il est possible d’activer le mode “paranoid” de Devise pour neutraliser les messages (par exemple on n’aura plus de différence sur le Recover password entre un mail qui n’existe pas en DB et un mail qui existe). Ca augmente la sécurité mais c’est casse couilles pour l’utilisateur final (par exemple l’utilisateur ne sait plus que son compte est locked, il a tout le temps un message “email ou mdp invalide”).