Aller au contenu

session: Bibliothèque de session pour nginx-module-lua – flexible et sécurisée

Installation

Si vous n'avez pas encore configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

CentOS/RHEL 7 ou Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-session

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-session

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-session v4.1.5 publiée le 24 novembre 2025.


lua-resty-session est une bibliothèque de session sécurisée et flexible pour OpenResty.

TL;DR;

  • Les sessions sont immuables (chaque sauvegarde génère une nouvelle session) et sans verrou.
  • Les données de session sont chiffrées avec AES-256-GCM avec une clé dérivée utilisant HKDF-SHA256 (en mode FIPS, elle utilise PBKDF2 avec SHA-256 à la place).
  • La session a un en-tête de taille fixe qui est protégé par un MAC HMAC-SHA256 avec une clé dérivée utilisant HKDF-SHA256 (en mode FIPS, elle utilise PBKDF2 avec SHA-256 à la place).
  • Les données de session peuvent être stockées dans un cookie sans état ou dans divers stockages backend.
  • Un seul cookie de session peut maintenir plusieurs sessions à travers différents publics.

Remarque : La version 4.0.0 était une réécriture de cette bibliothèque avec beaucoup de leçons apprises au fil des ans. Si vous utilisez encore une version antérieure, veuillez vous référer à la documentation ancienne.

Synopsis

worker_processes  1;

events {
  worker_connections 1024;
}

http {
  init_by_lua_block {
    require "resty.session".init({
      remember = true,
      audience = "demo",
      secret   = "RaJKp8UQW1",
      storage  = "cookie",
    })
  }

  server {
    listen       8080;
    server_name  localhost;
    default_type text/html;

    location / {
      content_by_lua_block {
        ngx.say([[
          <html>
          <body>
            <a href=/start>Démarrer le test</a>
          </body>
          </html>
        ]])
      }
    }

    location /start {
      content_by_lua_block {
        local session = require "resty.session".new()
        session:set_subject("Fan d'OpenResty")
        session:set("quote", "Le rapide renard brun saute par-dessus le chien paresseux")
        local ok, err = session:save()

        ngx.say(string.format([[
          <html>
          <body>
            <p>Session démarrée (%s)</p>
            <p><a href=/started>Vérifiez si c'était vraiment le cas</a></p>
          </body>
          </html>
        ]], err or "aucune erreur"))
      }
    }

    location /started {
      content_by_lua_block {
        local session, err = require "resty.session".start()

        ngx.say(string.format([[
          <html>
          <body>
            <p>La session a été démarrée par %s (%s)</p>
            <p><blockquote>%s</blockquote></p>
            <p><a href=/modify>Modifier la session</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anonyme",
          err or "aucune erreur",
          session:get("quote") or "aucune citation"
        ))
      }
    }

    location /modify {
      content_by_lua_block {
        local session, err = require "resty.session".start()
        session:set_subject("Fan de Lua")
        session:set("quote", "Lorem ipsum dolor sit amet")
        local _, err_save = session:save()

        ngx.say(string.format([[
          <html>
          <body>
            <p>La session a été modifiée (%s)</p>
            <p><a href=/modified>Vérifiez si elle est modifiée</a></p>
          </body>
          </html>
        ]], err or err_save or "aucune erreur"))
      }
    }

    location /modified {
      content_by_lua_block {
        local session, err = require "resty.session".start()

        ngx.say(string.format([[
          <html>
          <body>
            <p>La session a été démarrée par %s (%s)</p>
            <p><blockquote>%s</blockquote></p>
            <p><a href=/destroy>Détruire la session</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anonyme",
          err or "aucune erreur",
          session:get("quote")  or "aucune citation"
        ))
      }
    }

    location /destroy {
      content_by_lua_block {
        local ok, err = require "resty.session".destroy()

        ngx.say(string.format([[
          <html>
          <body>
            <p>La session a été détruite (%s)</p>
            <p><a href=/destroyed>Vérifiez si c'était vraiment le cas ?</a></p>
          </body>
          </html>
        ]], err or "aucune erreur"))
      }
    }

    location /destroyed {
      content_by_lua_block {
        local session, err = require "resty.session".open()

        ngx.say(string.format([[
          <html>
          <body>
            <p>La session a vraiment été détruite, vous êtes connu sous le nom de %s (%s)</p>
            <p><a href=/>Recommencer</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anonyme",
          err or "aucune erreur"
        ))
      }
    }
  }
}

Configuration

La configuration peut être divisée en configuration de session générique et configuration de stockage côté serveur.

Voici un exemple :

init_by_lua_block {
  require "resty.session".init({
    remember = true,
    store_metadata = true,
    secret = "RaJKp8UQW1",
    secret_fallbacks = {
      "X88FuG1AkY",
      "fxWNymIpbb",
    },
    storage = "postgres",
    postgres = {
      username = "my-service",
      password = "kVgIXCE5Hg",
      database = "sessions",
    },
  })
}

Configuration de Session

La configuration de session peut être passée aux fonctions initialisation, constructeur, et helper.

Voici les options de configuration de session possibles :

Option Par défaut Description
secret nil Secret utilisé pour la dérivation de clé. Le secret est haché avec SHA-256 avant d'être utilisé. Par exemple, "RaJKp8UQW1".
secret_fallbacks nil Tableau de secrets pouvant être utilisés comme secrets alternatifs (lors de la rotation de clé), par exemple { "6RfrAYYzYq", "MkbTkkyF9C" }.
ikm (aléatoire) Le matériel de clé initial (ou ikm) peut être spécifié directement (sans utiliser de secret) avec exactement 32 octets de données. Par exemple, "5ixIW4QVMk0dPtoIhn41Eh1I9enP2060"
ikm_fallbacks nil Tableau de matériaux de clé initiaux pouvant être utilisés comme clés alternatives (lors de la rotation de clé), par exemple { "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }.
cookie_prefix nil Préfixe de cookie, utilisez nil, "__Host-" ou "__Secure-".
cookie_name "session" Nom du cookie de session, par exemple "session".
cookie_path "/" Chemin du cookie, par exemple "/".
cookie_domain nil Domaine du cookie, par exemple "example.com"
cookie_http_only true Marquer le cookie HTTP uniquement, utilisez true ou false.
cookie_secure nil Marquer le cookie comme sécurisé, utilisez nil, true ou false.
cookie_priority nil Priorité du cookie, utilisez nil, "Low", "Medium", ou "High".
cookie_same_site "Lax" Politique de même site pour le cookie, utilisez nil, "Lax", "Strict", "None", ou "Default"
cookie_same_party nil Marquer le cookie avec le drapeau de même partie, utilisez nil, true, ou false.
cookie_partitioned nil Marquer le cookie avec le drapeau partitionné, utilisez nil, true, ou false.
remember false Activer ou désactiver les sessions persistantes, utilisez nil, true, ou false.
remember_safety "Medium" Complexité de dérivation de clé de cookie de souvenir, utilisez nil, "None" (rapide), "Low", "Medium", "High" ou "Very High" (lent).
remember_cookie_name "remember" Nom du cookie de session persistante, par exemple "remember".
audience "default" Public de session, par exemple "my-application".
subject nil Sujet de session, par exemple "[email protected]".
enforce_same_subject false Lorsqu'il est défini sur true, les publics doivent partager le même sujet. La bibliothèque supprime les données de public non correspondantes lors de la sauvegarde.
stale_ttl 10 Lorsqu'une session est sauvegardée, une nouvelle session est créée, le ttl obsolète spécifie combien de temps l'ancienne peut encore être utilisée, par exemple 10 (en secondes).
idling_timeout 900 Le délai d'attente d'inactivité spécifie combien de temps la session peut être inactive avant d'être considérée comme invalide, par exemple 900 (15 minutes) (en secondes), 0 désactive les vérifications et les touches.
rolling_timeout 3600 Le délai d'attente roulant spécifie combien de temps la session peut être utilisée avant de devoir être renouvelée, par exemple 3600 (une heure) (en secondes), 0 désactive les vérifications et le roulage.
absolute_timeout 86400 Le délai d'attente absolu limite combien de temps la session peut être renouvelée, jusqu'à ce qu'une nouvelle authentification soit requise, par exemple 86400 (un jour) (en secondes), 0 désactive les vérifications.
remember_rolling_timeout 604800 Le délai d'attente de souvenir spécifie combien de temps la session persistante est considérée comme valide, par exemple 604800 (une semaine) (en secondes), 0 désactive les vérifications et le roulage.
remember_absolute_timeout 2592000 Le délai d'attente absolu de souvenir limite combien de temps la session persistante peut être renouvelée, jusqu'à ce qu'une nouvelle authentification soit requise, par exemple 2592000 (30 jours) (en secondes), 0 désactive les vérifications.
hash_storage_key false Indique s'il faut hacher ou non la clé de stockage. Avec la clé de stockage hachée, il est impossible de déchiffrer les données côté serveur sans avoir également un cookie, utilisez nil, true ou false.
hash_subject false Indique s'il faut hacher ou non le sujet lorsque store_metadata est activé, par exemple pour des raisons de PII.
store_metadata false Indique s'il faut également stocker les métadonnées des sessions, telles que la collecte de données de sessions pour un public spécifique appartenant à un sujet spécifique.
touch_threshold 60 Le seuil de touche contrôle à quelle fréquence ou infrequent le session:refresh touche le cookie, par exemple 60 (une minute) (en secondes)
compression_threshold 1024 Le seuil de compression contrôle quand les données sont dégonflées, par exemple 1024 (un kilooctet) (en octets), 0 désactive la compression.
bind nil Lier la session aux données acquises à partir de la requête HTTP ou de la connexion, utilisez ip, scheme, user-agent. Par exemple, { "scheme", "user-agent" } calculera le MAC en utilisant également la requête HTTP Scheme et l'en-tête User-Agent.
request_headers nil Ensemble d'en-têtes à envoyer à l'amont, utilisez id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Par exemple, { "id", "timeout" } définira les en-têtes de requête Session-Id et Session-Timeout lorsque set_headers est appelé.
response_headers nil Ensemble d'en-têtes à envoyer à l'aval, utilisez id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Par exemple, { "id", "timeout" } définira les en-têtes de réponse Session-Id et Session-Timeout lorsque set_headers est appelé.
storage nil Le stockage est responsable du stockage des données de session, utilisez nil ou "cookie" (les données sont stockées dans un cookie), "dshm", "file", "memcached", "mysql", "postgres", "redis" ou "shm", ou donnez un nom de module personnalisé ("custom-storage"), ou une table qui implémente l'interface de stockage de session.
dshm nil Configuration pour le stockage dshm, par exemple { prefix = "sessions" } (voir ci-dessous)
file nil Configuration pour le stockage de fichiers, par exemple { path = "/tmp", suffix = "session" } (voir ci-dessous)
memcached nil Configuration pour le stockage memcached, par exemple { prefix = "sessions" } (voir ci-dessous)
mysql nil Configuration pour le stockage MySQL / MariaDB, par exemple { database = "sessions" } (voir ci-dessous)
postgres nil Configuration pour le stockage Postgres, par exemple { database = "sessions" } (voir ci-dessous)
redis nil Configuration pour les stockages Redis / Redis Sentinel / Redis Cluster, par exemple { prefix = "sessions" } (voir ci-dessous)
shm nil Configuration pour le stockage en mémoire partagée, par exemple { zone = "sessions" }
["custom-storage"] nil configuration de stockage personnalisé (chargée avec require "custom-storage")

Lors du stockage de données dans un cookie, aucune configuration supplémentaire n'est requise, il suffit de définir le storage sur nil ou "cookie".

Configuration de Stockage DSHM

Avec le stockage DSHM, vous pouvez utiliser les paramètres suivants (définissez le storage sur "dshm"):

Option Par défaut Description
prefix nil Le préfixe pour les clés stockées dans DSHM.
suffix nil Le suffixe pour les clés stockées dans DSHM.
host "127.0.0.1" L'hôte à connecter.
port 4321 Le port à connecter.
connect_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode connect de l'objet socket TCP/domaine-unix.
send_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode send de l'objet socket TCP/domaine-unix.
read_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode receive de l'objet socket TCP/domaine-unix.
keepalive_timeout nil Contrôle le temps d'inactivité maximal par défaut des connexions dans le pool de connexions.
pool nil Un nom personnalisé pour le pool de connexions utilisé.
pool_size nil La taille du pool de connexions.
backlog nil Une taille de file d'attente à utiliser lorsque le pool de connexions est plein (configuré avec pool_size).
ssl nil Activer SSL.
ssl_verify nil Vérifier le certificat du serveur.
server_name nil Le nom du serveur pour la nouvelle extension TLS Server Name Indication (SNI).

Veuillez vous référer à ngx-distributed-shm pour installer les dépendances nécessaires.

Configuration de Stockage de Fichiers

Avec le stockage de fichiers, vous pouvez utiliser les paramètres suivants (définissez le storage sur "file"):

Option Par défaut Description
prefix nil Préfixe de fichier pour le fichier de session.
suffix nil Suffixe de fichier (ou extension sans .) pour le fichier de session.
pool nil Nom du pool de threads sous lequel l'écriture de fichiers se produit (disponible uniquement sur Linux).
path (répertoire tmp) Chemin (ou répertoire) sous lequel les fichiers de session sont créés.

L'implémentation nécessite LuaFileSystem que vous pouvez installer avec LuaRocks :

 luarocks install LuaFileSystem

Configuration de Stockage Memcached

Avec le stockage Memcached, vous pouvez utiliser les paramètres suivants (définissez le storage sur "memcached"):

Option Par défaut Description
prefix nil Préfixe pour les clés stockées dans memcached.
suffix nil Suffixe pour les clés stockées dans memcached.
host 127.0.0.1 L'hôte à connecter.
port 11211 Le port à connecter.
socket nil Le fichier socket à connecter.
connect_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode connect de l'objet socket TCP/domaine-unix.
send_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode send de l'objet socket TCP/domaine-unix.
read_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode receive de l'objet socket TCP/domaine-unix.
keepalive_timeout nil Contrôle le temps d'inactivité maximal par défaut des connexions dans le pool de connexions.
pool nil Un nom personnalisé pour le pool de connexions utilisé.
pool_size nil La taille du pool de connexions.
backlog nil Une taille de file d'attente à utiliser lorsque le pool de connexions est plein (configuré avec pool_size).
ssl false Activer SSL
ssl_verify nil Vérifier le certificat du serveur
server_name nil Le nom du serveur pour la nouvelle extension TLS Server Name Indication (SNI).

Configuration de Stockage MySQL / MariaDB

Avec le stockage MySQL / MariaDB, vous pouvez utiliser les paramètres suivants (définissez le storage sur "mysql"):

Option Par défaut Description
host "127.0.0.1" L'hôte à connecter.
port 3306 Le port à connecter.
socket nil Le fichier socket à connecter.
username nil Le nom d'utilisateur de la base de données pour l'authentification.
password nil Mot de passe pour l'authentification, peut être requis en fonction de la configuration du serveur.
charset "ascii" L'ensemble de caractères utilisé sur la connexion MySQL.
database nil Le nom de la base de données à connecter.
table_name "sessions" Nom de la table de base de données dans laquelle stocker les données de session.
table_name_meta "sessions_meta" Nom de la table de métadonnées de la base de données dans laquelle stocker les métadonnées de session.
max_packet_size 1048576 La limite supérieure pour les paquets de réponse envoyés par le serveur MySQL (en octets).
connect_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode connect de l'objet socket TCP/domaine-unix.
send_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode send de l'objet socket TCP/domaine-unix.
read_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode receive de l'objet socket TCP/domaine-unix.
keepalive_timeout nil Contrôle le temps d'inactivité maximal par défaut des connexions dans le pool de connexions.
pool nil Un nom personnalisé pour le pool de connexions utilisé.
pool_size nil La taille du pool de connexions.
backlog nil Une taille de file d'attente à utiliser lorsque le pool de connexions est plein (configuré avec pool_size).
ssl false Activer SSL.
ssl_verify nil Vérifier le certificat du serveur.

Vous devez également créer les tables suivantes dans votre base de données :

--
-- Table de base de données qui stocke les données de session.
--
CREATE TABLE IF NOT EXISTS sessions (
  sid  CHAR(43) PRIMARY KEY,
  name VARCHAR(255),
  data MEDIUMTEXT,
  exp  DATETIME,
  INDEX (exp)
) CHARACTER SET ascii;

--
-- Table des métadonnées des sessions.
--
-- Ceci est uniquement nécessaire si vous souhaitez stocker des métadonnées de session.
--
CREATE TABLE IF NOT EXISTS sessions_meta (
  aud VARCHAR(255),
  sub VARCHAR(255),
  sid CHAR(43),
  PRIMARY KEY (aud, sub, sid),
  CONSTRAINT FOREIGN KEY (sid) REFERENCES sessions(sid) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET ascii;

Configuration Postgres

Avec le stockage Postgres, vous pouvez utiliser les paramètres suivants (définissez le storage sur "postgres"):

Option Par défaut Description
host "127.0.0.1" L'hôte à connecter.
port 5432 Le port à connecter.
application 5432 Définir le nom de la connexion tel qu'affiché dans pg_stat_activity (par défaut "pgmoon").
username "postgres" Le nom d'utilisateur de la base de données pour l'authentification.
password nil Mot de passe pour l'authentification, peut être requis en fonction de la configuration du serveur.
database nil Le nom de la base de données à connecter.
table_name "sessions" Nom de la table de base de données dans laquelle stocker les données de session (peut être préfixé par schéma de base de données).
table_name_meta "sessions_meta" Nom de la table de métadonnées de la base de données dans laquelle stocker les métadonnées de session (peut être préfixé par schéma de base de données).
connect_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode connect de l'objet socket TCP/domaine-unix.
send_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode send de l'objet socket TCP/domaine-unix.
read_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode receive de l'objet socket TCP/domaine-unix.
keepalive_timeout nil Contrôle le temps d'inactivité maximal par défaut des connexions dans le pool de connexions.
pool nil Un nom personnalisé pour le pool de connexions utilisé.
pool_size nil La taille du pool de connexions.
backlog nil Une taille de file d'attente à utiliser lorsque le pool de connexions est plein (configuré avec pool_size).
ssl false Activer SSL.
ssl_verify nil Vérifier le certificat du serveur.
ssl_required nil Abandonner la connexion si le serveur ne prend pas en charge les connexions SSL.

Vous devez également créer les tables suivantes dans votre base de données :

--
-- Table de base de données qui stocke les données de session.
--
CREATE TABLE IF NOT EXISTS sessions (
  sid  TEXT PRIMARY KEY,
  name TEXT,
  data TEXT,
  exp  TIMESTAMP WITH TIME ZONE
);
CREATE INDEX ON sessions (exp);

--
-- Table des métadonnées des sessions.
--
-- Ceci est uniquement nécessaire si vous souhaitez stocker des métadonnées de session.
--
CREATE TABLE IF NOT EXISTS sessions_meta (
  aud TEXT,
  sub TEXT,
  sid TEXT REFERENCES sessions (sid) ON DELETE CASCADE ON UPDATE CASCADE,
  PRIMARY KEY (aud, sub, sid)
);

L'implémentation nécessite pgmoon que vous pouvez installer avec LuaRocks :

 luarocks install pgmoon

Configuration Redis

La bibliothèque de session prend en charge les connexions Redis simples, Redis Sentinel et Redis Cluster. Les paramètres de configuration communs parmi eux tous :

Option Par défaut Description
prefix nil Préfixe pour les clés stockées dans Redis.
suffix nil Suffixe pour les clés stockées dans Redis.
username nil Le nom d'utilisateur de la base de données pour l'authentification.
password nil Mot de passe pour l'authentification.
connect_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode connect de l'objet socket TCP/domaine-unix.
send_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode send de l'objet socket TCP/domaine-unix.
read_timeout nil Contrôle la valeur de délai d'attente par défaut utilisée dans la méthode receive de l'objet socket TCP/domaine-unix.
keepalive_timeout nil Contrôle le temps d'inactivité maximal par défaut des connexions dans le pool de connexions.
pool nil Un nom personnalisé pour le pool de connexions utilisé.
pool_size nil La taille du pool de connexions.
backlog nil Une taille de file d'attente à utiliser lorsque le pool de connexions est plein (configuré avec pool_size).
ssl false Activer SSL
ssl_verify nil Vérifier le certificat du serveur
server_name nil Le nom du serveur pour la nouvelle extension TLS Server Name Indication (SNI).

L'implémentation redis simple est sélectionnée lorsque vous ne passez ni sentinels ni nodes, ce qui conduirait à sélectionner l'implémentation sentinel ou cluster.

Configuration Redis Simple

Redis simple a les options de configuration supplémentaires suivantes (définissez le storage sur "redis"):

Option Par défaut Description
host "127.0.0.1" L'hôte à connecter.
port 6379 Le port à connecter.
socket nil Le fichier socket à connecter.

Configuration des Sentinelles Redis

Redis Sentinel a les options de configuration supplémentaires suivantes (définissez le storage sur "redis" et configurez les sentinels):

Option Par défaut Description
master nil Nom du maître.
role nil "master" ou "slave".
socket nil Le fichier socket à connecter.
sentinels nil Sentinelles Redis.
sentinel_username nil Nom d'utilisateur de sentinelle optionnel.
sentinel_password nil Mot de passe de sentinelle optionnel.
database nil La base de données à connecter.

Les sentinels sont un tableau d'enregistrements de Sentinel :

Option Par défaut Description
host nil L'hôte à connecter.
port nil Le port à connecter.

L'implémentation sentinel est sélectionnée lorsque vous passez sentinels comme partie de la configuration redis (et ne passez pas nodes, ce qui sélectionnerait l'implémentation cluster).

L'implémentation nécessite lua-resty-redis-connector que vous pouvez installer avec LuaRocks :

 luarocks install lua-resty-redis-connector

Configuration du Cluster Redis

Redis Cluster a les options de configuration supplémentaires suivantes (définissez le storage sur "redis" et configurez les nodes):

Option Par défaut Description
name nil Nom du cluster Redis.
nodes nil Nœuds du cluster Redis.
lock_zone nil Nom du dictionnaire partagé pour les verrous.
lock_prefix nil Préfixe du nom du dictionnaire partagé pour le verrou.
max_redirections nil Tentatives de réessai maximales pour la redirection.
max_connection_attempts nil Tentatives de réessai maximales pour la connexion.
max_connection_timeout nil Délai d'attente maximal de connexion total parmi les réessais.

Les nodes sont un tableau d'enregistrements de nœuds de Cluster :

Option Par défaut Description
ip "127.0.0.1" L'adresse IP à connecter.
port 6379 Le port à connecter.

L'implémentation cluster est sélectionnée lorsque vous passez nodes comme partie de la configuration redis.

Pour que le cluster fonctionne correctement, vous devez configurer lock_zone, donc ajoutez également cela à votre configuration Nginx :

lua_shared_dict redis_cluster_locks 100k;

Et définissez lock_zone sur "redis_cluster_locks"

L'implémentation nécessite resty-redis-cluster ou kong-redis-cluster que vous pouvez installer avec LuaRocks :

 luarocks install resty-redis-cluster
## ou luarocks install kong-redis-cluster

Configuration SHM

Avec le stockage SHM, vous pouvez utiliser les paramètres suivants (définissez le storage sur "shm"):

Option Par défaut Description
prefix nil Préfixe pour les clés stockées dans SHM.
suffix nil Suffixe pour les clés stockées dans SHM.
zone "sessions" Un nom de zone de mémoire partagée.

Vous devrez également créer un dictionnaire partagé zone dans Nginx :

lua_shared_dict sessions 10m;

Remarque : vous devrez peut-être ajuster la taille de la zone de mémoire partagée en fonction de vos besoins.

API

Les documents API générés par LDoc peuvent également être consultés à bungle.github.io/lua-resty-session.

Initialisation

session.init

syntax: session.init(configuration)

Initialise la bibliothèque de session.

Cette fonction peut être appelée lors des phases init ou init_worker sur OpenResty pour définir la configuration par défaut globale pour toutes les instances de session créées par cette bibliothèque.

require "resty.session".init({
  audience = "my-application",
  storage = "redis",
  redis = {
    username = "session",
    password = "storage",
  },
})

Voir configuration pour les paramètres de configuration possibles.

Constructeurs

session.new

syntax: session = session.new(configuration)

Crée une nouvelle instance de session.

local session = require "resty.session".new()
-- OU
local session = require "resty.session".new({
  audience = "my-application",
})

Voir configuration pour les paramètres de configuration possibles.

Helpers

session.open

syntax: session, err, exists = session.open(configuration)

Cela peut être utilisé pour ouvrir une session, et cela renverra soit une session existante soit une nouvelle session. Le paramètre de retour exists (un booléen) indique si c'était une session existante ou nouvelle qui a été renvoyée. Le err (une chaîne) contient un message expliquant pourquoi l'ouverture a pu échouer (la fonction renverra toujours session aussi).

local session = require "resty.session".open()
-- OU
local session, err, exists = require "resty.session".open({
  audience = "my-application",
})

Voir configuration pour les paramètres de configuration possibles.

session.start

syntax: session, err, exists, refreshed = session.start(configuration)

Cela peut être utilisé pour démarrer une session, et cela renverra soit une session existante soit une nouvelle session. Dans le cas où il existe une session existante, la session sera également rafraîchie (si nécessaire). Le paramètre de retour exists (un booléen) indique si c'était une session existante ou nouvelle qui a été renvoyée. Le refreshed (un booléen) indique si l'appel à refresh a été réussi. Le err (une chaîne) contient un message expliquant pourquoi l'ouverture ou le rafraîchissement a pu échouer (la fonction renverra toujours session aussi).

local session = require "resty.session".start()
-- OU
local session, err, exists, refreshed = require "resty.session".start({
  audience = "my-application",
})

Voir configuration pour les paramètres de configuration possibles.

session.logout

syntax: ok, err, exists, logged_out = session.logout(configuration)

Cela déconnecte d'un public spécifique.

Un seul cookie de session peut être partagé entre plusieurs publics (ou applications), d'où la nécessité de pouvoir se déconnecter d'un seul public tout en conservant la session pour les autres publics. Le paramètre de retour exists (un booléen) indique si la session existait. Le paramètre de retour logged_out (un booléen) signale si la session existait et a également été déconnectée. Le err (une chaîne) contient une raison pour laquelle la session n'existait pas ou pourquoi la déconnexion a échoué. Le ok (véridique) sera true lorsque la session existait et a été déconnectée avec succès.

Lorsqu'il n'y a qu'un seul public, cela peut être considéré comme équivalent à session.destroy.

Lorsque le dernier public est déconnecté, le cookie sera également détruit et invalidé sur un client.

require "resty.session".logout()
-- OU
local ok, err, exists, logged_out = require "resty.session".logout({
  audience = "my-application",
})

Voir configuration pour les paramètres de configuration possibles.

session.destroy

syntax: ok, err, exists, destroyed = session.destroy(configuration)

Cela détruit la session entière et efface les cookies.

Un seul cookie de session peut être partagé entre plusieurs publics (ou applications), d'où la nécessité de pouvoir se déconnecter d'un seul public tout en conservant la session pour les autres publics. Le paramètre de retour exists (un booléen) indique si la session existait. Le paramètre de retour destroyed (un booléen) signale si la session existait et a également été détruite. Le err (une chaîne) contient une raison pour laquelle la session n'existait pas ou pourquoi la déconnexion a échoué. Le ok (véridique) sera true lorsque la session existait et a été déconnectée avec succès.

require "resty.session".destroy()
-- OU
local ok, err, exists, destroyed = require "resty.session".destroy({
  cookie_name = "auth",
})

Voir configuration pour les paramètres de configuration possibles.

Méthodes d'Instance

session:open

syntax: ok, err = session:open()

Cela peut être utilisé pour ouvrir une session. Il renvoie true lorsque la session a été ouverte et validée. Sinon, il renvoie nil et un message d'erreur.

local session = require "resty.session".new()
local ok, err = session:open()
if ok then
  -- la session existe

else
  -- la session n'existait pas ou était invalide
end

session:save

syntax: ok, err = session:save()

Sauvegarde les données de session et émet un nouveau cookie de session avec un nouvel identifiant de session. Lorsque remember est activé, il émettra également un nouveau cookie persistant et pourrait éventuellement sauvegarder les données dans le stockage backend. Il renvoie true lorsque la session a été sauvegardée. Sinon, il renvoie nil et un message d'erreur.

local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
  -- erreur lors de la sauvegarde de la session
end

session:touch

syntax: ok, err = session:touch()

Met à jour le décalage d'inactivité de la session en envoyant un cookie de session mis à jour. Il envoie uniquement le cookie client et n'appelle jamais aucune API de stockage de session backend. Normalement, le session:refresh est utilisé pour appeler cela indirectement. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true.

local session, err, exists = require "resty.session".open()
if exists then
  ok, err = session:touch()
end

session:refresh

syntax: ok, err = session:refresh()

Sauvegarde soit la session (créant un nouvel identifiant de session) soit touche la session en fonction de la proximité du délai d'attente roulant, ce qui signifie par défaut lorsque 3/4 du délai d'attente roulant est écoulé, c'est-à-dire 45 minutes avec un délai d'attente roulant par défaut d'une heure. Le toucher a un seuil, par défaut d'une minute, il peut donc être ignoré dans certains cas (vous pouvez appeler session:touch() pour le forcer). En cas d'erreur, il renvoie nil et un message d'erreur, sinon true.

local session, err, exists = require "resty.session".open()
if exists then
  local ok, err = session:refresh()
end

Le code ci-dessus ressemble un peu à l'assistant session.start().

session:logout

syntax: ok, err = session:logout()

La déconnexion détruit soit la session, soit efface simplement les données pour le public actuel, et les sauvegarde (se déconnectant du public actuel). En cas d'erreur, il renvoie nil et un message d'erreur, sinon true.

local session, err, exists = require "resty.session".open()
if exists then
  local ok, err = session:logout()
end

session:destroy

syntax: ok, err = session:destroy()

Détruit la session et efface les cookies. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true.

local session, err, exists = require "resty.session".open()
if exists then
  local ok, err = session:destroy()
end

session:close

syntax: session:close()

Ferme simplement l'instance de session afin qu'elle ne puisse plus être utilisée.

local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
  -- erreur lors de la sauvegarde de la session
end
session:close()

session:set_data

syntax: session:set_data(data)

Définir les données de session. Les data doivent être une table.

local session, err, exists = require "resty.session".open()
if not exists then
   session:set_data({
     cart = {},
   })
  session:save()
end

session:get_data

syntax: data = session:get_data()

Obtenir les données de session.

local session, err, exists = require "resty.session".open()
if exists then
  local data = session:get_data()
  ngx.req.set_header("Authorization", "Bearer " .. data.access_token)
end

session:set

syntax: session:set(key, value)

Définir une valeur dans la session.

local session, err, exists = require "resty.session".open()
if not exists then
  session:set("access-token", "eyJ...")
  session:save()
end

session:get

syntax: value = session:get(key)

Obtenir une valeur de la session.

local session, err, exists = require "resty.session".open()
if exists then
  local access_token = session:get("access-token")
  ngx.req.set_header("Authorization", "Bearer " .. access_token)
end

session:set_audience

syntax: session:set_audience(audience)

Définir le public de la session.

local session = require "resty.session".new()
session.set_audience("my-service")

session:get_audience

syntax: audience = session:get_audience()

Définir le sujet de la session.

session:set_subject

syntax: session:set_subject(subject)

Définir le sujet de la session.

local session = require "resty.session".new()
session.set_subject("[email protected]")

session:get_subject

syntax: subject = session:get_subject()

Obtenir le sujet de la session.

local session, err, exists = require "resty.session".open()
if exists then
  local subject = session.get_subject()
end

session:get_property

syntax: value = session:get_property(name)

Obtenir une propriété de session. Noms de propriétés possibles :

  • "id": identifiant de session de 43 octets (identique à nonce, mais encodé en base64 url)
  • "nonce": nonce de 32 octets (identique à l'identifiant de session mais en octets bruts)
  • "audience": Public actuel de la session
  • "subject": Sujet actuel de la session
  • "timeout": Délai d'attente le plus proche (en secondes) (ce qu'il reste)
  • "idling-timeout"`: Délai d'attente d'inactivité de la session (en secondes) (ce qu'il reste)
  • "rolling-timeout"`: Délai d'attente roulant de la session (en secondes) (ce qu'il reste)
  • "absolute-timeout"`: Délai d'attente absolu de la session (en secondes) (ce qu'il reste)

Remarque : la valeur renvoyée peut être nil.

local session, err, exists = require "resty.session".open()
if exists then
  local timeout = session.get_property("timeout")
end

session:set_remember

syntax: session:set_remember(value)

Définir les sessions persistantes activées ou désactivées.

Dans de nombreux formulaires de connexion, l'utilisateur a la possibilité de "se souvenir de moi". Vous pouvez appeler cette fonction en fonction de ce que l'utilisateur a sélectionné.

local session = require "resty.session".new()
if ngx.var.args.remember then
  session:set_remember(true)
end
session:set_subject(ngx.var.args.username)
session:save()

session:get_remember

syntax: remember = session:get_remember()

Obtenir l'état des sessions persistantes.

local session, err, exists = require "resty.session".open()
if exists then
  local remember = session.get_remember()
end

syntax: ok, err = session:clear_request_cookie()

Modifie les en-têtes de requête en supprimant les cookies liés à la session. Cela est utile lorsque vous utilisez la bibliothèque de session sur un serveur proxy et que vous ne souhaitez pas que les cookies de session soient transmis au service en amont. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true (qui peut être ignoré).

local session, err, exists = require "resty.session".open()
if exists then
  session:clear_request_cookie()
end

session:set_headers

syntax: ok, err = session:set_headers(arg1, arg2, ...)

Définit les en-têtes de requête et de réponse en fonction de la configuration. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true (qui peut être ignoré).

local session, err, exists = require "resty.session".open({
  request_headers = { "audience", "subject", "id" },
  response_headers = { "timeout", "idling-timeout", "rolling-timeout", "absolute-timeout" },
})
if exists then
  session:set_headers()
end

Lorsqu'il est appelé sans arguments, il définira les en-têtes de requête configurés avec request_headers et les en-têtes de réponse configurés avec response_headers.

Voir configuration pour les noms d'en-têtes possibles.

session:set_request_headers

syntax: ok, err = session:set_request_headers(arg1, arg2, ...)

Définir les en-têtes de requête. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true (qui peut être ignoré).

local session, err, exists = require "resty.session".open()
if exists then
  session:set_request_headers("audience", "subject", "id")
end

Lorsqu'il est appelé sans arguments, il définira les en-têtes de requête configurés avec request_headers.

Voir configuration pour les noms d'en-têtes possibles.

session:set_response_headers

syntax: ok, err = session:set_response_headers(arg1, arg2, ...)

Définir les en-têtes de réponse. En cas d'erreur, il renvoie nil et un message d'erreur, sinon true (qui peut être ignoré).

local session, err, exists = require "resty.session".open()
if exists then
  session:set_response_headers("timeout", "idling-timeout", "rolling-timeout", "absolute-timeout")
end

Lorsqu'il est appelé sans arguments, il définira les en-têtes de réponse configurés avec response_headers.

Voir configuration pour les noms d'en-têtes possibles.

session.info:set

syntax: session.info:set(key, value)

Définir une valeur dans le stockage d'informations de session. Le stockage d'informations de session peut être utilisé dans des scénarios où vous souhaitez stocker des données sur le stockage côté serveur, mais ne souhaitez pas créer une nouvelle session et envoyer un nouveau cookie de session. Les données du stockage d'informations ne sont pas considérées lors de la vérification de l'étiquette d'authentification ou du code d'authentification de message. Ainsi, si vous souhaitez utiliser cela pour des données qui doivent être chiffrées, vous devez chiffrer la valeur avant de la passer à cette fonction.

local session, err, exists = require "resty.session".open()
if exists then
  session.info:set("last-access", ngx.now())
  session.info:save()
end

Avec le stockage de cookies, cela fonctionne toujours, mais c'est alors presque identique à session:set.

session.info:get

syntax: value = session.info:get(key)

Obtenir une valeur du stockage d'informations de session.

local session, err, exists = require "resty.session".open()
if exists then
  local last_access = session.info:get("last-access")
end

session.info:save

syntax: ok, err = session.info:save()

Sauvegarder les informations. Met uniquement à jour le stockage backend. N'envoie pas un nouveau cookie (sauf avec le stockage de cookies).

local session = require "resty.session".new()
session.info:set("last-access", ngx.now())
local ok, err = session.info:save()
[ HEADER -------------------------------------------------------------------------------------]
[ Type || Flags || SID || Créé à || Décalage roulant || Taille || Tag || Décalage d'inactivité || Mac ]
[ 1B   || 2B    || 32B || 5B         || 4B             || 3B   || 16B || 3B            || 16B ]

et

[ PAYLOAD --]
[ Données  *B  ]

À la fois le HEADER et le PAYLOAD sont encodés en base64 url avant d'être mis dans un en-tête Set-Cookie. Lors de l'utilisation d'un stockage côté serveur, le PAYLOAD n'est pas mis dans le cookie. Avec le stockage de cookies, l'en-tête encodé en base64 url est concaténé avec le payload encodé en base64 url.

Le HEADER a une taille fixe de 82 octets binaires ou 110 octets en forme encodée en base64 url.

Champs d'en-tête expliqués :

  • Type : nombre 1 empaqueté en binaire dans un seul octet little endian (actuellement le seul type pris en charge).
  • Flags : drapeaux empaquetés en binaire (court) sous forme de deux octets little endian.
  • SID : 32 octets de données aléatoires cryptographiques (Identifiant de session).
  • Créé à : secondes empaquetées en binaire depuis l'époque sous forme little endian, tronquées à 5 octets.
  • Décalage roulant : secondes empaquetées en binaire depuis le temps de création sous forme little endian (entier).
  • Taille : taille des données empaquetées en binaire sous forme de trois octets little endian.
  • Tag : 16 octets de tag d'authentification provenant du chiffrement AES-256-GCM des données.
  • Décalage d'inactivité : secondes empaquetées en binaire depuis le temps de création + décalage roulant sous forme little endian, tronquées à 3 octets.
  • Mac : 16 octets de code d'authentification de message de l'en-tête.

Chiffrement des Données

  1. Matériel de clé initial (IKM) :
  2. dériver IKM à partir de secret en hachant secret avec SHA-256, ou
  3. utiliser IKM de 32 octets lorsqu'il est passé à la bibliothèque avec ikm
  4. Générer 32 octets d'identifiant de session aléatoire cryptographique (sid)
  5. Dériver une clé de chiffrement de 32 octets et un vecteur d'initialisation de 12 octets avec HKDF utilisant SHA-256 (en mode FIPS, elle utilise PBKDF2 avec SHA-256 à la place)
  6. Utiliser HKDF extract pour dériver une nouvelle clé à partir de ikm pour obtenir key (cette étape peut être effectuée une seule fois par ikm) :
    • longueur de sortie : 32
    • digest : "sha256"
    • clé : <ikm>
    • mode : extract only
    • info : ""
    • sel : ""
  7. Utiliser HKDF expand pour dériver 44 octets de output :
    • longueur de sortie : 44
    • digest : "sha256"
    • clé : <key>
    • mode : expand only
    • info : "encryption:<sid>"
    • sel : ""
  8. Les 32 premiers octets de output sont la clé de chiffrement (aes-key), et les 12 derniers octets sont le vecteur d'initialisation (iv)
  9. Chiffrer plaintext (encodé en JSON et éventuellement dégonflé) en utilisant AES-256-GCM pour obtenir ciphertext et tag
  10. chiffre : "aes-256-gcm"
  11. clé : <aes-key>
  12. iv : <iv>
  13. texte en clair : <plaintext>
  14. aad : utiliser les 47 premiers octets de header comme aad, cela inclut :
    1. Type
    2. Flags
    3. Identifiant de session
    4. Temps de création
    5. Décalage roulant
    6. Taille des données

Il existe une variation pour les cookies remember à l'étape 3, où nous pouvons utiliser PBKDF2 au lieu de HKDF, en fonction du paramètre remember_safety (nous l'utilisons également en mode FIPS). Les paramètres PBKDF2 :

  • longueur de sortie : 44
  • digest : "sha256"
  • mot de passe : <key>
  • sel : "encryption:<sid>"
  • itérations : <1000|10000|100000|1000000>
  • pkcs5 : 1 (conforme à FIPS dans notre cas d'utilisation, mais il est nécessaire de désactiver les vérifications basées sur SP800-132, telles que le nombre d'itérations, voir : https://docs.openssl.org/master/man7/provider-kdf/#kdf-parameters)

Les comptes d'itération sont basés sur le paramètre remember_safety ("Low", "Medium", "High", "Very High"), si remember_safety est défini sur "None", nous utiliserons le HDKF comme ci-dessus.

Remarque : Pour des raisons de compatibilité ascendante, nous avons désactivé les vérifications de conformité SP800-132 en mode FIPS. Cela vérifie que la longueur du sel est d'au moins 128 bits, que la longueur de la clé dérivée est d'au moins 112 bits, et que le nombre d'itérations est d'au moins 1000. Ces vérifications sont désactivées par défaut dans le fournisseur par défaut d'OpenSSL, mais sont activées par défaut dans le fournisseur FIPS.

  1. Dériver une clé d'authentification de 32 octets (mac_key) avec HKDF utilisant SHA-256 (en mode FIPS, elle utilise PBKDF2 avec SHA-256 à la place) :
    1. Utiliser HKDF extract pour dériver une nouvelle clé à partir de ikm pour obtenir key (cette étape peut être effectuée une seule fois par ikm et réutilisée avec la génération de clé de chiffrement) :
      • longueur de sortie : 32
      • digest : "sha256"
      • clé : <ikm>
      • mode : extract only
      • info : ""
      • sel : ""
    2. Utiliser HKDF expand pour dériver 32 octets de mac-key :
      • longueur de sortie : 32
      • digest : "sha256"
      • clé : <key>
      • mode : expand only
      • info : "authentication:<sid>"
      • sel : ""
  2. Calculer le code d'authentification de message en utilisant HMAC-SHA256 :
  3. digest : "sha256"
  4. clé : <mac-key>
  5. message : utiliser les 66 premiers octets de header, cela inclut :
    1. Type
    2. Flags
    3. Identifiant de session
    4. Temps de création
    5. Décalage roulant
    6. Taille des données
    7. Tag
    8. Décalage d'inactivité

Interface de Stockage Personnalisée

Si vous souhaitez implémenter un stockage personnalisé, vous devez implémenter l'interface suivante :

---
-- <custom> backend pour la bibliothèque de session
--
-- @module <custom>


---
-- Stockage
-- @section instance


local metatable = {}


metatable.__index = metatable


function metatable.__newindex()
  error("tentative de mise à jour d'une table en lecture seule", 2)
end


---
-- Stocker les données de session.
--
-- @function instance:set
-- @tparam string name nom du cookie
-- @tparam string key clé de session
-- @tparam string value valeur de session
-- @tparam number ttl ttl de session
-- @tparam number current_time temps actuel
-- @tparam[opt] string old_key ancien identifiant de session
-- @tparam string stale_ttl ttl obsolète
-- @tparam[opt] table metadata table de métadonnées
-- @treturn true|nil ok
-- @treturn string message d'erreur
function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata)
  -- NYI
end


---
-- Récupérer les données de session.
--
-- @function instance:get
-- @tparam string name nom du cookie
-- @tparam string key clé de session
-- @treturn string|nil données de session
-- @treturn string message d'erreur
function metatable:get(name, key)
  -- NYI
end


---
-- Supprimer les données de session.
--
-- @function instance:delete
-- @tparam string name nom du cookie
-- @tparam string key clé de session
-- @tparam[opt] table metadata métadonnées de session
-- @treturn boolean|nil données de session
-- @treturn string message d'erreur
function metatable:delete(name, key, current_time, metadata)
  -- NYI
end


local storage = {}


---
-- Constructeurs
-- @section constructors


---
-- Configuration
-- @section configuration


---
-- Configuration de stockage <custom>
-- @field <field-name> TBD
-- @table configuration


---
-- Créer un stockage <custom>.
--
-- Cela crée une nouvelle instance de stockage en mémoire partagée.
--
-- @function module.new
-- @tparam[opt]  table   configuration  stockage <custom> @{configuration}
-- @treturn      table                  instance de stockage <custom>
function storage.new(configuration)
  -- NYI
  -- return setmetatable({}, metatable)
end


return storage

Veuillez consulter les implémentations existantes pour les détails. Et veuillez faire une demande de tirage afin que nous puissions l'intégrer directement à la bibliothèque pour d'autres utilisateurs également.

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-session.