• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

bungle/lua-resty-session: Session library for OpenResty – flexible and secure

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

bungle/lua-resty-session

开源软件地址(OpenSource Url):

https://github.com/bungle/lua-resty-session

开源编程语言(OpenSource Language):

Lua 99.9%

开源软件介绍(OpenSource Introduction):

lua-resty-session

lua-resty-session is a secure, and flexible session library for OpenResty.

Hello World with lua-resty-session

worker_processes  1;

events {
    worker_connections  1024;
}

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

        location / {
            content_by_lua '
                ngx.say("<html><body><a href=/start>Start the test</a>!</body></html>")
            ';
        }
        location /start {
            content_by_lua '
                local session = require "resty.session".start()
                session.data.name = "OpenResty Fan"
                session:save()
                ngx.say("<html><body>Session started. ",
                        "<a href=/test>Check if it is working</a>!</body></html>")
            ';
        }
        location /test {
            content_by_lua '
                local session = require "resty.session".open()
                ngx.say("<html><body>Session was started by <strong>",
                        session.data.name or "Anonymous",
                        "</strong>! <a href=/modify>Modify the session</a>.</body></html>")
            ';
        }
        location /modify {
            content_by_lua '
                local session = require "resty.session".start()
                session.data.name = "Lua Fan"
                session:save()
                ngx.say("<html><body>Session modified. ",
                        "<a href=/modified>Check if it is modified</a>!</body></html>")
            ';
        }
        location /modified {
            content_by_lua '
                local session = require "resty.session".open()
                ngx.say("<html><body>Session was modified by <strong>",
                        session.data.name or "Anonymous",
                        "</strong>! <a href=/destroy>Destroy the session</a>.</body></html>")
            ';
        }
        location /destroy {
            content_by_lua '
                require "resty.session".destroy()
                ngx.say("<html><body>Session was destroyed. ",
                        "<a href=/check>Is it really so</a>?</body></html>")
            ';
        }
        location /check {
            content_by_lua '
                local session = require "resty.session".open()
                ngx.say("<html><body>Session was really destroyed, you are known as ",
                        "<strong>",
                        session.data.name or "Anonymous",
                        "</strong>! <a href=/>Start again</a>.</body></html>")
            ';
        }
    }
}

Installation

Just place session.lua and session directory somewhere in your package.path, under resty directory. If you are using OpenResty, the default location would be /usr/local/openresty/lualib/resty.

Using OpenResty Package Manager (opm)

$ opm get bungle/lua-resty-session

Using LuaRocks

$ luarocks install lua-resty-session

LuaRocks repository for lua-resty-session is located at https://luarocks.org/modules/bungle/lua-resty-session.

About The Defaults

lua-resty-session does by default session only cookies (non-persistent, and HttpOnly) so that the cookies are not readable from Javascript (not subjectible to XSS in that matter). It will also set Secure flag by default when the request was made via SSL/TLS connection or when cookie name (session.name) is prefixed with __Secure- or __Host- (see Cookies: HTTP State Management Mechanism. Cookies send via SSL/TLS don't work when sent via HTTP and vice-versa (unless the checks are disabled). By default the HMAC key is generated from session id (random bytes generated with OpenSSL), expiration time, unencrypted data, http_user_agent and scheme. You may also configure it to use remote_addr as well by setting set $session_check_addr on; (but this may be problematic with clients behind proxies or NATs that change the remote address between requests). If you are using SSL Session IDs you may also add set $session_check_ssi on;, but please check that it works accordingly (you may need to adjust both SSL and session library settings).

The data part is encrypted with AES-algorithm (by default it uses OpenSSL EVP_aes_256_cbc and EVP_sha512 functions that are provided with lua-resty-string. They come pre-installed with the default OpenResty bundle. The lua-resty-session library is not tested with all the resty.aes functions (but the defaults are tested to be working). Please let me know or contact lua-resty-string project if you hit any problems with different algorithms. We also support pluggable cipher adapters. You can also disable encryption by choosing none adapter.

Session identifier length is by default 16 bytes (randomly generated data with OpenSSL RAND_bytes function). The server secret is also generated by default with this same function and it's default length is 32 bytes. This will work until Nginx is restarted, but you might want to consider setting your own secret using set $session_secret 623q4hR325t36VsCD3g567922IC0073T;, for example (this will work in farms installations as well). On farm installations you should also configure other session configuration variables the same on all the servers in the farm.

Cookie parts are encoded with cookie safe Base64 encoding without padding (we also support pluggable encoders). Before encrypting and encoding the data part, the data is serialized with JSON encoding (so you can use basic Lua types in data, and expect to receive them back as the same Lua types). JSON encoding is done by the bundled OpenResty cJSON library (Lua cJSON). We do support pluggable serializers as well, though only serializer currently supplied is JSON. Cookie's path scope is by default / (meaning that it will be send to all paths in the server). The domain scope is not set by default, and it means that the cookie will only be sent back to same domain/host where it originated. If you set session name (e.g. set $session_name <value>) and it contains prefix __Secure- the Secure flag will be forced, and if it contains __Host- the path is forced to / and the domain is removed, and the Secure flag will be forced too.

For session data we do support pluggable storage adapters. The default adapter is cookie that stores data to client-side cookie. Currently we do also support a few server side storages: shm (aka a shared dictionary), memcache, redis, and dshm.

Notes About Turning Lua Code Cache Off

In issue (#15) it was raised that there may be problems of using lua-resty-session when the lua_code_cache setting has been turned off.

Nginx:

lua_code_cache off;

The problem is caused by the fact that by default we do generate session secret automatically with a random generator (on first use of the library). If the code cache is turned off, we regenerate the secret on each request. That will invalidate the cookies aka making sessions non-functioning. The cure for this problem is to define the secret in Nginx or in Lua code (it is a good idea to always have session secret defined).

Nginx:

set $session_secret 623q4hR325t36VsCD3g567922IC0073T;

Lua:

local session = require "resty.session".start{ secret = "623q4hR325t36VsCD3g567922IC0073T" }
-- or
local session = require "resty.session".new()
session.secret = "623q4hR325t36VsCD3g567922IC0073T"

About Locking

With some storage adapters we implement locking mechanism. The locks are normally released automatically, and they will timeout, but if you happen to call session.start() or session:start(), then it is your responsibility to release the lock by calling session:close(), session:save() or session:destroy().

Pluggable Session Strategies

Strategies can be a bit cumbersome to do with just configuration, and that's why you can implement them only with the code. Currently lua-resty-session comes with two strategies:

  • default — the default strategy (original implementation)
  • regenerate — similar to default strategy, but does not use session expiry with HMAC functions, and instead generates a new session identifier on each save.

The default one has been here from the beginning, but recently I got information about use case of Javascript application with parallel asynchronous queries, where the session was saved to a database with a custom storage adapter using header_filter phase, which resulted the need to use the asynchronous ngx.timer. And that resulted that the JS XHR requests may have sent an old cookie, or perhaps a new cookie that was not yet found in db because of async timer. This resulted issues because cryptographic functions in default strategy used expires, and every time you saved a cookie it got a new expiry. The regenerate adapter does not use expiry anymore, but it instead generates a new session id on each save call. This makes a new row in a database while the previous session will still function. If your storage adapter implements ttl the regenerate strategy will call that with the old id and 10 seconds of ttl. default strategy is still adequate if you use cookie storage adapter as that is not issue with it, but if using server side storage adapter like redis or memcache you may want to consider using regenerate if you have a heavily JS based application with a lot of asynchronous queries at the same time. This issue happens usually when session is about to be renewed, so it is quite rare even when using default strategy.

Strategy can be selected with configuration (if no configuration is present, the default strategy is picked up):

set $session_strategy regenerate;

To implement a custom strategy, please checkout the existing ones.

Basically you need to implement at least these functions:

  • boolean open(session, cookie)
  • boolean start(session)
  • boolean destroy(session)
  • boolean close(session)
  • cookie save(session, close)
  • cookie touch(session, close)

Pluggable HMAC Algorithms

If your strategy happens to be using HMAC, like the default and regenerate ones do, you can tell them what HMAC algorithm to use. At the moment only HMAC SHA1 is available as that comes with OpenResty and works without additional dependencies. You may implement your own custom HMAC algorithms (preferrably binding to some existing crypto library, such as OpenSSL), and the strategies will pick up from there.

HMAC can be selected with configuration (if no configuration is present, the sha1 strategy is picked up):

set $session_hmac sha1;

To implement your own, you need to implement this interface: digest hmac(secret, input).

Pluggable Storage Adapters

With version 2.0 we started to support pluggable session data storage adapters. We do currently have support for these backends:

  • cookie aka Client Side Cookie (this is the default adapter)
  • shm aka Lua Shared Dictionary
  • memcache aka Memcached Storage Backend (thanks @zandbelt)
  • redis aka Redis Backend
  • dshm aka ngx-distributed-shm Storage Adapter (thanks @grrolland)

Here are some comparisons about the backends:

cookie shm memcache redis dshm
Stateless
Lock-less ¹ ¹ ¹
Works with Web Farms
Session Data Stored on Client
Zero Configuration
Extra Dependencies
Extra Security ²

¹ Can be configured lock-less.

² HMAC is stored on a client but the data is stored on a server. That means that you are unable to edit cookie if you cannot edit server side storage as well, and vice-versa.

The storage adapter can be selected from Nginx config like this:

set $session_storage shm;

Or with Lua code like this:

local session = require "resty.session".new() -- OR .open() | .start()
-- After new you cannot specify storage as a string,
-- you need to give actual implementation
session.storage = require "resty.session.storage.shm".new(session)
-- or
local session = require "resty.session".new({
  storage = "shm"
})

Cookie Storage Adapter

Cookie storage adapter is the default adapter that is used if storage adapter has not been configured. Cookie adapter does not have any settings.

Cookie adapter can be selected with configuration (if no configuration is present, the cookie adapter is picked up):

set $session_storage cookie;

NOTE:

If you store large amounts of data in a cookie, this library will automatically split the cookies to 4k chars chunks. With large cookies, you may need to adjust your Nginx configuration to accept large client header buffers. E.g.:

large_client_header_buffers 4 16k;

Shared Dictionary Storage Adapter

Shared dictionary uses OpenResty shared dictionary and works with multiple worker processes, but it isn't a good choice if you want to run multiple separate frontends. It is relatively easy to configure and has some added benefits on security side compared to cookie, although the normal cookie adapter is quite secure as well. For locking the shm adapter uses lua-resty-lock.

Shared dictionary adapter can be selected with configuration:

set $session_storage shm;

But for this to work, you will also need a storage configured for that:

http {
   lua_shared_dict sessions 10m;
}

Additionally you can configure the locking and some other things as well:

set $session_shm_store         sessions;
set $session_shm_uselocking    on;
set $session_shm_lock_exptime  30;    # (in seconds)
set $session_shm_lock_timeout  5;     # (in seconds)
set $session_shm_lock_step     0.001; # (in seconds)
set $session_shm_lock_ratio    2;
set $session_shm_lock_max_step 0.5;   # (in seconds)

The keys stored in shared dictionary are in form:

{session id} and {session id}.lock.

Memcache Storage Adapter

Memcache storage adapter stores the session data inside Memcached server. It is scalable and works with web farms.

Memcache adapter can be selected with configuration:

set $session_storage memcache;

Additionally you can configure Memcache adapter with these settings:

set $session_memcache_prefix           sessions;
set $session_memcache_connect_timeout  1000; # (in milliseconds)
set $session_memcache_send_timeout     1000; # (in milliseconds)
set $session_memcache_read_timeout     1000; # (in milliseconds)
set $session_memcache_socket           unix:///var/run/memcached/memcached.sock;
set $session_memcache_host             127.0.0.1;
set $session_memcache_port             11211;
set $session_memcache_uselocking       on;
set $session_memcache_spinlockwait     150;  # (in milliseconds)
set $session_memcache_maxlockwait      30;   # (in seconds)
set $session_memcache_pool_name        sessions;
set $session_memcache_pool_timeout     1000; # (in milliseconds)
set $session_memcache_pool_size        10;
set $session_memcache_pool_backlog     10;

The keys stored in Memcached are in form:

{prefix}:{session id} and {prefix}:{session id}.lock.

Redis Storage Adapter

Redis storage adapter stores the session data inside Redis server. It is scalable and works with web farms.

Redis adapter can be selected with configuration:

set $session_storage redis;

Additionally you can configure Redis adapter with these settings:

set $session_redis_prefix                   sessions;
set $session_redis_database                 0;
set $session_redis_connect_timeout          1000; # (in milliseconds)
set $session_redis_send_timeout             1000; # (in milliseconds)
set $session_redis_read_timeout             1000; # (in milliseconds)
set $session_redis_socket                   unix:///var/run/redis/redis.sock;
set $session_redis_host                     127.0.0.1;
set $session_redis_port                     6379;
set $session_redis_ssl                      off;
set $session_redis_ssl_verify               off;
set $session_redis_server_name              example.com; # for TLS SNI
set $session_redis_username                 username;
set $session_redis_password                 password;
set $session_redis_uselocking               on;
set $session_redis_spinlockwait             150;  # (in milliseconds)
set $session_redis_maxlockwait              30;   # (in seconds)
set $session_redis_pool_name                sessions;
set $session_redis_pool_timeout             1000; # (in milliseconds)
set $session_redis_pool_size                10;
set $session_redis_pool_backlog             10;
set $session_redis_cluster_name             redis-cluster;
set $session_redis_cluster_dict             sessions;
set $session_redis_cluster_maxredirections  5;
set $session_redis_cluster_nodes            '127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006';

Note: session_redis_auth has been deprecated; use session_redis_password.

To use cluster you need also to install:

luarocks install kong-redis-cluster
# OR
luarocks install lua-resty-redis-cluster

# OR install this manually https://github.com/steve0511/resty-redis-cluster

The keys stored in Redis are in form:

{prefix}:{session id} and {prefix}:{session id}.lock.

DSHM Storage Adapter

DSHM storage adapter stores the session data inside Distributed Shared Memory server based on Vertx and Hazelcast. It is scalable and works with web farms.

The DSHM lua library and the DSHM servers should be installed conforming with the documentation here.

DSHM adapter can be selected with configuration:

set $session_storage dshm;

Additionally you can configure DSHM adapter with these settings:

set $session_dshm_region           sessions;
set $session_dshm_connect_timeout  1000; # (in milliseconds)
set $session_dshm_send_timeout     1000; # (in milliseconds)
set $session_dshm_read_timeout     1000; # (in milliseconds)
set $session_dshm_host             127.0.0.1;
set $session_dshm_port             4321;
set $session_dshm_pool_name        sessions;
set $session_dshm_pool_timeout     1000; # (in milliseconds)
set $session_dshm_pool_size        10;
set $session_dshm_pool_backlog     10;

The keys stored in DSHM are in form:

{region}::{encoded session id}

The region represents the cache region in DSHM.

Implementing a Storage Adapter

It is possible to implement additional storage adapters using the plugin architecture in lua-resty-session.

You need to implement APIs you need

  • storage new(session)
  • boolean storage:open(id)
  • boolean storage:start(id)
  • boolean storage:save(id, ttl, data, close)
  • bookean storage:close(id)
  • boolean storage:destroy(id)
  • boolean storage:ttl(id, ttl, close)

The id parameter is already encoded, but data is in raw bytes, so please encode it as needed.

You have to place your adapter inside resty.session.storage for auto-loader to work.

To configure session to use your adapter, you can do so with Nginx configuration (or in Lua code):

# Just an example. Pull request for MySQL support is greatly welcomed.
set $session_storage mysql;

Pluggable Ciphers

With version 2.1 we started to support pluggable ciphers. We currently have support for these ciphers:

  • aes aka AES encryption / decryption using lua-resty-string's AES library (the default).
  • none aka no encryption or decryption is done.

The cipher adapter can be selected from Nginx config like this:

set $session_cipher aes;

Or with Lua code like this:

local session = require "resty.session".start{ cipher = "aes" }

AES Cipher

AES Cipher uses lua-resty-string's (an OpenResty core library) AES implementation (bindings to OpenSSL) for encryption.

AES adapter can be selected with configuration:

set $session_cipher aes;

Additionally you can configure Memcache adapter with these settings:

set $session_aes_size   256;
set $session_aes_mode   "cbc";
set $session_aes_hash   "sha512";
set $session_aes_rounds 1;

Here follows the description of each setting:

size

session.aes.size holds the size of the cipher (lua-resty-string supports AES in 128, 192, and 256 bits key sizes). See aes.cipher function in lua-resty-string for more information. By default this will use 256 bits key size. This can be configured with Nginx set $session_aes_size 256;.

mode

session.aes.mode holds the mode of the cipher. lua-resty-string supports AES in ecb, cbc, cfb1, cfb8, cfb128, ofb, ctr, and gcm (recommended!) modes (ctr mode is not available with 256 bit keys). See aes.cipher function in lua-resty-string for more information. By default cbc mode is used. This can be configured with Nginx set $session_aes_mode cbc;.

hash

session.aes.hash is used in ecryption key, and iv derivation (see: OpenSSL EVP_BytesToKey). By default sha512 is used but md5, sha1, sha224, sha256, and sha384 are supported as well in lua-resty-string. This can be configured with Nginx set $session_aes_hash sha512;.

rounds

session.aes.rounds can be used to slow-down the encryption key, and iv derivation. By default this is set to 1 (the fastest). This can be configured with Nginx set $session_aes_rounds 1;.

None Cipher

None cipher disables encryption of the session data. This can be handy if you want to debug things or want you session management as light as possible, or perhaps share the session data with some other process without having to deal with encryption key management. In general it is better to have encryption enabled in a production.

None adapter can be selected with configuration:

set $session_cipher none;

There isn't any settings for None adapter as it is basically a no-op adapter.

Implementing a Cipher Adapter

If you want to write your own cipher adapter, you need to implement these three methods:

  • cipher new(session)
  • string, err, tag = cipher:encrypt(data, key, salt, aad)
  • string, err, tag = cipher:decrypt(data, key, salt, aad, tag)

If you do not use say salt or aad (associated data) in your cipher, you can ignore them. If you don't use AEAD construct (like AES in GCM-mode), don't return tag.

You have to place your adapter inside resty.session.ciphers for auto-loader to work.

Pluggable Serializers

Currently we only support JSON serializer, but there is a plugin architecture that you can use to plugin your own serializer. The serializer is used to serialize session data in a form that can be later deserialized and stored in some of our supported storages.

The supported serializer names are:

  • json

You need only to implement two functions to write an adapter:

  • string serialize(table)
  • table deserialize(string)

You have to place your adapter inside resty.session.serializers for auto-loader to work.

To configure session to use your adapter, you can do so with Nginx configuration (or in Lua code):

set $session_serializer json;


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap