Ruby Biscuit

Share this post

đŸȘ ⚡ Ce cookie de session Devise qui divise nos dĂ©veloppeurs

www.rubybiscuit.fr

đŸȘ ⚡ Ce cookie de session Devise qui divise nos dĂ©veloppeurs

Mélanie
Jan 18
8
Share this post

đŸȘ ⚡ Ce cookie de session Devise qui divise nos dĂ©veloppeurs

www.rubybiscuit.fr

Bonjour Ă  tous,

J'espĂšre que vous avez passĂ© de belles fĂȘtes de fin d'annĂ©e et que vous ĂȘtes d’attaque pour 2023 !

Je suis ravie de vous prĂ©senter notre premiĂšre newsletter de l'annĂ©e 🚀

Aujourd'hui, je vais vous parler d’un sujet qui fait dĂ©bat chez nous : Comment renforcer vos sessions Devise contre les attaques par rejeu de session (session replay attack) ?

Bref, on parle cookie dans la newsletter biscuit 😉 đŸȘ

Lors d'un audit de sécurité récent, nous avons découvert que lorsqu'un utilisateur se déconnectait, son cookie de session restait actif, ce qui lui permettait de se reconnecter via son inspecteur.

Nous nous sommes donc penchĂ©s sur le sujet pour remĂ©dier Ă  ce problĂšme sur l’ensemble de nos plateformes.

IsmaĂ«l vous explique, Ă©tape par Ă©tape, le fonctionnement des sessions de Rails et comment Devise l’utilise. Il nous montre comment vĂ©rifier manuellement puis avec des tests unitaires si cette faille est prĂ©sente sur votre application avant d’explorer deux solutions diffĂ©rentes pour y remĂ©dier.

Spoiler : le choix entre les deux solutions ne sera pas simple, chez nous, elles font encore dĂ©bat. 😉

Je donne la parole à Ismaël :

Chez Capsens, nous sommes une agence spécialisée dans la Fintech et le Ruby on Rails. Nos clients sont principalement des plateformes d'investissement, des banques ou des startups qui manipulent des flux financiers ou des données sensibles.

Naturellement, la sécurité est donc une priorité pour nous. Nous sommes réguliÚrement audités par des experts de la cybersécurité.

Lors d'un de ces audits, un Ă©lĂ©ment intĂ©ressant nous a Ă©tĂ© remontĂ© : si un utilisateur se dĂ©connecte, son cookie de session est toujours actif, c'est Ă  dire qu'on peut le rĂ©utiliser pour ĂȘtre connectĂ© en tant que cet utilisateur. Cela rendrait la plateforme vulnĂ©rable aux vols de sessions dans le cas oĂč l'utilisateur se connecterait via un ordinateur infectĂ© par un logiciel qui enregistrerait ses actions.

Photographie de unsplash.com/@jeisblack

Nous utilisons systématiquement la gem Devise pour gérer l'authentification sur nos plateformes Ruby on Rails. Cette librairie existe depuis plus de 10 ans, a été installée presque 150 millions de fois sur sa derniÚre version et est utilisée par plus de 480 000 utilisateurs Github. C'est l'une des gems phare de la communauté Rails & Ruby avec ses 484 contributeurs.

L'idée de cette article est de s'intéresser aux raisons qui font que cette faille existe et de proposer une solution pour la combler.

Dans cet article, nous nous intéresserons à ce point précis du fonctionnement des sessions de Rails et comment Devise l'utilise pour gérer l'authentification. Comment reproduire cette faille via des tests unitaires avec Rspec, la comprendre et enfin comment la corriger avec Active Record Session Store.

Comprendre les sessions de Rails & Devise

Avant de s'y atteler, il est important de comprendre comment les sessions de Devise fonctionnent. Pour rappel, le but d'une session est de permettre d'identifier un client dans ses interactions avec le serveur.

Le fonctionnement classique de Rails, et du web en gĂ©nĂ©ral, est d'utiliser pour cela un cookie. Il s'agit d'un petit fichier texte stockĂ© sur le navigateur de l'utilisateur qui contient plusieurs couples clĂ©-valeur (xxxx=yyyy). Ce fichier est dĂ©posĂ© par le serveur et va ĂȘtre transmis avec chaque requĂȘte HTTP.

Exemple de cookie de session Rails :
session_id="23929"
expire_at="2023-01-01 20h30"
domain="localhost"
HttpOnly=true

Devise utilise ce systĂšme pour l'authentification. Lorsque l'utilisateur se connecte, il ajoute au cookie de session une clĂ© avec en valeur l'identifiant de l'utilisateur en base de donnĂ©es. Ainsi Ă  chaque requĂȘte, il sait comment identifier l'utilisateur. Le systĂšme est sĂ©curisĂ© puisque la valeur du cookie de session est chiffrĂ©e et dĂ©chiffrĂ©e avec la SECRET_KEY_BASE de votre application pour Ă©viter que quelqu'un ne puisse modifier son identifiant et ainsi s'authentifier en tant que quelqu'un d'autre.

Pour rĂ©sumer, lorsque l'utilisateur se connecte, Devise ajoute dans la session de l'utilisateur son identifiant en base de donnĂ©es. Ensuite Ă  chaque requĂȘte, le serveur dĂ©chiffre le cookie de session et assigne donc current_user Ă  l'utilisateur en base de donnĂ©es qui correspond Ă  cet identifiant.

Ce systÚme provient de Warden, un middleware Rack et qui va ajouter à la session un mécanisme d'authentification en injectant un objet dans l'environnement Rack à request.env['warden']. Plus d'informations sur Warden disponibles à ce lien.

Afin de mieux comprendre, prenons un exemple concret. Je suis donc allĂ© mettre un breakpoint dans mon code grĂące Ă  pry de maniĂšre Ă  intercepter une requĂȘte authentifiĂ©e.

Je déchiffre alors le contenu de la session, plus particuliÚrement de la partie qui nous concerne :

$ pry(<AccountController>)> session
# => 
{
   "warden.user.user.key" => [
      [1], 
      "35VBDJgZO7F1sPfhvPJ57u"
   ],
   # [...d'autres attributs (session_id, flashes, request...)],
}

On obtient une clé warden.user.user.key qui contient une liste de deux valeurs :

  • Le 1 correspond Ă  l'identifiant de mon utilisateur en base de donnĂ©es

  • $2a$12$35VBDJgZO7F1sPfhvPJ57us correspond aux 30 premiers caractĂšres du encrypted_password de l'utilisateur.

Le deuxiÚme paramÚtre permet d'avoir un mécanisme pour invalider une session en cas de modification du mot de passe. Il est défini par la méthode authenticatable_salt de Devise:

# devise/models/database_authenticatable.rb

# A reliable way to expose the salt regardless of the implementation.
def authenticatable_salt
  encrypted_password[0,29] if encrypted_password
end

En cas de déconnexion, le serveur supprime simplement ce paramÚtre warden.user.user.key du cookie de session du navigateur l'utilisateur.

Comprendre le problĂšme

Le problĂšme se situe au niveau de la dĂ©connexion. En effet, comme Ă©voquĂ©, la dĂ©connexion native proposĂ©e par Devise se rĂ©sume Ă  rĂ©initialiser le cookie de session de l'utilisateur. Ainsi, si on suit la logique du fonctionnement, il suffit de rĂ©utiliser n'importe quel cookie de l'utilisateur, et celui-ci serait encore valide mĂȘme aprĂšs dĂ©connexion tant qu'il n'aura pas changĂ© de mot de passe. Essayons !

Je me connecte sur une application Rails avec une configuration Devise classique.

Capture d'écran du dashboard de Doclift, l'application utilisée pour cet exemple

J'inspecte la valeur de mon cookie de session et je la copie

Le cookie de session s'appelle ici _doclift_session et sa valeur est UgGIeMB2jbpOrtHOQsGq[...]2FmoSlrg1YBQ%2Bg%3D%3D (mise de cÎté pour notre test).

Capture d'écran de comment accéder au cookie de session d'une application via l'inspecteur de Google Chrome, onglet application.

Je me déconnecte

Capture d'écran du formulaire de connexion de Doclift

Je modifie la valeur de mon nouveau cookie de session pour remettre l'ancien

Capture de l'inspecteur Chrome pour modifier la valeur d'un cookie

Via l'inspecteur, dans l'onglet Application → Cookie, je remplace la valeur de _doclift_session par l'ancienne que j'avais mise de cĂŽtĂ©.

Je rafraichis la page, je suis connectĂ© 💣

Capture d'écran du dashboard de Doclift.io

La session de l'utilisateur n'Ă©tant gĂ©rĂ© que via le cookie de session de Rails qui contient uniquement l'ID de l'utilisateur, aucun autre mĂ©canisme que le changement de mot de passe ne permet d'invalider dĂ©finitivement un cookie de session. MĂȘme si l'utilisateur s'est dĂ©connectĂ©, si le cookie a Ă©tĂ© interceptĂ©, alors il pourra ĂȘtre rĂ©utilisĂ©.

Tests unitaires

Avant de corriger le problÚme, nous allons écrire un test unitaire simple qui le reproduit afin de nous assurer facilement que notre solution le corrige bien.

Pour cela nous utiliserons des request_specs de rspec :

rails g rspec:request users/sessions/delete
# =>  create  spec/requests/users/sessions/delete_spec.rb

Nous souhaitons que notre test fasse ce que nous avons fait manuellement, c'est-Ă -dire rĂ©cupĂ©rer un cookie de session d'un utilisateur authentifiĂ©, puis se dĂ©connecter et remplacer son nouveau cookie de session par l'ancien : le rĂ©sultat attendu est de ne pas ĂȘtre connectĂ©.

# spec/requests/users/sessions/delete_spec.rb

require "rails_helper"

RSpec.describe "POST Users::Sessions#delete", type: :request do
  let(:user) { create(:user) } # FactoryBot

  subject(:perform_log_out_request) { delete(destroy_user_session_path) }
  before do
    sign_in(user)
    # la requĂȘte est necessaire pour que la session
    # soit créée et son cookie déposé
    get(root_path) 
  end

  context "when user signs out" do
    it "deletes user_session" do
      expect {
        perform_log_out_request
      }.to change {
        session["warden.user.user.key"]&.dig(0, 0)
      }.from(user.id).to(nil)
    end

    it "has invalidated previous session" do
      previous_cookie = cookies[:_doclift_session]
      perform_log_out_request
      cookies[:_doclift_session] = previous_cookie
      get(root_path)
      expect(response).to redirect_to(
        new_user_session_path
      )
    end
  end
end

Lorsque l'on exĂ©cute les tests, on constate bien que le dernier Ă©choue, le cookie de l'utilisateur est toujours valide mĂȘme aprĂšs sa dĂ©connexion :

$ rspec spec/requests/users/sessions/delete_spec.rb
.F
Failures:

  1) POST Users::Sessions#delete when user signs out has invalidated previous session
     Failure/Error: expect(response).to redirect_to(new_user_session_path)
       Expected response to be a <3XX: redirect>, but was a <200: OK>
     # ./spec/requests/users/sessions/delete_spec.rb:21:in `block (3 levels) in <top (required)>'

Stocker la session cÎté serveur : Active Record Session Store

Aperçu Github de la gem Active Record Session Store

Active Record Session Store est une gem qui a était d'abord incluse directement dans Rails puis proposée sous la forme d'une gem externe. Il permet de stocker la session non plus cÎté client mais directement en base de données, donc cÎté serveur.

Les avantages d'utiliser une telle solution sont multiples :

  • RĂ©duit fortement le risque de cookie overflow, c'est-Ă -dire de dĂ©passer la taille maximum (4 Ko) autorisĂ©e par les navigateur lorsque l'on tente de stocker une trop grande quantitĂ© d'informations dans la DB.

  • Permet de garder un contrĂŽle total des sessions : les sessions Ă©tant en base de donnĂ©es, on peut facilement imaginer un systĂšme qui permet de rĂ©voquer une session lorsque le besoin se prĂ©sente.

  • Permet d'Ă©viter de stocker cĂŽtĂ© client des informations potentiellement sensibles (mĂȘme s'il est fortement recommandĂ© d'Ă©viter de stocker des informations sensibles dans la session).

  • RĂ©duit la taille rĂ©seau de chaque requĂȘte puisqu'on n'a plus besoin de transfĂ©rer toutes les informations de la session Ă  chaque requĂȘte, elles peuvent rester cĂŽtĂ© serveur.

Cette solution présente également des inconvénients :

  • Cela implique d'ajouter une nouvelle dĂ©pendance (liĂ© Ă  une fonctionnalitĂ© coeur) Ă  son application et son lot de problĂšme que cela engendre.

  • Lorsque une application cherche Ă  scaler, elle tend Ă  rĂ©duire le nombre de requĂȘtes Ă  la base de donnĂ©es dĂ©s que c'est possible. La session cĂŽtĂ© client est une bonne solution pour Ă©conomiser de nombreuses requĂȘtes en lecture et Ă©criture.

  • Depuis Rails 6, il est plus facilement possible de connecter plusieurs bases de donnĂ©es Ă  son application notamment pour sĂ©parer les requĂȘtes de lecture des requĂȘtes en Ă©critures (plus lentes). Active Record Session Store complique ce genre de pratiques puisqu'il serait plus complexe de faire la sĂ©paration entre les requĂȘtes qui impliquent de l'Ă©criture des autres en se basant sur les verbes HTTP (GET, POST, PUT ...)

Ceci étant dit, la gem est trÚs simple à mettre en place, il suffit de :

Ajouter la gem gem 'activerecord-session_store' à son Gemfile & bundle exec install puis lancer la commande rails generate active_record:session_migration qui crée la migration suivante :

# db/migrate/20230116171912_add_sessions_table.rb

class AddSessionsTable < ActiveRecord::Migration[7.0]
  def change
    create_table :sessions do |t|
      t.string :session_id, null: false
      t.text :data
      t.timestamps
    end

    add_index :sessions, :session_id, :unique => true
    add_index :sessions, :updated_at
  end
end

La migration va donc créer une table sessions composée de 4 champs :

  • un champ string unique session_id avec un index qui va servir Ă  stocker l'identifiant de la session

  • un champ text data qui contiendra le contenu de la session utilisateur

  • les 2 timestamps created_at & updated_at

Lançons cette migration avec rake db:migrate.

Il nous reste plus qu'à indiquer à Rails que l'on souhaite utiliser active_record_store plutÎt que cookie_store en modifiant ou créant le fichier config/initializers/session_store.rb :

# config/initializers/session_store.rb

Rails.application.config.session_store(
  :active_record_store,
  key: "_doclift_session",
)

Lançons les tests pour vérifier que l'on corrige bien notre faille de sécurité :

rspec spec/requests/users/sessions/delete_spec.rb
..

Finished in 2.11 seconds (files took 1.11 seconds to load)
2 examples, 0 failures

Le correctif corrige bien le problĂšme ... mais comment ?

Intéressons-nous un peu plus en détail à ce qu'il se passe.

Capture d'écran du cookie de session avec Active Record Session Store

Active Record Session Store utilise sans surprise également un cookie mais il y a deux différences majeures :

  • Sa taille est significativement rĂ©duite : 48 bytes contre 488 bytes avec le cookie_store

  • Sa valeur est beaucoup plus courte, en effet, ici l'ID mesure seulement 32 caractĂšres

Ces deux différences s'expliquent par le fait que ce cookie ne sert qu'à lier le client à une session qui est cette fois stockée en base de données.

# console Rails

SESSION_CLASS = ActionDispatch::Session::ActiveRecordStore.session_class.freeze

$ SESSION_CLASS.last
 =>
#<ActiveRecord::SessionStore::Session:0x000000010f145db0
  id: 26,
  session_id: "2::d92dd021553fb0826be17dbc6bf1e17f77b34bf5384f45b4517f6f8e187060ff",
  data: "BAh7BkkiEF9jc3JmX3Rva2VuBjoGRUZJIjBoREZseTB3dGt4QXpHVWFodEd6\nRzE2Q2xGODhaamdIMzlIaXJGUE5YdXc4BjsARg==\n",
  created_at: Mon, 16 Jan 2023 22:05:42.567198000 CET +01:00,
  updated_at: Mon, 16 Jan 2023 22:05:42.579124000 CET +01:00
>

Ici le session_id est différent de celui visible dans le cookie de l'utilisateur pour mitiger le risque d'attaque temporelle mais on peut relativement facilement le retrouver :

# console Rails

session_public_id = "7b0ef645ef6d6bbe468b01664bd11c44"
sid = Rack::Session::SessionId.new(session_public_id)
sid.private_id 
# => "2::d92dd021553fb0826be17dbc6bf1e17f77b34bf5384f45b4517f6f8e187060ff"

Le champ data est encodé avec Marshall en base64 mais la gem implémente des méthodes #load & #dump afin de pouvoir les lire et modifier facilement  :

# console Rails

SESSION_CLASS = ActionDispatch::Session::ActiveRecordStore.session_class.freeze

SESSION_CLASS.last.data
=> ActiveRecord::SessionStore::Session Load (0.7ms)  SELECT "sessions".* FROM "sessions" ORDER BY "sessions"."id" DESC LIMIT $1  [["LIMIT", 1]]
{
  "_csrf_token" => "hDFly0wtkxAzGUahtGzG16ClF88ZjgH39HirFPNXuw8",
  "user_return_to" => "/"
}

Tant que l'utilisateur n'est pas connecté, il n'a pas de clé warden.user.user.key en revanche on peut retrouver les éléments classiques que contiennent une session tel que l'url de redirection aprÚs connexion.

Il est pertinent de faire remarquer qu'Ă  chaque fois qu'un client effectue une requĂȘte Ă  l'application, une session en base de donnĂ©es est crĂ©e pour lui si il n'en a pas dĂ©jĂ  une. Cela signifie qu'un utilisateur malintentionnĂ© peut facilement crĂ©er des millions de lignes dans votre base de donnĂ©es avec un simple script qui fait une requĂȘte et supprime le cookie de session en boucle. Il est donc important de se protĂ©ger contre ce genre d'attaques avec un systĂšme de bannissement d'IP ou autre systĂšme de protection contre les bots (Cloudflare avec challenge avant d'accĂ©der au site par exemple).

Une fois l'utilisateur connectĂ©, sa session est augmentĂ©e avec les mĂȘmes informations que dans le cas d'une session cĂŽtĂ© client :

# console Rails

SESSION_CLASS = ActionDispatch::Session::ActiveRecordStore.session_class.freeze

def find_session_with_public_id(public_id)
  rack_sid = Rack::Session::SessionId.new(public_id)
  private_id = rack_sid.private_id
  
  SESSION_CLASS.find_by_session_id(private_id)
end

$ session = find_session_with_public_id("eb7a620b9f947590bcd917e3a985f018")
$ session.data

#  ActiveRecord::SessionStore::Session Load (0.5ms)  SELECT "sessions".* FROM "sessions" ORDER BY "sessions"."id" DESC LIMIT $1  [["LIMIT", 1]]
# =>  
{ 
  "warden.user.user.key"=>[
    [1],
    "$2a$12$35VBDJgZO7F1sPfhvPJ57u"
  ],
  "flash" => { ... }
}

En revanche, une fois que l'on se déconnecte, la session est supprimée de la base de données :

# console Rails

$ find_session_with_public_id("eb7a620b9f947590bcd917e3a985f018")
# ActiveRecord::SessionStore::Session Load (0.3ms)  SELECT "sessions".* FROM "sessions" WHERE "sessions"."session_id" = $1 ORDER BY "sessions"."id" ASC LIMIT $2  [["session_id", "2::eb635a939ad94353db96019c812621cefd16d723b90794634411de35961d1b5b"], ["LIMIT", 1]]
=> nil

De maniĂšre gĂ©nĂ©rale, Ă  chaque fois que la session a besoin d'ĂȘtre modifiĂ©e, elle est supprimĂ©e puis recréée :

# console Rails

$ SESSION_CLASS.ids
=> [32]

# On se connecte 
$ SESSION_CLASS.ids
=> [33]

# On se déconnecte 
$ SESSION_CLASS.ids
=> [34]

Pour conclure, Active Record Session Store nous apporte un moyen simple de se prémunir contre les attaques par rejeu de session aprÚs que l'utilisateur se soit déconnecté.

Ce systĂšme ne permet d'invalider facilement une session dans le cas oĂč le client (de cette session) ne se dĂ©connecterait pas. S'il a oubliĂ© de se dĂ©connecter de cet ordinateur, sa seule possibilitĂ© pour invalider immĂ©diatement toutes ses sessions est de changer son mot de passe, ce qui invaliderait son salt comme Ă©voquĂ© prĂ©cĂ©demment grĂące Ă  la mĂ©thode authenticatable_salt.

De plus, cette solution prĂ©sente tout de mĂȘme des dĂ©fauts non nĂ©gligeables :

  • elle implique d'avoir un systĂšme pour purger pĂ©riodiquement les sessions obsolĂštes sinon vous vous retrouvez avec des millions de sessions inutiles en bases de donnĂ©es

  • elle complexifie Ă©normĂ©ment le scaling de votre application en rendant trĂšs fastidieuse la sĂ©paration des requĂȘtes en lecture et en Ă©criture

  • elle sollicite la base de donnĂ©es Ă  chaque requĂȘte avec une grande partie de requĂȘte en Ă©criture

  • elle rend (plus) vulnĂ©rable aux attaques DDOS

Pour ces raisons, le choix de Rails de déprécier et d'externaliser cette solution est compréhensible. Il serait donc intéressant d'explorer une autre solution.

Corriger le problĂšme avec authenticatable_salt

# devise/models/database_authenticatable.rb

# A reliable way to expose the salt regardless of the implementation.
def authenticatable_salt
  encrypted_password[0,29] if encrypted_password
end

Comme évoqué précédemment, cette méthode permet d'invalider une session lorsque l'utilisateur change de mot de passe. Nous pourrions donc augmenter sa logique pour ajouter un second paramÚtre qui permettrait de faire changer ce salt en cas de déconnexion.

Nous allons donc ajouter un token stocké sur la table de l'utilisateur qui sera regeneré à chaque fois que l'utilisateur se déconnecte et ajouter ce token à la méthode authenticatable_salt .

Créons donc une migration pour ajouter session_token à la table users :

class AddSessionTokenToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column(
      :users,
      :session_token,
      :text,
      default: -> { "md5((random())::text)" },
      null: false,
    )
    add_index :users, :session_token
  end
end

La migration ci-dessus ajoute un champ texte session_token sur la table utilisateur et lui assigne une valeur par défaut aléatoire de type md5 (exemple : 314809aaf64aa80d35e117ae1111ee82) différente (sans garantie d'unicité) pour chaque ligne en base de données.

Pour que la solution soit fonctionnelle, il reste encore à modifier ce token à chaque fois que l'utilisateur se déconnecte. Pour cela il suffit de modifier la méthode de déconnexion de Devise et donc overrider le controller de sessions :

# On génére le controller SessionsController
# dans le scope "users"
$ rails g devise:controllers users -c sessions
# =>   create  app/controllers/users/sessions_controller.rb

Puis ensuite on spĂ©cifie Ă  Rails oĂč chercher le controller pour les routes de sessions :

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users,
              controllers: {
                sessions: "users/sessions",
              }
              
  # [...]
end

On modifie alors le SessionsController que l'on vient de créer pour overrider la méthode de déconnexion afin qu'elle regenÚre le token stocké dans session_token

# app/controllers/users/sessions_controller.rb

class Users::SessionsController < Devise::SessionsController
  # [...]

  # DELETE /resource/sign_out
  def destroy
    current_user.invalidate_all_sessions
    super
  end
end

Enfin on crée la méthode invalidate_all_sessions et on override la méthode authenticatable_salt sur le modÚle utilisateur :

# app/models/user.rb
class User < ApplicationRecord
  # [...]

  def authenticatable_salt
    "#{super}#{session_token}"
  end

  def invalidate_all_sessions
    self.update_column :session_token, SecureRandom.hex(8)
  end
end

Désormais à chaque déconnexion, le salt sera mis à jour ce qui implique d'invalider tous les anciens cookies de session.

Vous n'avez pas besoin de dĂ©finir une valeur trop Ă©levĂ©e pour ce session_token, en effet, il sert ici uniquement de salt, c'est Ă  dire que mĂȘme si par hasard deux utilisateurs venaient Ă  avoir le mĂȘme token, cela ne serait pas gĂȘnant puisqu'ils n'auront pas pour autant le mĂȘme ID en base de donnĂ©es.

Vérifions que notre correctif permet bien d'invalider le cookie de session de l'utilisateur en cas de déconnexion :

$ rspec spec/requests/users/sessions/delete_spec.rb
..

Finished in 2.11 seconds (files took 1.12 seconds to load)
2 examples, 0 failures

Si votre application contient un dispositif permettant à vos administrateurs de se connecter en tant qu'un utilisateur, il conviendrait alors de ne pas réinitialiser le token de l'user en cas de déconnexion de l'administrateur sinon celui-ci se retrouverait déconnecté alors qu'il est potentiellement en train de naviguer sur le site.

Cette solution a été proposée initialement par Natalie Zeumann dans son article Devise: Invalidating all sessions for a user.

De là, on peut donc trÚs facilement utiliser notre méthode user.invalidate_all_sessions pour permettre à un administrateur d'invalider immédiatement les sessions pour un utilisateur donné sans avoir besoin de lui demander de changer de mot de passe. Notez que cette solution est également compatible avec Active Record Session Store afin de permettre de s'assurer que toutes les sessions sont bien purgées lorsque l'utilisateur se déconnecte.

MĂȘme si elle est simple Ă  mettre en place, cette solution prĂ©sente un dĂ©faut non nĂ©gligeable, elle repose sur une mĂ©thode qui n'est pas exposĂ©e dans l'API publique de Devise. En d'autres termes, celle-ci peut ĂȘtre dĂ©prĂ©ciĂ©e Ă  n'importe quel moment dans une future version de Devise.

D'autres solutions existent, nous explorerons notamment dans une prochaine lettre le stockage des sessions dans Redis.

Alors Ă  vous de jouer maintenant, plus d'excuses pour laisser cette faille risquer de mettre en pĂ©ril les sessions de vos utilisateurs, vous avez toutes les clĂ©s en main. D’ailleurs, je serais curieuse de savoir laquelle vous avez choisie et pour quelles raisons, peut-ĂȘtre arriveriez vous Ă  mettre tout le monde d’accord !

En attendant, nous nous penchons sur une troisiùme solution, j’espùre pouvoir vous en parler bientît ! 😉


Toujours dans le thĂšme de la sĂ©curitĂ©, je souhaitais Ă©galement vous partager cet article qui liste les principales en-tĂȘtes de sĂ©curitĂ© que vous pouvez utiliser pour protĂ©ger vos sites Web.
Cet article nous a rĂ©cemment Ă©tĂ© partagĂ© par Robert sur notre canal #technique_links. C’est un canal Slack que nous utilisons au quotidien pour se partager des articles intĂ©ressants et pour dĂ©battre de sujets techniques (notre passe-temps prĂ©fĂ©rĂ©).

Bonne fin de semaine Ă  tous et Ă  la semaine prochaine !

Mélanie

Share this post

đŸȘ ⚡ Ce cookie de session Devise qui divise nos dĂ©veloppeurs

www.rubybiscuit.fr
Previous
Next
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 Mélanie
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing