session: Biblioteca de sesiones para nginx-module-lua – flexible y segura
Instalación
Si aún no has configurado la suscripción al repositorio RPM, regístrate. Luego, puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-session v4.1.5 lanzado el 24 de noviembre de 2025.
lua-resty-session es una biblioteca de sesiones segura y flexible para OpenResty.
TL;DR;
- Las sesiones son inmutables (cada guardado genera una nueva sesión) y sin bloqueo.
- Los datos de la sesión están cifrados con AES-256-GCM con una clave derivada usando HKDF-SHA256 (en modo FIPS utiliza PBKDF2 con SHA-256 en su lugar).
- La sesión tiene un encabezado de tamaño fijo que está protegido con HMAC-SHA256 MAC con una clave derivada usando HKDF-SHA256 (en modo FIPS utiliza PBKDF2 con SHA-256 en su lugar).
- Los datos de la sesión pueden almacenarse en una cookie sin estado o en varios almacenes de backend.
- Una sola cookie de sesión puede mantener múltiples sesiones a través de diferentes audiencias.
Nota: La versión 4.0.0 fue una reescritura de esta biblioteca con muchas lecciones aprendidas a lo largo de los años. Si aún usas una versión anterior, consulta la documentación antigua.
Sinopsis
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>Iniciar la prueba</a>
</body>
</html>
]])
}
}
location /start {
content_by_lua_block {
local session = require "resty.session".new()
session:set_subject("Fan de OpenResty")
session:set("quote", "El rápido zorro marrón salta sobre el perro perezoso")
local ok, err = session:save()
ngx.say(string.format([[
<html>
<body>
<p>Sesión iniciada (%s)</p>
<p><a href=/started>Ver si realmente fue</a></p>
</body>
</html>
]], err or "sin error"))
}
}
location /started {
content_by_lua_block {
local session, err = require "resty.session".start()
ngx.say(string.format([[
<html>
<body>
<p>La sesión fue iniciada por %s (%s)</p>
<p><blockquote>%s</blockquote></p>
<p><a href=/modify>Modificar la sesión</a></p>
</body>
</html>
]],
session:get_subject() or "Anónimo",
err or "sin error",
session:get("quote") or "sin cita"
))
}
}
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 sesión fue modificada (%s)</p>
<p><a href=/modified>Ver si está modificada</a></p>
</body>
</html>
]], err or err_save or "sin error"))
}
}
location /modified {
content_by_lua_block {
local session, err = require "resty.session".start()
ngx.say(string.format([[
<html>
<body>
<p>La sesión fue iniciada por %s (%s)</p>
<p><blockquote>%s</blockquote></p>
<p><a href=/destroy>Destruir la sesión</a></p>
</body>
</html>
]],
session:get_subject() or "Anónimo",
err or "sin error",
session:get("quote") or "sin cita"
))
}
}
location /destroy {
content_by_lua_block {
local ok, err = require "resty.session".destroy()
ngx.say(string.format([[
<html>
<body>
<p>La sesión fue destruida (%s)</p>
<p><a href=/destroyed>¿Verificar que realmente fue?</a></p>
</body>
</html>
]], err or "sin error"))
}
}
location /destroyed {
content_by_lua_block {
local session, err = require "resty.session".open()
ngx.say(string.format([[
<html>
<body>
<p>La sesión fue realmente destruida, eres conocido como %s (%s)</p>
<p><a href=/>Iniciar de nuevo</a></p>
</body>
</html>
]],
session:get_subject() or "Anónimo",
err or "sin error"
))
}
}
}
}
Configuración
La configuración se puede dividir en configuración de sesión genérica y configuración de almacenamiento del lado del servidor.
Aquí hay un ejemplo:
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",
},
})
}
Configuración de Sesión
La configuración de la sesión se puede pasar a las funciones de inicialización, constructor y ayuda.
Aquí están las posibles opciones de configuración de sesión:
| Opción | Predeterminado | Descripción |
|---|---|---|
secret |
nil |
Secreto utilizado para la derivación de la clave. El secreto se hash con SHA-256 antes de usarlo. Ej. "RaJKp8UQW1". |
secret_fallbacks |
nil |
Array de secretos que pueden usarse como secretos alternativos (al hacer rotación de claves), Ej. { "6RfrAYYzYq", "MkbTkkyF9C" }. |
ikm |
(aleatorio) | El material de clave inicial (o ikm) se puede especificar directamente (sin usar un secreto) con exactamente 32 bytes de datos. Ej. "5ixIW4QVMk0dPtoIhn41Eh1I9enP2060" |
ikm_fallbacks |
nil |
Array de materiales de clave inicial que pueden usarse como claves alternativas (al hacer rotación de claves), Ej. { "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }. |
cookie_prefix |
nil |
Prefijo de la cookie, usa nil, "__Host-" o "__Secure-". |
cookie_name |
"session" |
Nombre de la cookie de sesión, Ej. "session". |
cookie_path |
"/" |
Ruta de la cookie, Ej. "/". |
cookie_domain |
nil |
Dominio de la cookie, Ej. "example.com" |
cookie_http_only |
true |
Marca la cookie como solo HTTP, usa true o false. |
cookie_secure |
nil |
Marca la cookie como segura, usa nil, true o false. |
cookie_priority |
nil |
Prioridad de la cookie, usa nil, "Low", "Medium" o "High". |
cookie_same_site |
"Lax" |
Política de misma-sitio de la cookie, usa nil, "Lax", "Strict", "None" o "Default" |
cookie_same_party |
nil |
Marca la cookie con la bandera de misma parte, usa nil, true o false. |
cookie_partitioned |
nil |
Marca la cookie con la bandera de particionado, usa nil, true o false. |
remember |
false |
Habilita o deshabilita sesiones persistentes, usa nil, true o false. |
remember_safety |
"Medium" |
Complejidad de derivación de clave de cookie de recuerdo, usa nil, "None" (rápido), "Low", "Medium", "High" o "Very High" (lento). |
remember_cookie_name |
"remember" |
Nombre de la cookie de sesión persistente, Ej. "remember". |
audience |
"default" |
Audiencia de la sesión, Ej. "my-application". |
subject |
nil |
Sujeto de la sesión, Ej. "[email protected]". |
enforce_same_subject |
false |
Cuando se establece en true, las audiencias deben compartir el mismo sujeto. La biblioteca elimina los datos de audiencia que no coinciden con el sujeto al guardar. |
stale_ttl |
10 |
Cuando se guarda la sesión, se crea una nueva sesión, el ttl obsoleto especifica cuánto tiempo puede seguir usándose la antigua, Ej. 10 (en segundos). |
idling_timeout |
900 |
El tiempo de espera de inactividad especifica cuánto tiempo puede estar inactiva la sesión hasta que se considere inválida, Ej. 900 (15 minutos) (en segundos), 0 desactiva las verificaciones y el toque. |
rolling_timeout |
3600 |
El tiempo de espera rodante especifica cuánto tiempo puede usarse la sesión hasta que necesite ser renovada, Ej. 3600 (una hora) (en segundos), 0 desactiva las verificaciones y el rodado. |
absolute_timeout |
86400 |
El tiempo de espera absoluto limita cuánto tiempo puede renovarse la sesión, hasta que se requiera re-autenticación, Ej. 86400 (un día) (en segundos), 0 desactiva las verificaciones. |
remember_rolling_timeout |
604800 |
El tiempo de espera de recuerdo especifica cuánto tiempo se considera válida la sesión persistente, Ej. 604800 (una semana) (en segundos), 0 desactiva las verificaciones y el rodado. |
remember_absolute_timeout |
2592000 |
El tiempo de espera absoluto de recuerdo limita cuánto tiempo puede renovarse la sesión persistente, hasta que se requiera re-autenticación, Ej. 2592000 (30 días) (en segundos), 0 desactiva las verificaciones. |
hash_storage_key |
false |
Si se debe o no hash la clave de almacenamiento. Con la clave de almacenamiento hash, es imposible descifrar datos en el lado del servidor sin tener una cookie también, usa nil, true o false. |
hash_subject |
false |
Si se debe o no hash el sujeto cuando store_metadata está habilitado, Ej. por razones de PII. |
store_metadata |
false |
Si también se deben almacenar los metadatos de las sesiones, como la recopilación de datos de sesiones para una audiencia específica perteneciente a un sujeto específico. |
touch_threshold |
60 |
El umbral de toque controla con qué frecuencia o infrecuencia el session:refresh toca la cookie, Ej. 60 (un minuto) (en segundos) |
compression_threshold |
1024 |
El umbral de compresión controla cuándo se desinflan los datos, Ej. 1024 (un kilobyte) (en bytes), 0 desactiva la compresión. |
bind |
nil |
Vincula la sesión a datos adquiridos de la solicitud HTTP o conexión, usa ip, scheme, user-agent. Ej. { "scheme", "user-agent" } calculará MAC utilizando también Scheme de la solicitud HTTP y el encabezado User-Agent. |
request_headers |
nil |
Conjunto de encabezados para enviar a upstream, usa id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Ej. { "id", "timeout" } establecerá los encabezados de solicitud Session-Id y Session-Timeout cuando se llame a set_headers. |
response_headers |
nil |
Conjunto de encabezados para enviar a downstream, usa id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Ej. { "id", "timeout" } establecerá los encabezados de respuesta Session-Id y Session-Timeout cuando se llame a set_headers. |
storage |
nil |
El almacenamiento es responsable de almacenar los datos de la sesión, usa nil o "cookie" (los datos se almacenan en la cookie), "dshm", "file", "memcached", "mysql", "postgres", "redis" o "shm", o da un nombre de módulo personalizado ("custom-storage"), o una table que implemente la interfaz de almacenamiento de sesión. |
dshm |
nil |
Configuración para almacenamiento dshm, Ej. { prefix = "sessions" } (ver abajo) |
file |
nil |
Configuración para almacenamiento en archivo, Ej. { path = "/tmp", suffix = "session" } (ver abajo) |
memcached |
nil |
Configuración para almacenamiento en memcached, Ej. { prefix = "sessions" } (ver abajo) |
mysql |
nil |
Configuración para almacenamiento en MySQL / MariaDB, Ej. { database = "sessions" } (ver abajo) |
postgres |
nil |
Configuración para almacenamiento en Postgres, Ej. { database = "sessions" } (ver abajo) |
redis |
nil |
Configuración para almacenamiento en Redis / Redis Sentinel / Redis Cluster, Ej. { prefix = "sessions" } (ver abajo) |
shm |
nil |
Configuración para almacenamiento en memoria compartida, Ej. { zone = "sessions" } |
["custom-storage"] |
nil |
configuración de almacenamiento personalizado (cargado con require "custom-storage"). |
Configuración de Almacenamiento en Cookie
Al almacenar datos en una cookie, no se requiere configuración adicional, solo establece el storage en nil o "cookie".
Configuración de Almacenamiento DSHM
Con el almacenamiento DSHM puedes usar la siguiente configuración (establece el storage en "dshm"):
| Opción | Predeterminado | Descripción |
|---|---|---|
prefix |
nil |
El prefijo para las claves almacenadas en DSHM. |
suffix |
nil |
El sufijo para las claves almacenadas en DSHM. |
host |
"127.0.0.1" |
El host al que conectarse. |
port |
4321 |
El puerto al que conectarse. |
connect_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método connect del objeto de socket TCP/dominio-unix. |
send_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método send del objeto de socket TCP/dominio-unix. |
read_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método receive del objeto de socket TCP/dominio-unix. |
keepalive_timeout |
nil |
Controla el tiempo máximo de inactividad de las conexiones en el grupo de conexiones. |
pool |
nil |
Un nombre personalizado para el grupo de conexiones que se está utilizando. |
pool_size |
nil |
El tamaño del grupo de conexiones. |
backlog |
nil |
Un tamaño de cola para usar cuando el grupo de conexiones está lleno (configurado con pool_size). |
ssl |
nil |
Habilitar SSL. |
ssl_verify |
nil |
Verificar el certificado del servidor. |
server_name |
nil |
El nombre del servidor para la nueva extensión TLS Server Name Indication (SNI). |
Por favor, consulta ngx-distributed-shm para instalar las dependencias necesarias.
Configuración de Almacenamiento en Archivo
Con el almacenamiento en archivo puedes usar la siguiente configuración (establece el storage en "file"):
| Opción | Predeterminado | Descripción |
|---|---|---|
prefix |
nil |
Prefijo de archivo para el archivo de sesión. |
suffix |
nil |
Sufijo de archivo (o extensión sin .) para el archivo de sesión. |
pool |
nil |
Nombre del grupo de hilos bajo el cual ocurre la escritura en archivo (disponible solo en Linux). |
path |
(directorio tmp) | Ruta (o directorio) bajo el cual se crean los archivos de sesión. |
La implementación requiere LuaFileSystem que puedes instalar con LuaRocks:
❯ luarocks install LuaFileSystem
Configuración de Almacenamiento en Memcached
Con el almacenamiento en Memcached puedes usar la siguiente configuración (establece el storage en "memcached"):
| Opción | Predeterminado | Descripción |
|---|---|---|
prefix |
nil |
Prefijo para las claves almacenadas en memcached. |
suffix |
nil |
Sufijo para las claves almacenadas en memcached. |
host |
127.0.0.1 |
El host al que conectarse. |
port |
11211 |
El puerto al que conectarse. |
socket |
nil |
El archivo de socket al que conectarse. |
connect_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método connect del objeto de socket TCP/dominio-unix. |
send_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método send del objeto de socket TCP/dominio-unix. |
read_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método receive del objeto de socket TCP/dominio-unix. |
keepalive_timeout |
nil |
Controla el tiempo máximo de inactividad de las conexiones en el grupo de conexiones. |
pool |
nil |
Un nombre personalizado para el grupo de conexiones que se está utilizando. |
pool_size |
nil |
El tamaño del grupo de conexiones. |
backlog |
nil |
Un tamaño de cola para usar cuando el grupo de conexiones está lleno (configurado con pool_size). |
ssl |
false |
Habilitar SSL |
ssl_verify |
nil |
Verificar el certificado del servidor. |
server_name |
nil |
El nombre del servidor para la nueva extensión TLS Server Name Indication (SNI). |
Configuración de Almacenamiento en MySQL / MariaDB
Con el almacenamiento en MySQL / MariaDB puedes usar la siguiente configuración (establece el storage en "mysql"):
| Opción | Predeterminado | Descripción |
|---|---|---|
host |
"127.0.0.1" |
El host al que conectarse. |
port |
3306 |
El puerto al que conectarse. |
socket |
nil |
El archivo de socket al que conectarse. |
username |
nil |
El nombre de usuario de la base de datos para autenticarse. |
password |
nil |
Contraseña para la autenticación, puede ser requerida dependiendo de la configuración del servidor. |
charset |
"ascii" |
El conjunto de caracteres utilizado en la conexión MySQL. |
database |
nil |
El nombre de la base de datos a la que conectarse. |
table_name |
"sessions" |
Nombre de la tabla de base de datos a la que almacenar los datos de sesión. |
table_name_meta |
"sessions_meta" |
Nombre de la tabla de metadatos de la base de datos a la que almacenar los metadatos de sesión. |
max_packet_size |
1048576 |
El límite superior para los paquetes de respuesta enviados desde el servidor MySQL (en bytes). |
connect_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método connect del objeto de socket TCP/dominio-unix. |
send_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método send del objeto de socket TCP/dominio-unix. |
read_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método receive del objeto de socket TCP/dominio-unix. |
keepalive_timeout |
nil |
Controla el tiempo máximo de inactividad de las conexiones en el grupo de conexiones. |
pool |
nil |
Un nombre personalizado para el grupo de conexiones que se está utilizando. |
pool_size |
nil |
El tamaño del grupo de conexiones. |
backlog |
nil |
Un tamaño de cola para usar cuando el grupo de conexiones está lleno (configurado con pool_size). |
ssl |
false |
Habilitar SSL. |
ssl_verify |
nil |
Verificar el certificado del servidor. |
También necesitas crear las siguientes tablas en tu base de datos:
--
-- Tabla de base de datos que almacena datos de sesión.
--
CREATE TABLE IF NOT EXISTS sessions (
sid CHAR(43) PRIMARY KEY,
name VARCHAR(255),
data MEDIUMTEXT,
exp DATETIME,
INDEX (exp)
) CHARACTER SET ascii;
--
-- Tabla de metadatos de sesiones.
--
-- Esto solo es necesario si deseas almacenar metadatos de sesión.
--
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;
Configuración de Postgres
Con el almacenamiento en Postgres puedes usar la siguiente configuración (establece el storage en "postgres"):
| Opción | Predeterminado | Descripción |
|---|---|---|
host |
"127.0.0.1" |
El host al que conectarse. |
port |
5432 |
El puerto al que conectarse. |
application |
5432 |
Establece el nombre de la conexión como se muestra en pg_stat_activity (por defecto es "pgmoon"). |
username |
"postgres" |
El nombre de usuario de la base de datos para autenticarse. |
password |
nil |
Contraseña para la autenticación, puede ser requerida dependiendo de la configuración del servidor. |
database |
nil |
El nombre de la base de datos a la que conectarse. |
table_name |
"sessions" |
Nombre de la tabla de base de datos a la que almacenar los datos de sesión (puede estar precedido por el esquema de base de datos). |
table_name_meta |
"sessions_meta" |
Nombre de la tabla de metadatos de la base de datos a la que almacenar los metadatos de sesión (puede estar precedido por el esquema de base de datos). |
connect_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método connect del objeto de socket TCP/dominio-unix. |
send_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método send del objeto de socket TCP/dominio-unix. |
read_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método receive del objeto de socket TCP/dominio-unix. |
keepalive_timeout |
nil |
Controla el tiempo máximo de inactividad de las conexiones en el grupo de conexiones. |
pool |
nil |
Un nombre personalizado para el grupo de conexiones que se está utilizando. |
pool_size |
nil |
El tamaño del grupo de conexiones. |
backlog |
nil |
Un tamaño de cola para usar cuando el grupo de conexiones está lleno (configurado con pool_size). |
ssl |
false |
Habilitar SSL. |
ssl_verify |
nil |
Verificar el certificado del servidor. |
ssl_required |
nil |
Abortará la conexión si el servidor no admite conexiones SSL. |
También necesitas crear las siguientes tablas en tu base de datos:
--
-- Tabla de base de datos que almacena datos de sesión.
--
CREATE TABLE IF NOT EXISTS sessions (
sid TEXT PRIMARY KEY,
name TEXT,
data TEXT,
exp TIMESTAMP WITH TIME ZONE
);
CREATE INDEX ON sessions (exp);
--
-- Tabla de metadatos de sesiones.
--
-- Esto solo es necesario si deseas almacenar metadatos de sesión.
--
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)
);
La implementación requiere pgmoon que puedes instalar con LuaRocks:
❯ luarocks install pgmoon
Configuración de Redis
La biblioteca de sesiones admite conexiones de Redis único, Redis Sentinel y Redis Cluster. Configuraciones comunes entre todos ellos:
| Opción | Predeterminado | Descripción |
|---|---|---|
prefix |
nil |
Prefijo para las claves almacenadas en Redis. |
suffix |
nil |
Sufijo para las claves almacenadas en Redis. |
username |
nil |
El nombre de usuario de la base de datos para autenticarse. |
password |
nil |
Contraseña para la autenticación. |
connect_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método connect del objeto de socket TCP/dominio-unix. |
send_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método send del objeto de socket TCP/dominio-unix. |
read_timeout |
nil |
Controla el valor de tiempo de espera predeterminado utilizado en el método receive del objeto de socket TCP/dominio-unix. |
keepalive_timeout |
nil |
Controla el tiempo máximo de inactividad de las conexiones en el grupo de conexiones. |
pool |
nil |
Un nombre personalizado para el grupo de conexiones que se está utilizando. |
pool_size |
nil |
El tamaño del grupo de conexiones. |
backlog |
nil |
Un tamaño de cola para usar cuando el grupo de conexiones está lleno (configurado con pool_size). |
ssl |
false |
Habilitar SSL. |
ssl_verify |
nil |
Verificar el certificado del servidor. |
server_name |
nil |
El nombre del servidor para la nueva extensión TLS Server Name Indication (SNI). |
La implementación de redis único se selecciona cuando no pasas ni sentinels ni nodes, lo que llevaría a seleccionar la implementación de sentinel o cluster.
Configuración de Redis Único
Redis único tiene las siguientes opciones de configuración adicionales (establece el storage en "redis"):
| Opción | Predeterminado | Descripción |
|---|---|---|
host |
"127.0.0.1" |
El host al que conectarse. |
port |
6379 |
El puerto al que conectarse. |
socket |
nil |
El archivo de socket al que conectarse. |
Configuración de Redis Sentinels
Redis Sentinel tiene las siguientes opciones de configuración adicionales (establece el storage en "redis" y configura los sentinels):
| Opción | Predeterminado | Descripción |
|---|---|---|
master |
nil |
Nombre del maestro. |
role |
nil |
"master" o "slave". |
socket |
nil |
El archivo de socket al que conectarse. |
sentinels |
nil |
Redis Sentinels. |
sentinel_username |
nil |
Nombre de usuario opcional de sentinel. |
sentinel_password |
nil |
Contraseña opcional de sentinel. |
database |
nil |
La base de datos a la que conectarse. |
Los sentinels son un array de registros de Sentinel:
| Opción | Predeterminado | Descripción |
|---|---|---|
host |
nil |
El host al que conectarse. |
port |
nil |
El puerto al que conectarse. |
La implementación de sentinel se selecciona cuando pasas sentinels como parte de la configuración de redis (y no pasas nodes, lo que seleccionaría la implementación de cluster).
La implementación requiere lua-resty-redis-connector que puedes instalar con LuaRocks:
❯ luarocks install lua-resty-redis-connector
Configuración de Redis Cluster
Redis Cluster tiene las siguientes opciones de configuración adicionales (establece el storage en "redis" y configura los nodes):
| Opción | Predeterminado | Descripción |
|---|---|---|
name |
nil |
Nombre del clúster de Redis. |
nodes |
nil |
Nodos del clúster de Redis. |
lock_zone |
nil |
Nombre del diccionario compartido para bloqueos. |
lock_prefix |
nil |
Prefijo del nombre del diccionario compartido para bloqueos. |
max_redirections |
nil |
Máximo número de intentos de re-dirección. |
max_connection_attempts |
nil |
Máximo número de intentos de conexión. |
max_connection_timeout |
nil |
Tiempo máximo de espera de conexión en total entre los intentos. |
Los nodes son un array de registros de nodos del clúster:
| Opción | Predeterminado | Descripción |
|---|---|---|
ip |
"127.0.0.1" |
La dirección IP a la que conectarse. |
port |
6379 |
El puerto al que conectarse. |
La implementación de cluster se selecciona cuando pasas nodes como parte de la configuración de redis.
Para que el cluster funcione correctamente, necesitas configurar lock_zone, así que también agrega esto a tu configuración de Nginx:
lua_shared_dict redis_cluster_locks 100k;
Y establece el lock_zone en "redis_cluster_locks".
La implementación requiere resty-redis-cluster o kong-redis-cluster que puedes instalar con LuaRocks:
❯ luarocks install resty-redis-cluster
## o
❯ luarocks install kong-redis-cluster
Configuración SHM
Con el almacenamiento SHM puedes usar la siguiente configuración (establece el storage en "shm"):
| Opción | Predeterminado | Descripción |
|---|---|---|
prefix |
nil |
Prefijo para las claves almacenadas en SHM. |
suffix |
nil |
Sufijo para las claves almacenadas en SHM. |
zone |
"sessions" |
Un nombre de zona de memoria compartida. |
También necesitarás crear un diccionario compartido zone en Nginx:
lua_shared_dict sessions 10m;
Nota: es posible que necesites ajustar el tamaño de la zona de memoria compartida según tus necesidades.
API
Los documentos de API generados por LDoc también se pueden ver en bungle.github.io/lua-resty-session.
Inicialización
session.init
sintaxis: session.init(configuration)
Inicializa la biblioteca de sesiones.
Esta función se puede llamar en las fases init o init_worker en OpenResty para establecer la configuración predeterminada global para todas las instancias de sesión creadas por esta biblioteca.
require "resty.session".init({
audience = "my-application",
storage = "redis",
redis = {
username = "session",
password = "storage",
},
})
Consulta configuration para posibles configuraciones.
Constructores
session.new
sintaxis: session = session.new(configuration)
Crea una nueva instancia de sesión.
local session = require "resty.session".new()
-- O
local session = require "resty.session".new({
audience = "my-application",
})
Consulta configuration para posibles configuraciones.
Ayudantes
session.open
sintaxis: session, err, exists = session.open(configuration)
Esto se puede usar para abrir una sesión, y devolverá una sesión existente o una nueva. El parámetro de retorno exists (un booleano) indica si se devolvió una sesión existente o nueva. El err (una cadena) contiene un mensaje de por qué la apertura podría haber fallado (la función aún devolverá session también).
local session = require "resty.session".open()
-- O
local session, err, exists = require "resty.session".open({
audience = "my-application",
})
Consulta configuration para posibles configuraciones.
session.start
sintaxis: session, err, exists, refreshed = session.start(configuration)
Esto se puede usar para iniciar una sesión, y devolverá una sesión existente o una nueva. En caso de que haya una sesión existente, la sesión también se actualizará (según sea necesario). El parámetro de retorno exists (un booleano) indica si se devolvió una sesión existente o nueva. El refreshed (un booleano) indica si la llamada a refresh fue exitosa. El err (una cadena) contiene un mensaje de por qué la apertura o la actualización podrían haber fallado (la función aún devolverá session también).
local session = require "resty.session".start()
-- O
local session, err, exists, refreshed = require "resty.session".start({
audience = "my-application",
})
Consulta configuration para posibles configuraciones.
session.logout
sintaxis: ok, err, exists, logged_out = session.logout(configuration)
Cierra sesión de una audiencia específica.
Una sola cookie de sesión puede compartirse entre múltiples audiencias (o aplicaciones), por lo que existe la necesidad de poder cerrar sesión de solo una audiencia mientras se mantiene la sesión para las otras audiencias. El parámetro de retorno exists (un booleano) indica si la sesión existía. El parámetro de retorno logged_out (un booleano) señala si la sesión existía y también se cerró. El err (una cadena) contiene una razón por la cual la sesión no existía o por qué falló el cierre de sesión. El ok (veraz) será true cuando la sesión existía y se cerró con éxito.
Cuando hay solo una audiencia, esto se puede considerar igual a session.destroy.
Cuando la última audiencia cierra sesión, la cookie también se destruirá e invalidará en el cliente.
require "resty.session".logout()
-- O
local ok, err, exists, logged_out = require "resty.session".logout({
audience = "my-application",
})
Consulta configuration para posibles configuraciones.
session.destroy
sintaxis: ok, err, exists, destroyed = session.destroy(configuration)
Destruye toda la sesión y borra las cookies.
Una sola cookie de sesión puede compartirse entre múltiples audiencias (o aplicaciones), por lo que existe la necesidad de poder cerrar sesión de solo una audiencia mientras se mantiene la sesión para las otras audiencias. El parámetro de retorno exists (un booleano) indica si la sesión existía. El parámetro de retorno destroyed (un booleano) señala si la sesión existía y también fue destruida. El err (una cadena) contiene una razón por la cual la sesión no existía o por qué falló el cierre de sesión. El ok (veraz) será true cuando la sesión existía y fue cerrada con éxito.
require "resty.session".destroy()
-- O
local ok, err, exists, destroyed = require "resty.session".destroy({
cookie_name = "auth",
})
Consulta configuration para posibles configuraciones.
Métodos de Instancia
session:open
sintaxis: ok, err = session:open()
Esto se puede usar para abrir una sesión. Devuelve true cuando la sesión fue abierta y validada. De lo contrario, devuelve nil y un mensaje de error.
local session = require "resty.session".new()
local ok, err = session:open()
if ok then
-- la sesión existe
else
-- la sesión no existía o era inválida
end
session:save
sintaxis: ok, err = session:save()
Guarda los datos de la sesión y emite una nueva cookie de sesión con un nuevo id de sesión. Cuando remember está habilitado, también emitirá una nueva cookie persistente y posiblemente guardará los datos en el almacén de backend. Devuelve true cuando la sesión fue guardada. De lo contrario, devuelve nil y un mensaje de error.
local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
-- error al guardar la sesión
end
session:touch
sintaxis: ok, err = session:touch()
Actualiza el desplazamiento de inactividad de la sesión enviando una cookie de sesión actualizada. Solo envía la cookie del cliente y nunca llama a ninguna API de almacenamiento de sesión de backend. Normalmente, el session:refresh se usa para llamar a esto indirectamente. En caso de error, devuelve nil y un mensaje de error, de lo contrario true.
local session, err, exists = require "resty.session".open()
if exists then
ok, err = session:touch()
end
session:refresh
sintaxis: ok, err = session:refresh()
Ya sea guarda la sesión (creando un nuevo id de sesión) o toca la sesión dependiendo de si el tiempo de espera rodante se está acercando, lo que significa que por defecto cuando se ha gastado 3/4 del tiempo de espera rodante, es decir, 45 minutos con el tiempo de espera rodante predeterminado de una hora. El toque tiene un umbral, por defecto de un minuto, por lo que puede omitirse en algunos casos (puedes llamar a session:touch() para forzarlo). En caso de error, devuelve nil y un mensaje de error, de lo contrario true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:refresh()
end
El código anterior se parece un poco al ayudante session.start().
session:logout
sintaxis: ok, err = session:logout()
Cerrar sesión destruye la sesión o simplemente borra los datos para la audiencia actual, y los guarda (cerrando sesión de la audiencia actual). En caso de error, devuelve nil y un mensaje de error, de lo contrario true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:logout()
end
session:destroy
sintaxis: ok, err = session:destroy()
Destruye la sesión y borra las cookies. En caso de error, devuelve nil y un mensaje de error, de lo contrario true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:destroy()
end
session:close
sintaxis: session:close()
Simplemente cierra la instancia de sesión para que no pueda ser utilizada más.
local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
-- error al guardar la sesión
end
session:close()
session:set_data
sintaxis: session:set_data(data)
Establece los datos de la sesión. Los data deben ser una table.
local session, err, exists = require "resty.session".open()
if not exists then
session:set_data({
cart = {},
})
session:save()
end
session:get_data
sintaxis: data = session:get_data()
Obtiene los datos de la sesión.
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
sintaxis: session:set(key, value)
Establece un valor en la sesión.
local session, err, exists = require "resty.session".open()
if not exists then
session:set("access-token", "eyJ...")
session:save()
end
session:get
sintaxis: value = session:get(key)
Obtiene un valor de la sesión.
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
sintaxis: session:set_audience(audience)
Establece la audiencia de la sesión.
local session = require "resty.session".new()
session.set_audience("my-service")
session:get_audience
sintaxis: audience = session:get_audience()
Establece el sujeto de la sesión.
session:set_subject
sintaxis: session:set_subject(subject)
Establece la audiencia de la sesión.
local session = require "resty.session".new()
session.set_subject("[email protected]")
session:get_subject
sintaxis: subject = session:get_subject()
Obtiene el sujeto de la sesión.
local session, err, exists = require "resty.session".open()
if exists then
local subject = session.get_subject()
end
session:get_property
sintaxis: value = session:get_property(name)
Obtiene la propiedad de la sesión. Posibles nombres de propiedades:
"id": id de sesión de 43 bytes (igual que nonce, pero codificado en base64 url)"nonce": nonce de 32 bytes (igual que el id de sesión pero en bytes crudos)"audience": Audiencia actual de la sesión"subject": Sujeto actual de la sesión"timeout": Tiempo de espera más cercano (en segundos) (lo que queda de él)"idling-timeout": Tiempo de espera de inactividad de la sesión (en segundos) (lo que queda de él)"rolling-timeout": Tiempo de espera rodante de la sesión (en segundos) (lo que queda de él)"absolute-timeout": Tiempo de espera absoluto de la sesión (en segundos) (lo que queda de él)
Nota: el valor devuelto puede ser nil.
local session, err, exists = require "resty.session".open()
if exists then
local timeout = session.get_property("timeout")
end
session:set_remember
sintaxis: session:set_remember(value)
Establece sesiones persistentes activadas/desactivadas.
En muchos formularios de inicio de sesión, se le da al usuario una opción de "recuérdame". Puedes llamar a esta función según lo que seleccionó el usuario.
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
sintaxis: remember = session:get_remember()
Obtiene el estado de las sesiones persistentes.
local session, err, exists = require "resty.session".open()
if exists then
local remember = session.get_remember()
end
session:clear_request_cookie
sintaxis: ok, err = session:clear_request_cookie()
Modifica los encabezados de la solicitud eliminando las cookies relacionadas con la sesión. Esto es útil cuando usas la biblioteca de sesiones en un servidor proxy y no deseas que las cookies de sesión se reenvíen al servicio upstream. En caso de error, devuelve nil y un mensaje de error, de lo contrario true (que se puede ignorar).
local session, err, exists = require "resty.session".open()
if exists then
session:clear_request_cookie()
end
session:set_headers
sintaxis: ok, err = session:set_headers(arg1, arg2, ...)
Establece encabezados de solicitud y respuesta según la configuración. En caso de error, devuelve nil y un mensaje de error, de lo contrario true (que se puede ignorar).
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
Cuando se llama sin argumentos, establecerá los encabezados de solicitud configurados con request_headers y los encabezados de respuesta configurados con response_headers.
Consulta configuration para posibles nombres de encabezados.
session:set_request_headers
sintaxis: ok, err = session:set_request_headers(arg1, arg2, ...)
Establece encabezados de solicitud. En caso de error, devuelve nil y un mensaje de error, de lo contrario true (que se puede ignorar).
local session, err, exists = require "resty.session".open()
if exists then
session:set_request_headers("audience", "subject", "id")
end
Cuando se llama sin argumentos, establecerá los encabezados de solicitud configurados con request_headers.
Consulta configuration para posibles nombres de encabezados.
session:set_response_headers
sintaxis: ok, err = session:set_response_headers(arg1, arg2, ...)
Establece encabezados de respuesta. En caso de error, devuelve nil y un mensaje de error, de lo contrario true (que se puede ignorar).
local session, err, exists = require "resty.session".open()
if exists then
session:set_response_headers("timeout", "idling-timeout", "rolling-timeout", "absolute-timeout")
end
Cuando se llama sin argumentos, establecerá los encabezados de respuesta configurados con response_headers.
Consulta configuration para posibles nombres de encabezados.
session.info:set
sintaxis: session.info:set(key, value)
Establece un valor en el almacén de información de la sesión. El almacén de información de la sesión puede usarse en escenarios en los que deseas almacenar datos en el almacenamiento del lado del servidor, pero no deseas crear una nueva sesión y enviar una nueva cookie de sesión. Los datos del almacén de información no se consideran al verificar la etiqueta de autenticación o el código de autenticación de mensajes. Por lo tanto, si deseas usar esto para datos que necesitan ser cifrados, necesitas cifrar el valor antes de pasarlo a esta función.
local session, err, exists = require "resty.session".open()
if exists then
session.info:set("last-access", ngx.now())
session.info:save()
end
Con el almacenamiento en cookie, esto aún funciona, pero es casi lo mismo que session:set.
session.info:get
sintaxis: value = session.info:get(key)
Obtiene un valor del almacén de información de la sesión.
local session, err, exists = require "resty.session".open()
if exists then
local last_access = session.info:get("last-access")
end
session.info:save
sintaxis: ok, err = session.info:save()
Guarda la información. Solo actualiza el almacenamiento de backend. No envía una nueva cookie (excepto con el almacenamiento en cookie).
local session = require "resty.session".new()
session.info:set("last-access", ngx.now())
local ok, err = session.info:save()
Formato de Cookie
[ HEADER -------------------------------------------------------------------------------------]
[ Tipo || Flags || SID || Creado en || Desplazamiento Rodante || Tamaño || Etiqueta || Desplazamiento de Inactividad || Mac ]
[ 1B || 2B || 32B || 5B || 4B || 3B || 16B || 3B || 16B ]
y
[ PAYLOAD --]
[ Datos *B ]
Tanto el HEADER como el PAYLOAD están codificados en base64 url antes de colocarse en un encabezado Set-Cookie. Al usar un almacenamiento del lado del servidor, el PAYLOAD no se coloca en la cookie. Con el almacenamiento en cookie, el encabezado codificado en base64 url se concatena con el PAYLOAD codificado en base64 url.
El HEADER tiene un tamaño fijo de 82 bytes en binario o 110 bytes en forma codificada en base64 url.
Campos del encabezado explicados:
- Tipo: número
1empaquetado en un solo byte little endian (actualmente el únicotiposoportado). - Flags: flags empaquetados en binario (corto) en una forma de dos bytes little endian.
- SID:
32bytes de datos aleatorios criptográficos (ID de sesión). - Creado en: segundos empaquetados en binario desde la época en una forma little endian, truncados a 5 bytes.
- Desplazamiento Rodante: segundos empaquetados en binario desde el momento de creación en una forma little endian (entero).
- Tamaño: tamaño de datos empaquetado en binario en una forma de tres bytes little endian.
- Etiqueta:
16bytes de etiqueta de autenticación del cifrado AES-256-GCM de los datos. - Desplazamiento de Inactividad: segundos empaquetados en binario desde el momento de creación + desplazamiento rodante en una forma little endian, truncados a 3 bytes.
- Mac:
16bytes de código de autenticación de mensajes del encabezado.
Cifrado de Datos
- Material de clave inicial (IKM):
- derivar IKM del
secrethashandosecretcon SHA-256, o - usar IKM de 32 bytes cuando se pase a la biblioteca con
ikm - Generar 32 bytes de ID de sesión aleatorio criptográfico (
sid) - Derivar una clave de cifrado de 32 bytes y un vector de inicialización de 12 bytes con HKDF usando SHA-256 (en modo FIPS utiliza PBKDF2 con SHA-256 en su lugar)
- Usar HKDF extract para derivar una nueva clave de
ikmpara obtenerkey(este paso se puede hacer una vez por cadaikm):- longitud de salida:
32 - digest:
"sha256" - clave:
<ikm> - modo:
extract only - info:
"" - sal:
""
- longitud de salida:
- Usar HKDF expand para derivar
44bytes deoutput:- longitud de salida:
44 - digest:
"sha256" - clave:
<key> - modo:
expand only - info:
"encryption:<sid>" - sal:
""
- longitud de salida:
- Los primeros 32 bytes de
outputson la clave de cifrado (aes-key), y los últimos 12 bytes son el vector de inicialización (iv) - Cifrar
plaintext(codificado en JSON y opcionalmente desinflado) usando AES-256-GCM para obtenerciphertextytag - cifrado:
"aes-256-gcm" - clave:
<aes-key> - iv:
<iv> - texto plano:
<plaintext> - aad: usar los primeros 47 bytes de
headercomoaad, que incluye:- Tipo
- Flags
- ID de sesión
- Tiempo de creación
- Desplazamiento rodante
- Tamaño de los datos
Hay una variación para las cookies de remember en el paso 3, donde podemos usar PBKDF2 en lugar de HKDF, dependiendo de la configuración de remember_safety (también lo usamos en modo FIPS). La configuración de PBKDF2:
- longitud de salida:
44 - digest:
"sha256" - contraseña:
<key> - sal:
"encryption:<sid>" - iteraciones:
<1000|10000|100000|1000000> - pkcs5:
1(cumple con FIPS en nuestro caso de uso, pero se necesita desactivar las verificaciones basadas enSP800-132, como el recuento de iteraciones, ver: https://docs.openssl.org/master/man7/provider-kdf/#kdf-parameters)
Los recuentos de iteración se basan en la configuración de remember_safety ("Low", "Medium", "High", "Very High"), si remember_safety se establece en "None", utilizaremos el HDKF como se mencionó anteriormente.
Nota: Para compatibilidad hacia atrás, desactivamos las verificaciones de cumplimiento de SP800-132 en modo FIPS. Estas verificaciones comprueban que la longitud de la sal sea de al menos 128 bits, la longitud de la clave derivada sea de al menos 112 bits y que el recuento de iteraciones sea de al menos 1000. Estas verificaciones están desactivadas por defecto en el proveedor predeterminado de OpenSSL, pero están habilitadas por defecto en el proveedor FIPS.
Autenticación de Encabezado de Cookie
- Deriva una clave de autenticación de 32 bytes (
mac_key) con HKDF usando SHA-256 (en modo FIPS utiliza PBKDF2 con SHA-256 en su lugar):- Usar HKDF extract para derivar una nueva clave de
ikmpara obtenerkey(este paso se puede hacer una vez por cadaikmy reutilizarse con la generación de clave de cifrado):- longitud de salida:
32 - digest:
"sha256" - clave:
<ikm> - modo:
extract only - info:
"" - sal:
""
- longitud de salida:
- Usar HKDF expand para derivar
32bytes demac-key:- longitud de salida:
32 - digest:
"sha256" - clave:
<key> - modo:
expand only - info:
"authentication:<sid>" - sal:
""
- longitud de salida:
- Usar HKDF extract para derivar una nueva clave de
- Calcular el código de autenticación de mensajes usando HMAC-SHA256:
- digest:
"sha256" - clave:
<mac-key> - mensaje: usar los primeros 66 bytes de
header, que incluye:- Tipo
- Flags
- ID de sesión
- Tiempo de creación
- Desplazamiento rodante
- Tamaño de datos
- Etiqueta
- Desplazamiento de inactividad
Interfaz de Almacenamiento Personalizado
Si deseas implementar un almacenamiento personalizado, necesitas implementar la siguiente interfaz:
---
-- <custom> backend para la biblioteca de sesiones
--
-- @module <custom>
---
-- Almacenamiento
-- @section instance
local metatable = {}
metatable.__index = metatable
function metatable.__newindex()
error("intento de actualizar una tabla de solo lectura", 2)
end
---
-- Almacena datos de sesión.
--
-- @function instance:set
-- @tparam string name nombre de la cookie
-- @tparam string key clave de sesión
-- @tparam string value valor de sesión
-- @tparam number ttl ttl de sesión
-- @tparam number current_time tiempo actual
-- @tparam[opt] string old_key id de sesión antiguo
-- @tparam string stale_ttl ttl obsoleto
-- @tparam[opt] table metadata tabla de metadatos
-- @treturn true|nil ok
-- @treturn string mensaje de error
function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata)
-- NYI
end
---
-- Recupera datos de sesión.
--
-- @function instance:get
-- @tparam string name nombre de la cookie
-- @tparam string key clave de sesión
-- @treturn string|nil datos de sesión
-- @treturn string mensaje de error
function metatable:get(name, key)
-- NYI
end
---
-- Elimina datos de sesión.
--
-- @function instance:delete
-- @tparam string name nombre de la cookie
-- @tparam string key clave de sesión
-- @tparam[opt] table metadata metadatos de sesión
-- @treturn boolean|nil datos de sesión
-- @treturn string mensaje de error
function metatable:delete(name, key, current_time, metadata)
-- NYI
end
local storage = {}
---
-- Constructores
-- @section constructors
---
-- Configuración
-- @section configuration
---
-- Configuración de almacenamiento <custom>
-- @field <field-name> TBD
-- @table configuration
---
-- Crea un almacenamiento <custom>.
--
-- Esto crea una nueva instancia de almacenamiento en memoria compartida.
--
-- @function module.new
-- @tparam[opt] table configuration almacenamiento <custom> @{configuration}
-- @treturn table instancia de almacenamiento <custom>
function storage.new(configuration)
-- NYI
-- return setmetatable({}, metatable)
end
return storage
Por favor, consulta las implementaciones existentes para los detalles. Y por favor, haz una solicitud de extracción para que podamos integrarlo directamente a la biblioteca para otros usuarios también.
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-session.