Pular para conteúdo

session: Biblioteca de sessão para nginx-module-lua – flexível e segura

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Então você pode prosseguir com os seguintes passos.

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

Para usar esta biblioteca Lua com NGINX, certifique-se de que nginx-module-lua está instalado.

Este documento descreve lua-resty-session v4.1.5 lançado em 24 de novembro de 2025.


lua-resty-session é uma biblioteca de sessão segura e flexível para OpenResty.

TL;DR;

  • As sessões são imutáveis (cada salvamento gera uma nova sessão) e sem bloqueio.
  • Os dados da sessão são criptografados com AES-256-GCM usando uma chave derivada com HKDF-SHA256 (no modo FIPS, usa PBKDF2 com SHA-256).
  • A sessão tem um cabeçalho de tamanho fixo que é protegido com HMAC-SHA256 MAC com uma chave derivada usando HKDF-SHA256 (no modo FIPS, usa PBKDF2 com SHA-256).
  • Os dados da sessão podem ser armazenados em um cookie sem estado ou em vários armazenamentos de backend.
  • Um único cookie de sessão pode manter várias sessões entre diferentes públicos.

Nota: A versão 4.0.0 foi uma reescrita desta biblioteca com muitas lições aprendidas ao longo dos anos. Se você ainda usa uma versão mais antiga, consulte a documentação antiga.

Sinopse

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 o teste</a>
          </body>
          </html>
        ]])
      }
    }

    location /start {
      content_by_lua_block {
        local session = require "resty.session".new()
        session:set_subject("Fã do OpenResty")
        session:set("quote", "A rápida raposa marrom salta sobre o cachorro preguiçoso")
        local ok, err = session:save()

        ngx.say(string.format([[
          <html>
          <body>
            <p>Sessão iniciada (%s)</p>
            <p><a href=/started>Verifique se realmente foi</a></p>
          </body>
          </html>
        ]], err or "sem erro"))
      }
    }

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

        ngx.say(string.format([[
          <html>
          <body>
            <p>A sessão foi iniciada por %s (%s)</p>
            <p><blockquote>%s</blockquote></p>
            <p><a href=/modify>Modificar a sessão</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anônimo",
          err or "sem erro",
          session:get("quote") or "sem citação"
        ))
      }
    }

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

        ngx.say(string.format([[
          <html>
          <body>
            <p>A sessão foi modificada (%s)</p>
            <p><a href=/modified>Verifique se foi modificada</a></p>
          </body>
          </html>
        ]], err or err_save or "sem erro"))
      }
    }

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

        ngx.say(string.format([[
          <html>
          <body>
            <p>A sessão foi iniciada por %s (%s)</p>
            <p><blockquote>%s</blockquote></p>
            <p><a href=/destroy>Destruir a sessão</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anônimo",
          err or "sem erro",
          session:get("quote")  or "sem citação"
        ))
      }
    }

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

        ngx.say(string.format([[
          <html>
          <body>
            <p>A sessão foi destruída (%s)</p>
            <p><a href=/destroyed>Verifique se realmente foi?</a></p>
          </body>
          </html>
        ]], err or "sem erro"))
      }
    }

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

        ngx.say(string.format([[
          <html>
          <body>
            <p>A sessão foi realmente destruída, você é conhecido como %s (%s)</p>
            <p><a href=/>Começar novamente</a></p>
          </body>
          </html>
        ]],
          session:get_subject() or "Anônimo",
          err or "sem erro"
        ))
      }
    }
  }
}

Configuração

A configuração pode ser dividida em configuração de sessão genérica e configuração de armazenamento do lado do servidor.

Aqui está um exemplo:

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",
    },
  })
}

Configuração da Sessão

A configuração da sessão pode ser passada para as funções de inicialização, construtor e ajudante.

Aqui estão as possíveis opções de configuração da sessão:

Opção Padrão Descrição
secret nil Segredo usado para a derivação da chave. O segredo é hashado com SHA-256 antes de ser usado. Ex: "RaJKp8UQW1".
secret_fallbacks nil Array de segredos que podem ser usados como segredos alternativos (ao fazer rotação de chave), Ex: { "6RfrAYYzYq", "MkbTkkyF9C" }.
ikm (aleatório) Material de chave inicial (ou ikm) pode ser especificado diretamente (sem usar um segredo) com exatamente 32 bytes de dados. Ex: "5ixIW4QVMk0dPtoIhn41Eh1I9enP2060"
ikm_fallbacks nil Array de materiais de chave inicial que podem ser usados como chaves alternativas (ao fazer rotação de chave), Ex: { "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }.
cookie_prefix nil Prefixo do cookie, use nil, "__Host-" ou "__Secure-".
cookie_name "session" Nome do cookie de sessão, ex: "session".
cookie_path "/" Caminho do cookie, ex: "/".
cookie_domain nil Domínio do cookie, ex: "example.com"
cookie_http_only true Marcar cookie como apenas HTTP, use true ou false.
cookie_secure nil Marcar cookie como seguro, use nil, true ou false.
cookie_priority nil Prioridade do cookie, use nil, "Low", "Medium" ou "High".
cookie_same_site "Lax" Política de mesmo site do cookie, use nil, "Lax", "Strict", "None" ou "Default"
cookie_same_party nil Marcar cookie com a flag de mesmo partido, use nil, true ou false.
cookie_partitioned nil Marcar cookie com a flag de particionado, use nil, true ou false.
remember false Habilitar ou desabilitar sessões persistentes, use nil, true ou false.
remember_safety "Medium" Complexidade da derivação da chave do cookie de lembrança, use nil, "None" (rápido), "Low", "Medium", "High" ou "Very High" (lento).
remember_cookie_name "remember" Nome do cookie de sessão persistente, ex: "remember".
audience "default" Público da sessão, ex: "my-application".
subject nil Sujeito da sessão, ex: "[email protected]".
enforce_same_subject false Quando definido como true, os públicos precisam compartilhar o mesmo sujeito. A biblioteca remove dados de público que não correspondem ao sujeito ao salvar.
stale_ttl 10 Quando a sessão é salva, uma nova sessão é criada, stale ttl especifica quanto tempo a antiga ainda pode ser usada, ex: 10 (em segundos).
idling_timeout 900 O tempo limite de inatividade especifica quanto tempo a sessão pode estar inativa até ser considerada inválida, ex: 900 (15 minutos) (em segundos), 0 desabilita as verificações e toques.
rolling_timeout 3600 O tempo limite rolante especifica quanto tempo a sessão pode ser usada até precisar ser renovada, ex: 3600 (uma hora) (em segundos), 0 desabilita as verificações e rolamentos.
absolute_timeout 86400 O tempo limite absoluto limita quanto tempo a sessão pode ser renovada, até que a re-autenticação seja necessária, ex: 86400 (um dia) (em segundos), 0 desabilita as verificações.
remember_rolling_timeout 604800 O tempo limite de lembrança especifica quanto tempo a sessão persistente é considerada válida, ex: 604800 (uma semana) (em segundos), 0 desabilita as verificações e rolamentos.
remember_absolute_timeout 2592000 O tempo limite absoluto de lembrança limita quanto tempo a sessão persistente pode ser renovada, até que a re-autenticação seja necessária, ex: 2592000 (30 dias) (em segundos), 0 desabilita as verificações.
hash_storage_key false Se deve ou não hash a chave de armazenamento. Com a chave de armazenamento hashada, é impossível descriptografar dados no lado do servidor sem ter um cookie também, use nil, true ou false.
hash_subject false Se deve ou não hash o sujeito quando store_metadata está habilitado, ex: por razões de PII.
store_metadata false Se deve ou não armazenar também os metadados das sessões, como coletar dados de sessões para um público específico pertencente a um sujeito específico.
touch_threshold 60 O limiar de toque controla com que frequência ou infrequentemente o session:refresh toca o cookie, ex: 60 (um minuto) (em segundos)
compression_threshold 1024 O limiar de compressão controla quando os dados são desinflados, ex: 1024 (um kilobyte) (em bytes), 0 desabilita a compressão.
bind nil Vincular a sessão a dados adquiridos da solicitação HTTP ou conexão, use ip, scheme, user-agent. Ex: { "scheme", "user-agent" } calculará MAC utilizando também a solicitação HTTP Scheme e o cabeçalho User-Agent.
request_headers nil Conjunto de cabeçalhos a serem enviados para o upstream, use id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Ex: { "id", "timeout" } definirá os cabeçalhos de solicitação Session-Id e Session-Timeout quando set_headers for chamado.
response_headers nil Conjunto de cabeçalhos a serem enviados para o downstream, use id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Ex: { "id", "timeout" } definirá os cabeçalhos de resposta Session-Id e Session-Timeout quando set_headers for chamado.
storage nil O armazenamento é responsável por armazenar dados da sessão, use nil ou "cookie" (os dados são armazenados em cookie), "dshm", "file", "memcached", "mysql", "postgres", "redis" ou "shm", ou dê um nome de módulo personalizado ("custom-storage"), ou uma tabela que implementa a interface de armazenamento de sessão.
dshm nil Configuração para armazenamento dshm, ex: { prefix = "sessions" } (veja abaixo)
file nil Configuração para armazenamento de arquivo, ex: { path = "/tmp", suffix = "session" } (veja abaixo)
memcached nil Configuração para armazenamento memcached, ex: { prefix = "sessions" } (veja abaixo)
mysql nil Configuração para armazenamento MySQL / MariaDB, ex: { database = "sessions" } (veja abaixo)
postgres nil Configuração para armazenamento Postgres, ex: { database = "sessions" } (veja abaixo)
redis nil Configuração para armazenamento Redis / Redis Sentinel / Redis Cluster, ex: { prefix = "sessions" } (veja abaixo)
shm nil Configuração para armazenamento em memória compartilhada, ex: { zone = "sessions" }
["custom-storage"] nil configuração de armazenamento personalizado (carregado com require "custom-storage").

Ao armazenar dados em cookie, não há configuração adicional necessária, apenas defina o storage como nil ou "cookie".

Configuração de Armazenamento DSHM

Com o armazenamento DHSM você pode usar as seguintes configurações (defina o storage como "dshm"):

Opção Padrão Descrição
prefix nil O prefixo para as chaves armazenadas no DSHM.
suffix nil O sufixo para as chaves armazenadas no DSHM.
host "127.0.0.1" O host para conectar.
port 4321 A porta para conectar.
connect_timeout nil Controla o valor padrão de tempo limite usado no método connect do objeto de socket TCP/unix-domain.
send_timeout nil Controla o valor padrão de tempo limite usado no método send do objeto de socket TCP/unix-domain.
read_timeout nil Controla o valor padrão de tempo limite usado no método receive do objeto de socket TCP/unix-domain.
keepalive_timeout nil Controla o tempo máximo de inatividade das conexões no pool de conexões.
pool nil Um nome personalizado para o pool de conexões em uso.
pool_size nil O tamanho do pool de conexões.
backlog nil Um tamanho de fila a ser usado quando o pool de conexões estiver cheio (configurado com pool_size).
ssl nil Habilitar SSL.
ssl_verify nil Verificar o certificado do servidor.
server_name nil O nome do servidor para a nova extensão TLS Server Name Indication (SNI).

Consulte ngx-distributed-shm para obter as dependências necessárias instaladas.

Configuração de Armazenamento em Arquivo

Com o armazenamento em arquivo você pode usar as seguintes configurações (defina o storage como "file"):

Opção Padrão Descrição
prefix nil Prefixo do arquivo para o arquivo de sessão.
suffix nil Sufixo do arquivo (ou extensão sem .) para o arquivo de sessão.
pool nil Nome do pool de threads sob o qual a gravação do arquivo acontece (disponível apenas no Linux).
path (diretório tmp) Caminho (ou diretório) sob o qual os arquivos de sessão são criados.

A implementação requer LuaFileSystem, que você pode instalar com LuaRocks:

 luarocks install LuaFileSystem

Configuração de Armazenamento Memcached

Com o armazenamento Memcached você pode usar as seguintes configurações (defina o storage como "memcached"):

Opção Padrão Descrição
prefix nil Prefixo para as chaves armazenadas no memcached.
suffix nil Sufixo para as chaves armazenadas no memcached.
host 127.0.0.1 O host para conectar.
port 11211 A porta para conectar.
socket nil O arquivo de socket para conectar.
connect_timeout nil Controla o valor padrão de tempo limite usado no método connect do objeto de socket TCP/unix-domain.
send_timeout nil Controla o valor padrão de tempo limite usado no método send do objeto de socket TCP/unix-domain.
read_timeout nil Controla o valor padrão de tempo limite usado no método receive do objeto de socket TCP/unix-domain.
keepalive_timeout nil Controla o tempo máximo de inatividade das conexões no pool de conexões.
pool nil Um nome personalizado para o pool de conexões em uso.
pool_size nil O tamanho do pool de conexões.
backlog nil Um tamanho de fila a ser usado quando o pool de conexões estiver cheio (configurado com pool_size).
ssl false Habilitar SSL
ssl_verify nil Verificar o certificado do servidor
server_name nil O nome do servidor para a nova extensão TLS Server Name Indication (SNI).

Configuração de Armazenamento MySQL / MariaDB

Com o armazenamento MySQL / MariaDB você pode usar as seguintes configurações (defina o storage como "mysql"):

Opção Padrão Descrição
host "127.0.0.1" O host para conectar.
port 3306 A porta para conectar.
socket nil O arquivo de socket para conectar.
username nil O nome de usuário do banco de dados para autenticação.
password nil Senha para autenticação, pode ser necessária dependendo da configuração do servidor.
charset "ascii" O conjunto de caracteres usado na conexão MySQL.
database nil O nome do banco de dados para conectar.
table_name "sessions" Nome da tabela do banco de dados para armazenar dados da sessão.
table_name_meta "sessions_meta" Nome da tabela de metadados do banco de dados para armazenar metadados da sessão.
max_packet_size 1048576 O limite superior para os pacotes de resposta enviados do servidor MySQL (em bytes).
connect_timeout nil Controla o valor padrão de tempo limite usado no método connect do objeto de socket TCP/unix-domain.
send_timeout nil Controla o valor padrão de tempo limite usado no método send do objeto de socket TCP/unix-domain.
read_timeout nil Controla o valor padrão de tempo limite usado no método receive do objeto de socket TCP/unix-domain.
keepalive_timeout nil Controla o tempo máximo de inatividade das conexões no pool de conexões.
pool nil Um nome personalizado para o pool de conexões em uso.
pool_size nil O tamanho do pool de conexões.
backlog nil Um tamanho de fila a ser usado quando o pool de conexões estiver cheio (configurado com pool_size).
ssl false Habilitar SSL.
ssl_verify nil Verificar o certificado do servidor.

Você também precisa criar as seguintes tabelas no seu banco de dados:

--
-- Tabela do banco de dados que armazena dados da sessão.
--
CREATE TABLE IF NOT EXISTS sessions (
  sid  CHAR(43) PRIMARY KEY,
  name VARCHAR(255),
  data MEDIUMTEXT,
  exp  DATETIME,
  INDEX (exp)
) CHARACTER SET ascii;

--
-- Tabela de metadados das sessões.
--
-- Isso é necessário apenas se você quiser armazenar metadados da sessão.
--
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;

Configuração Postgres

Com o armazenamento Postgres você pode usar as seguintes configurações (defina o storage como "postgres"):

Opção Padrão Descrição
host "127.0.0.1" O host para conectar.
port 5432 A porta para conectar.
application 5432 Defina o nome da conexão conforme exibido em pg_stat_activity (padrão é "pgmoon").
username "postgres" O nome de usuário do banco de dados para autenticação.
password nil Senha para autenticação, pode ser necessária dependendo da configuração do servidor.
database nil O nome do banco de dados para conectar.
table_name "sessions" Nome da tabela do banco de dados para armazenar dados da sessão (pode ser prefixado com schema do banco).
table_name_meta "sessions_meta" Nome da tabela de metadados do banco de dados para armazenar metadados da sessão (pode ser prefixado com schema do banco).
connect_timeout nil Controla o valor padrão de tempo limite usado no método connect do objeto de socket TCP/unix-domain.
send_timeout nil Controla o valor padrão de tempo limite usado no método send do objeto de socket TCP/unix-domain.
read_timeout nil Controla o valor padrão de tempo limite usado no método receive do objeto de socket TCP/unix-domain.
keepalive_timeout nil Controla o tempo máximo de inatividade das conexões no pool de conexões.
pool nil Um nome personalizado para o pool de conexões em uso.
pool_size nil O tamanho do pool de conexões.
backlog nil Um tamanho de fila a ser usado quando o pool de conexões estiver cheio (configurado com pool_size).
ssl false Habilitar SSL.
ssl_verify nil Verificar o certificado do servidor.

Você também precisa criar as seguintes tabelas no seu banco de dados:

--
-- Tabela do banco de dados que armazena dados da sessão.
--
CREATE TABLE IF NOT EXISTS sessions (
  sid  TEXT PRIMARY KEY,
  name TEXT,
  data TEXT,
  exp  TIMESTAMP WITH TIME ZONE
);
CREATE INDEX ON sessions (exp);

--
-- Tabela de metadados das sessões.
--
-- Isso é necessário apenas se você quiser armazenar metadados da sessão.
--
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)
);

A implementação requer pgmoon, que você pode instalar com LuaRocks:

 luarocks install pgmoon

Configuração Redis

A biblioteca de sessão suporta conexões Redis simples, Redis Sentinel e Redis Cluster. Configurações comuns entre todas elas:

Opção Padrão Descrição
prefix nil Prefixo para as chaves armazenadas no Redis.
suffix nil Sufixo para as chaves armazenadas no Redis.
username nil O nome de usuário do banco de dados para autenticação.
password nil Senha para autenticação.
connect_timeout nil Controla o valor padrão de tempo limite usado no método connect do objeto de socket TCP/unix-domain.
send_timeout nil Controla o valor padrão de tempo limite usado no método send do objeto de socket TCP/unix-domain.
read_timeout nil Controla o valor padrão de tempo limite usado no método receive do objeto de socket TCP/unix-domain.
keepalive_timeout nil Controla o tempo máximo de inatividade das conexões no pool de conexões.
pool nil Um nome personalizado para o pool de conexões em uso.
pool_size nil O tamanho do pool de conexões.
backlog nil Um tamanho de fila a ser usado quando o pool de conexões estiver cheio (configurado com pool_size).
ssl false Habilitar SSL
ssl_verify nil Verificar o certificado do servidor
server_name nil O nome do servidor para a nova extensão TLS Server Name Indication (SNI).

A implementação redis simples é selecionada quando você não passa nem sentinels nem nodes, o que levaria a selecionar a implementação sentinel ou cluster.

Configuração Redis Simples

Redis simples tem as seguintes opções de configuração adicionais (defina o storage como "redis"):

Opção Padrão Descrição
host "127.0.0.1" O host para conectar.
port 6379 A porta para conectar.
socket nil O arquivo de socket para conectar.

Configuração Redis Sentinel

Redis Sentinel tem as seguintes opções de configuração adicionais (defina o storage como "redis" e configure os sentinels):

Opção Padrão Descrição
master nil Nome do mestre.
role nil "master" ou "slave".
socket nil O arquivo de socket para conectar.
sentinels nil Redis Sentinels.
sentinel_username nil Nome de usuário opcional do sentinel.
sentinel_password nil Senha opcional do sentinel.
database nil O banco de dados para conectar.

Os sentinels são um array de registros Sentinel:

Opção Padrão Descrição
host nil O host para conectar.
port nil A porta para conectar.

A implementação sentinel é selecionada quando você passa sentinels como parte da configuração redis (e não passa nodes, o que selecionaria a implementação cluster).

A implementação requer lua-resty-redis-connector, que você pode instalar com LuaRocks:

 luarocks install lua-resty-redis-connector

Configuração Redis Cluster

Redis Cluster tem as seguintes opções de configuração adicionais (defina o storage como "redis" e configure os nodes):

Opção Padrão Descrição
name nil Nome do cluster Redis.
nodes nil Nós do cluster Redis.
lock_zone nil Nome do dicionário compartilhado para bloqueios.
lock_prefix nil Prefixo do nome do dicionário compartilhado para bloqueio.
max_redirections nil Máximo de tentativas de redirecionamento.
max_connection_attempts nil Máximo de tentativas de conexão.
max_connection_timeout nil Tempo máximo de conexão total entre as tentativas.

Os nodes são um array de registros de nós do Cluster:

Opção Padrão Descrição
ip "127.0.0.1" O endereço IP para conectar.
port 6379 A porta para conectar.

A implementação cluster é selecionada quando você passa nodes como parte da configuração redis.

Para que o cluster funcione corretamente, você precisa configurar lock_zone, então adicione isso à sua configuração do Nginx:

lua_shared_dict redis_cluster_locks 100k;

E defina o lock_zone como "redis_cluster_locks".

A implementação requer resty-redis-cluster ou kong-redis-cluster, que você pode instalar com LuaRocks:

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

Configuração SHM

Com o armazenamento SHM você pode usar as seguintes configurações (defina o storage como "shm"):

Opção Padrão Descrição
prefix nil Prefixo para as chaves armazenadas no SHM.
suffix nil Sufixo para as chaves armazenadas no SHM.
zone "sessions" Um nome da zona de memória compartilhada.

Você também precisará criar um dicionário compartilhado zone no Nginx:

lua_shared_dict sessions 10m;

Nota: você pode precisar ajustar o tamanho da zona de memória compartilhada de acordo com suas necessidades.

API

Os docs da API gerados pelo LDoc também podem ser visualizados em bungle.github.io/lua-resty-session.

Inicialização

session.init

sintaxe: session.init(configuração)

Inicializa a biblioteca de sessão.

Esta função pode ser chamada nas fases init ou init_worker no OpenResty para definir a configuração padrão global para todas as instâncias de sessão criadas por esta biblioteca.

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

Consulte configuração para possíveis configurações.

Construtores

session.new

sintaxe: session = session.new(configuração)

Cria uma nova instância de sessão.

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

Consulte configuração para possíveis configurações.

Ajudantes

session.open

sintaxe: session, err, exists = session.open(configuração)

Isso pode ser usado para abrir uma sessão, e retornará uma sessão existente ou uma nova sessão. O parâmetro de retorno exists (um booleano) informa se era uma sessão existente ou nova que foi retornada. O err (uma string) contém uma mensagem do porquê a abertura pode ter falhado (a função ainda retornará session também).

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

Consulte configuração para possíveis configurações.

session.start

sintaxe: session, err, exists, refreshed = session.start(configuração)

Isso pode ser usado para iniciar uma sessão, e retornará uma sessão existente ou uma nova sessão. No caso de haver uma sessão existente, a sessão será atualizada também (conforme necessário). O parâmetro de retorno exists (um booleano) informa se era uma sessão existente ou nova que foi retornada. O refreshed (um booleano) informa se a chamada para refresh foi bem-sucedida. O err (uma string) contém uma mensagem do porquê a abertura ou atualização pode ter falhado (a função ainda retornará session também).

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

Consulte configuração para possíveis configurações.

session.logout

sintaxe: ok, err, exists, logged_out = session.logout(configuração)

Desconecta de um público específico.

Um único cookie de sessão pode ser compartilhado entre vários públicos (ou aplicações), portanto, há a necessidade de poder desconectar de apenas um único público enquanto mantém a sessão para os outros públicos. O parâmetro de retorno exists (um booleano) informa se a sessão existia. O parâmetro de retorno logged_out (um booleano) sinaliza se a sessão existia e também foi desconectada. O err (uma string) contém uma razão pela qual a sessão não existia ou por que a desconexão falhou. O ok (verdadeiro) será true quando a sessão existia e foi desconectada com sucesso.

Quando há apenas um único público, isso pode ser considerado igual a session.destroy.

Quando o último público é desconectado, o cookie também será destruído e invalidado no cliente.

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

Consulte configuração para possíveis configurações.

session.destroy

sintaxe: ok, err, exists, destroyed = session.destroy(configuração)

Destrói toda a sessão e limpa os cookies.

Um único cookie de sessão pode ser compartilhado entre vários públicos (ou aplicações), portanto, há a necessidade de poder desconectar de apenas um único público enquanto mantém a sessão para os outros públicos. O parâmetro de retorno exists (um booleano) informa se a sessão existia. O parâmetro de retorno destroyed (um booleano) sinaliza se a sessão existia e também foi destruída. O err (uma string) contém uma razão pela qual a sessão não existia ou por que a desconexão falhou. O ok (verdadeiro) será true quando a sessão existia e foi desconectada com sucesso.

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

Consulte configuração para possíveis configurações.

Métodos de Instância

session:open

sintaxe: ok, err = session:open()

Isso pode ser usado para abrir uma sessão. Retorna true quando a sessão foi aberta e validada. Caso contrário, retorna nil e uma mensagem de erro.

local session = require "resty.session".new()
local ok, err = session:open()
if ok then
  -- a sessão existe

else
  -- a sessão não existia ou era inválida
end

session:save

sintaxe: ok, err = session:save()

Salva os dados da sessão e emite um novo cookie de sessão com um novo id de sessão. Quando remember está habilitado, também emitirá um novo cookie persistente e possivelmente salvará os dados no armazenamento de backend. Retorna true quando a sessão foi salva. Caso contrário, retorna nil e uma mensagem de erro.

local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
  -- erro ao salvar a sessão
end

session:touch

sintaxe: ok, err = session:touch()

Atualiza o deslocamento de inatividade da sessão enviando um cookie de sessão atualizado. Ele apenas envia o cookie do cliente e nunca chama nenhuma API de armazenamento de sessão de backend. Normalmente, o session:refresh é usado para chamar isso indiretamente. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true.

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

session:refresh

sintaxe: ok, err = session:refresh()

Ou salva a sessão (criando um novo id de sessão) ou toca a sessão dependendo de se o tempo limite rolante está se aproximando, o que significa que, por padrão, quando 3/4 do tempo limite rolante é gasto, ou seja, 45 minutos com o tempo limite rolante padrão de uma hora. O toque tem um limiar, por padrão um minuto, então pode ser pulado em alguns casos (você pode chamar session:touch() para forçá-lo). Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true.

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

O código acima se parece um pouco com o ajudante session.start().

session:logout

sintaxe: ok, err = session:logout()

Logout destrói a sessão ou apenas limpa os dados para o público atual, e salva (desconectando do público atual). Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true.

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

session:destroy

sintaxe: ok, err = session:destroy()

Destrói a sessão e limpa os cookies. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true.

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

session:close

sintaxe: session:close()

Apenas fecha a instância da sessão para que não possa mais ser usada.

local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
  -- erro ao salvar a sessão
end
session:close()

session:set_data

sintaxe: session:set_data(data)

Define os dados da sessão. O data precisa ser uma tabela.

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

session:get_data

sintaxe: data = session:get_data()

Obtém os dados da sessão.

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

sintaxe: session:set(key, value)

Define um valor na sessão.

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

session:get

sintaxe: value = session:get(key)

Obtém um valor da sessão.

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

sintaxe: session:set_audience(audience)

Define o público da sessão.

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

session:get_audience

sintaxe: audience = session:get_audience()

Define o sujeito da sessão.

session:set_subject

sintaxe: session:set_subject(subject)

Define o sujeito da sessão.

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

session:get_subject

sintaxe: subject = session:get_subject()

Obtém o sujeito da sessão.

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

session:get_property

sintaxe: value = session:get_property(name)

Obtém uma propriedade da sessão. Possíveis nomes de propriedades:

  • "id": 43 bytes de id da sessão (igual ao nonce, mas codificado em base64 url)
  • "nonce": 32 bytes de nonce (igual ao id da sessão, mas em bytes brutos)
  • "audience": Público atual da sessão
  • "subject": Sujeito atual da sessão
  • "timeout": Tempo limite mais próximo (em segundos) (o que resta dele)
  • "idling-timeout"`: Tempo limite de inatividade da sessão (em segundos) (o que resta dele)
  • "rolling-timeout"`: Tempo limite rolante da sessão (em segundos) (o que resta dele)
  • "absolute-timeout"`: Tempo limite absoluto da sessão (em segundos) (o que resta dele)

Nota: o valor retornado pode ser nil.

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

session:set_remember

sintaxe: session:set_remember(value)

Define sessões persistentes ativadas/desativadas.

Em muitos formulários de login, o usuário tem a opção de "lembrar-me". Você pode chamar esta função com base no que o usuário selecionou.

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

sintaxe: remember = session:get_remember()

Obtém o estado das sessões persistentes.

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

sintaxe: ok, err = session:clear_request_cookie()

Modifica os cabeçalhos da solicitação removendo os cookies relacionados à sessão. Isso é útil quando você usa a biblioteca de sessão em um servidor proxy e não deseja que os cookies da sessão sejam encaminhados para o serviço upstream. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true (que pode ser ignorado).

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

session:set_headers

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

Define cabeçalhos de solicitação e resposta com base na configuração. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true (que pode ser ignorado).

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

Quando chamado sem argumentos, ele definirá os cabeçalhos de solicitação configurados com request_headers e os cabeçalhos de resposta configurados com response_headers.

Consulte configuração para possíveis nomes de cabeçalhos.

session:set_request_headers

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

Define cabeçalhos de solicitação. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true (que pode ser ignorado).

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

Quando chamado sem argumentos, ele definirá os cabeçalhos de solicitação configurados com request_headers.

Consulte configuração para possíveis nomes de cabeçalhos.

session:set_response_headers

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

Define cabeçalhos de resposta. Em caso de erro, retorna nil e uma mensagem de erro, caso contrário, true (que pode ser ignorado).

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

Quando chamado sem argumentos, ele definirá os cabeçalhos de resposta configurados com response_headers.

Consulte configuração para possíveis nomes de cabeçalhos.

session.info:set

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

Define um valor no armazenamento de informações da sessão. O armazenamento de informações da sessão pode ser usado em cenários quando você deseja armazenar dados no armazenamento do lado do servidor, mas não deseja criar uma nova sessão e enviar um novo cookie de sessão. Os dados do armazenamento de informações não são considerados ao verificar a tag de autenticação ou o código de autenticação da mensagem. Assim, se você quiser usar isso para dados que precisam ser criptografados, precisa criptografar o valor antes de passá-lo para esta função.

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

Com o armazenamento em cookie, isso ainda funciona, mas é quase o mesmo que session:set.

session.info:get

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

Obtém um valor do armazenamento de informações da sessão.

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

session.info:save

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

Salva informações. Apenas atualiza o armazenamento de backend. Não envia um novo cookie (exceto com armazenamento em cookie).

local session = require "resty.session".new()
session.info:set("last-access", ngx.now())
local ok, err = session.info:save()
[ HEADER -------------------------------------------------------------------------------------]
[ Tipo || Flags || SID || Criado em || Deslocamento Rolante || Tamanho || Tag || Deslocamento de Inatividade || Mac ]
[ 1B   || 2B    || 32B || 5B         || 4B             || 3B   || 16B || 3B            || 16B ]

e

[ PAYLOAD --]
[ Dados  *B  ]

Tanto o HEADER quanto o PAYLOAD são codificados em base64 url antes de serem colocados em um cabeçalho Set-Cookie. Ao usar um armazenamento do lado do servidor, o PAYLOAD não é colocado no cookie. Com o armazenamento em cookie, o cabeçalho codificado em base64 url é concatenado com o payload codificado em base64 url.

O HEADER tem um tamanho fixo de 82 bytes binários ou 110 bytes em forma codificada em base64 url.

Campos do cabeçalho explicados:

  • Tipo: número 1 empacotado em um único byte little endian (atualmente o único tipo suportado).
  • Flags: flags empacotadas em binário (curto) em uma forma de dois bytes little endian.
  • SID: 32 bytes de dados aleatórios criptográficos (ID da Sessão).
  • Criado em: segundos empacotados em binário desde a época em uma forma little endian, truncados para 5 bytes.
  • Deslocamento Rolante: segundos empacotados em binário desde o tempo de criação em uma forma little endian (inteiro).
  • Tamanho: tamanho dos dados empacotados em binário em uma forma de três bytes little endian.
  • Tag: 16 bytes da tag de autenticação da criptografia AES-256-GCM dos dados.
  • Deslocamento de Inatividade: segundos empacotados em binário desde o tempo de criação + deslocamento rolante em uma forma little endian, truncados para 3 bytes.
  • Mac: 16 bytes do código de autenticação da mensagem do cabeçalho.

Criptografia de Dados

  1. Material de chave inicial (IKM):
  2. derive IKM do secret hashando secret com SHA-256, ou
  3. use IKM de 32 bytes quando passado para a biblioteca com ikm
  4. Gere 32 bytes de id de sessão aleatório criptográfico (sid)
  5. Derive a chave de criptografia de 32 bytes e o vetor de inicialização de 12 bytes com HKDF usando SHA-256 (no modo FIPS, usa PBKDF2 com SHA-256).
  6. Use HKDF extract para derivar uma nova chave do ikm para obter key (este passo pode ser feito apenas uma vez por ikm):
    • comprimento da saída: 32
    • digest: "sha256"
    • key: <ikm>
    • modo: apenas extração
    • info: ""
    • salt: ""
  7. Use HKDF expand para derivar 44 bytes de output:
    • comprimento da saída: 44
    • digest: "sha256"
    • key: <key>
    • modo: apenas expansão
    • info: "encryption:<sid>"
    • salt: ""
  8. Os primeiros 32 bytes de output são a chave de criptografia (aes-key), e os últimos 12 bytes são o vetor de inicialização (iv).
  9. Criptografe plaintext (JSON codificado e opcionalmente desinflado) usando AES-256-GCM para obter ciphertext e tag.
  10. cipher: "aes-256-gcm"
  11. key: <aes-key>
  12. iv: <iv>
  13. plaintext: <plaintext>
  14. aad: use os primeiros 47 bytes do header como aad, que inclui:
    1. Tipo
    2. Flags
    3. ID da Sessão
    4. Hora de Criação
    5. Deslocamento Rolante
    6. Tamanho dos Dados

Há uma variação para cookies remember no passo 3, onde podemos usar PBKDF2 em vez de HKDF, dependendo da configuração de remember_safety (também a usamos no modo FIPS). As configurações de PBKDF2:

  • comprimento da saída: 44
  • digest: "sha256"
  • senha: <key>
  • salt: "encryption:<sid>"
  • iterações: <1000|10000|100000|1000000>
  • pkcs5: 1 (compatível com FIPS em nosso caso de uso, mas é necessário desabilitar as verificações baseadas em SP800-132, como contagem de iterações, veja: https://docs.openssl.org/master/man7/provider-kdf/#kdf-parameters)

As contagens de iterações são baseadas na configuração de remember_safety ("Low", "Medium", "High", "Very High"), se remember_safety estiver definido como "None", usaremos o HDKF como acima.

Nota: Para compatibilidade com versões anteriores, desabilitamos as verificações de conformidade do SP800-132 no modo FIPS. Essas verificações garantem que o comprimento do salt seja de pelo menos 128 bits, o comprimento da chave derivada seja de pelo menos 112 bits e que a contagem de iterações seja de pelo menos 1000. Essas verificações estão desabilitadas por padrão no provedor padrão do OpenSSL, mas estão habilitadas por padrão no provedor FIPS.

  1. Derive a chave de autenticação de 32 bytes (mac_key) com HKDF usando SHA-256 (no modo FIPS, usa PBKDF2 com SHA-256):
    1. Use HKDF extract para derivar uma nova chave do ikm para obter key (este passo pode ser feito apenas uma vez por ikm e reutilizado com a geração da chave de criptografia):
      • comprimento da saída: 32
      • digest: "sha256"
      • key: <ikm>
      • modo: apenas extração
      • info: ""
      • salt: ""
    2. Use HKDF expand para derivar 32 bytes de mac-key:
      • comprimento da saída: 32
      • digest: "sha256"
      • key: <key>
      • modo: apenas expansão
      • info: "authentication:<sid>"
      • salt: ""
  2. Calcule o código de autenticação da mensagem usando HMAC-SHA256:
  3. digest: "sha256"
  4. key: <mac-key>
  5. message: use os primeiros 66 bytes do header, que inclui:
    1. Tipo
    2. Flags
    3. ID da Sessão
    4. Hora de Criação
    5. Deslocamento Rolante
    6. Tamanho dos Dados
    7. Tag
    8. Deslocamento de Inatividade

Interface de Armazenamento Personalizado

Se você deseja implementar um armazenamento personalizado, precisa implementar a seguinte interface:

---
-- <custom> backend para biblioteca de sessão
--
-- @module <custom>


---
-- Armazenamento
-- @section instance


local metatable = {}


metatable.__index = metatable


function metatable.__newindex()
  error("tentativa de atualizar uma tabela somente leitura", 2)
end


---
-- Armazena dados da sessão.
--
-- @function instance:set
-- @tparam string name nome do cookie
-- @tparam string key chave da sessão
-- @tparam string value valor da sessão
-- @tparam number ttl tempo de vida da sessão
-- @tparam number current_time tempo atual
-- @tparam[opt] string old_key id da sessão antiga
-- @tparam string stale_ttl tempo de vida antiga
-- @tparam[opt] table metadata tabela de metadados
-- @treturn true|nil ok
-- @treturn string mensagem de erro
function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata)
  -- NYI
end


---
-- Recupera dados da sessão.
--
-- @function instance:get
-- @tparam string name nome do cookie
-- @tparam string key chave da sessão
-- @treturn string|nil dados da sessão
-- @treturn string mensagem de erro
function metatable:get(name, key)
  -- NYI
end


---
-- Deleta dados da sessão.
--
-- @function instance:delete
-- @tparam string name nome do cookie
-- @tparam string key chave da sessão
-- @tparam[opt] table metadata metadados da sessão
-- @treturn boolean|nil dados da sessão
-- @treturn string mensagem de erro
function metatable:delete(name, key, current_time, metadata)
  -- NYI
end


local storage = {}


---
-- Construtores
-- @section constructors


---
-- Configuração
-- @section configuration


---
-- Configuração de armazenamento <custom>
-- @field <field-name> TBD
-- @table configuration


---
-- Cria um armazenamento <custom>.
--
-- Isso cria uma nova instância de armazenamento em memória compartilhada.
--
-- @function module.new
-- @tparam[opt]  table   configuration  armazenamento <custom> @{configuration}
-- @treturn      table                  instância de armazenamento <custom>
function storage.new(configuration)
  -- NYI
  -- return setmetatable({}, metatable)
end


return storage

Consulte as implementações existentes para os detalhes. E, por favor, faça um pull-request para que possamos integrá-lo diretamente à biblioteca para outros usuários também.

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub para nginx-module-session.