Skip to content

Commit 56918ea

Browse files
committed
Implemented SameSite cookie flag, added small bug fix related to #27, and some cleanups, marking this as a Release 2.4.
1 parent 6a0609a commit 56918ea

File tree

9 files changed

+152
-104
lines changed

9 files changed

+152
-104
lines changed

CHANGES

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
Changes with lua-resty-session 2.4 17 Apr 2016
2+
3+
*) Feature: Cookie will now have SameSite attribute set as "Lax" by
4+
default. You can turn it off or set to "Strict" by configuration.
5+
6+
*) Change: Calling save will not also set session.id if the save
7+
was called without calling start first.
8+
9+
See Also: https://github.com/bungle/lua-resty-session/issues/27
10+
11+
Thanks @hcaihao
12+
113
Changes with lua-resty-session 2.3 16 Oct 2015
214

315
*) Bugfix: Fixes issue #19 where regenerating session would

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2014, Aapo Talvensaari
1+
Copyright (c) 2014 – 2016, Aapo Talvensaari
22
All rights reserved.
33

44
Redistribution and use in source and binary forms, with or without

README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,14 @@ This can be configured with Nginx `set $session_cookie_path /forums/;`.
706706
Nginx variable `host`. This can be configured with Nginx `set $session_cookie_domain openresty.org;`.
707707
For `localhost` this is omitted.
708708

709+
#### string session.cookie.samesite
710+
711+
`session.cookie.samesite` holds the value of the cookie SameSite flag. By default we do use value of `Lax`.
712+
The possible values are `Lax`, `Strict`, and `off`. Actually, setting this parameter anything else than
713+
`Lax` or `Strict` will turn this off (but in general, you shouldn't do it). If you want better protection
714+
against Cross Site Requet Forgery (CSRF), set this to `Strict`. Default value of `Lax` gives you quite a
715+
good protection against CSRF, but `Strict` goes even further.
716+
709717
#### boolean session.cookie.secure
710718

711719
`session.cookie.secure` holds the value of the cookie `Secure` flag. meaning that when set the client will
@@ -830,6 +838,7 @@ set $session_cookie_renew 600;
830838
set $session_cookie_lifetime 3600;
831839
set $session_cookie_path /;
832840
set $session_cookie_domain openresty.org;
841+
set $session_cookie_samesite Lax;
833842
set $session_cookie_secure on;
834843
set $session_cookie_httponly on;
835844
set $session_cookie_delimiter |;
@@ -850,7 +859,7 @@ set $session_cipher_rounds 1;
850859
`lua-resty-session` uses two clause BSD license.
851860

852861
```
853-
Copyright (c) 2015, Aapo Talvensaari
862+
Copyright (c) 2014 – 2016 Aapo Talvensaari
854863
All rights reserved.
855864
856865
Redistribution and use in source and binary forms, with or without modification,

lib/resty/session.lua

+89-66
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
local ngx_var = ngx.var
2-
local ngx_header = ngx.header
1+
local require = require
2+
local var = ngx.var
3+
local header = ngx.header
34
local concat = table.concat
45
local hmac = ngx.hmac_sha1
56
local time = ngx.time
67
local http_time = ngx.http_time
8+
local find = string.find
79
local type = type
10+
local pcall = pcall
11+
local tonumber = tonumber
812
local setmetatable = setmetatable
13+
local getmetatable = getmetatable
914
local ffi = require "ffi"
1015
local ffi_cdef = ffi.cdef
1116
local ffi_new = ffi.new
@@ -33,50 +38,63 @@ end
3338

3439
local function setcookie(session, value, expires)
3540
local c = session.cookie
36-
local cookie = { session.name, "=", value or "" }
37-
local domain = c.domain
41+
local i = 3
42+
local n = session.name .. "="
43+
local k = { n, value or "" }
44+
local d = c.domain
3845
if expires then
39-
cookie[#cookie + 1] = "; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0"
46+
k[i] = "; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0"
47+
i=i+1
4048
elseif c.persistent then
41-
cookie[#cookie + 1] = "; Expires="
42-
cookie[#cookie + 1] = http_time(session.expires)
43-
cookie[#cookie + 1] = "; Max-Age="
44-
cookie[#cookie + 1] = c.lifetime
49+
k[i] = "; Expires="
50+
k[i+1] = http_time(session.expires)
51+
k[i+2] = "; Max-Age="
52+
k[i+3] = c.lifetime
53+
i=i+4
4554
end
46-
if domain and domain ~= "localhost" and domain ~= "" then
47-
cookie[#cookie + 1] = "; Domain="
48-
cookie[#cookie + 1] = domain
55+
if d and d ~= "localhost" and d ~= "" then
56+
k[i] = "; Domain="
57+
k[i+1] = d
58+
i=i+2
59+
end
60+
k[i] = "; Path="
61+
k[i+1] = c.path or "/"
62+
i=i+2
63+
if c.samesite == "Lax" or c.samesite == "Strict" then
64+
k[i] = "; SameSite="
65+
k[i+1] = c.samesite
66+
i=i+2
4967
end
50-
cookie[#cookie + 1] = "; Path="
51-
cookie[#cookie + 1] = c.path or "/"
5268
if c.secure then
53-
cookie[#cookie + 1] = "; Secure"
69+
k[i] = "; Secure"
70+
i=i+1
5471
end
5572
if c.httponly then
56-
cookie[#cookie + 1] = "; HttpOnly"
73+
k[i] = "; HttpOnly"
74+
i=i+1
5775
end
58-
local needle = concat(cookie, nil, 1, 2)
59-
cookie = concat(cookie)
60-
local cookies = ngx_header["Set-Cookie"]
61-
local t = type(cookies)
76+
k = concat(k)
77+
local s = header["Set-Cookie"]
78+
local t = type(s)
6279
if t == "table" then
63-
local found = false
64-
for i, c in ipairs(cookies) do
65-
if c:find(needle, 1, true) == 1 then
66-
cookies[i] = cookie
67-
found = true
80+
local f = false
81+
local z = #s
82+
for i=1, z do
83+
if find(s[i], n, 1, true) == 1 then
84+
s[i] = k
85+
f = true
6886
break
6987
end
7088
end
71-
if not found then
72-
cookies[#cookies + 1] = cookie
89+
if not f then
90+
s[z+1] = k
7391
end
74-
elseif t == "string" and cookies:find(needle, 1, true) ~= 1 then
75-
cookies = { cookies, cookie }
92+
elseif t == "string" and find(s, n, 1, true) ~= 1 then
93+
s = { s, k }
7694
else
77-
cookies = cookie
95+
s = k
7896
end
79-
ngx_header["Set-Cookie"] = cookies
97+
header["Set-Cookie"] = s
8098
return true
8199
end
82100

@@ -104,35 +122,36 @@ local function regenerate(session, flush)
104122
end
105123
end
106124

107-
local persistent = enabled(ngx_var.session_cookie_persistent or false)
125+
local persistent = enabled(var.session_cookie_persistent or false)
108126
local defaults = {
109-
name = ngx_var.session_name or "session",
110-
storage = ngx_var.session_storage or "cookie",
111-
serializer = ngx_var.session_serializer or "json",
112-
encoder = ngx_var.session_encoder or "base64",
113-
cipher = ngx_var.session_cipher or "aes",
127+
name = var.session_name or "session",
128+
storage = var.session_storage or "cookie",
129+
serializer = var.session_serializer or "json",
130+
encoder = var.session_encoder or "base64",
131+
cipher = var.session_cipher or "aes",
114132
cookie = {
115133
persistent = persistent,
116-
renew = tonumber(ngx_var.session_cookie_renew) or 600,
117-
lifetime = tonumber(ngx_var.session_cookie_lifetime) or 3600,
118-
path = ngx_var.session_cookie_path or "/",
119-
domain = ngx_var.session_cookie_domain,
120-
secure = enabled(ngx_var.session_cookie_secure),
121-
httponly = enabled(ngx_var.session_cookie_httponly or true),
122-
delimiter = ngx_var.session_cookie_delimiter or "|"
134+
renew = tonumber(var.session_cookie_renew) or 600,
135+
lifetime = tonumber(var.session_cookie_lifetime) or 3600,
136+
path = var.session_cookie_path or "/",
137+
domain = var.session_cookie_domain,
138+
secure = enabled(var.session_cookie_secure),
139+
httponly = enabled(var.session_cookie_httponly or true),
140+
samesite = var.session_cookie_samesite or "Lax",
141+
delimiter = var.session_cookie_delimiter or "|"
123142
}, check = {
124-
ssi = enabled(ngx_var.session_check_ssi or persistent == false),
125-
ua = enabled(ngx_var.session_check_ua or true),
126-
scheme = enabled(ngx_var.session_check_scheme or true),
127-
addr = enabled(ngx_var.session_check_addr or false)
143+
ssi = enabled(var.session_check_ssi or persistent == false),
144+
ua = enabled(var.session_check_ua or true),
145+
scheme = enabled(var.session_check_scheme or true),
146+
addr = enabled(var.session_check_addr or false)
128147
}, identifier = {
129-
length = tonumber(ngx_var.session_identifier_length) or 16
148+
length = tonumber(var.session_identifier_length) or 16
130149
}
131150
}
132-
defaults.secret = ngx_var.session_secret or random(32)
151+
defaults.secret = var.session_secret or random(32)
133152

134153
local session = {
135-
_VERSION = "2.2"
154+
_VERSION = "2.3-dev"
136155
}
137156

138157
session.__index = session
@@ -177,6 +196,7 @@ function session.new(opts)
177196
domain = a.domain or b.domain,
178197
secure = a.secure or b.secure,
179198
httponly = a.httponly or b.httponly,
199+
samesite = a.samesite or b.samesite,
180200
delimiter = a.delimiter or b.delimiter
181201
}, check = {
182202
ssi = c.ssi or d.ssi,
@@ -201,43 +221,43 @@ function session.open(opts)
201221
else
202222
self = session.new(opts)
203223
end
204-
local scheme = ngx_header["X-Forwarded-Proto"]
224+
local scheme = header["X-Forwarded-Proto"]
205225
if self.cookie.secure == nil then
206226
if scheme then
207227
self.cookie.secure = scheme == "https"
208228
else
209-
self.cookie.secure = ngx_var.https == "on"
229+
self.cookie.secure = var.https == "on"
210230
end
211231
end
212-
scheme = self.check.scheme and (scheme or ngx_var.scheme or "") or ""
232+
scheme = self.check.scheme and (scheme or var.scheme or "") or ""
213233
local addr = ""
214234
if self.check.addr then
215-
addr = ngx_header["CF-Connecting-IP"] or
216-
ngx_header["Fastly-Client-IP"] or
217-
ngx_header["Incap-Client-IP"] or
218-
ngx_header["X-Real-IP"]
235+
addr = header["CF-Connecting-IP"] or
236+
header["Fastly-Client-IP"] or
237+
header["Incap-Client-IP"] or
238+
header["X-Real-IP"]
219239
if not addr then
220-
addr = ngx_header["X-Forwarded-For"]
240+
addr = header["X-Forwarded-For"]
221241
if addr then
222242
-- We shouldn't really get the left-most address, because of spoofing,
223243
-- but this is better handled with a module, like nginx realip module,
224244
-- anyway (see also: http://goo.gl/Z6u2oR).
225-
local s = (addr:find(',', 1, true))
245+
local s = find(addr, ',', 1, true)
226246
if s then
227247
addr = addr:sub(1, s - 1)
228248
end
229249
else
230-
addr = ngx_var.remote_addr
250+
addr = var.remote_addr
231251
end
232252
end
233253
end
234254
self.key = concat{
235-
self.check.ssi and (ngx_var.ssl_session_id or "") or "",
236-
self.check.ua and (ngx_var.http_user_agent or "") or "",
255+
self.check.ssi and (var.ssl_session_id or "") or "",
256+
self.check.ua and (var.http_user_agent or "") or "",
237257
addr,
238258
scheme
239259
}
240-
local cookie = ngx_var["cookie_" .. self.name]
260+
local cookie = var["cookie_" .. self.name]
241261
if cookie then
242262
local i, e, d, h = self.storage:open(cookie, self.cookie.lifetime)
243263
if i and e and e > time() and d and h then
@@ -283,8 +303,11 @@ function session:regenerate(flush)
283303
return save(self)
284304
end
285305

286-
function session:save()
287-
return save(self, true)
306+
function session:save(close)
307+
if not self.id then
308+
self.id = random(self.identifier.length)
309+
end
310+
return save(self, close)
288311
end
289312

290313
function session:destroy()

lib/resty/session/ciphers/aes.lua

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ local tonumber = tonumber
33
local aes = require "resty.aes"
44
local cip = aes.cipher
55
local hashes = aes.hash
6-
local ngx_var = ngx.var
6+
local var = ngx.var
77

88
local CIPHER_MODES = {
99
ecb = "ecb",
@@ -22,10 +22,10 @@ local CIPHER_SIZES = {
2222
}
2323

2424
local defaults = {
25-
size = CIPHER_SIZES[ngx_var.session_aes_size] or 256,
26-
mode = CIPHER_MODES[ngx_var.session_aes_mode] or "cbc",
27-
hash = hashes[ngx_var.session_aes_hash] or "sha512",
28-
rounds = tonumber(ngx_var.session_aes_rounds) or 1
25+
size = CIPHER_SIZES[var.session_aes_size] or 256,
26+
mode = CIPHER_MODES[var.session_aes_mode] or "cbc",
27+
hash = hashes[var.session_aes_hash] or "sha512",
28+
rounds = tonumber(var.session_aes_rounds) or 1
2929
}
3030

3131
local cipher = {}
@@ -35,10 +35,10 @@ cipher.__index = cipher
3535
function cipher.new(config)
3636
local a = config.aes or defaults
3737
return setmetatable({
38-
size = CIPHER_MODES[a.size or defaults.size] or 256,
39-
mode = CIPHER_MODES[a.mode or defaults.mode] or "cbc",
40-
hash = hashes[a.hash or defaults.hash] or hashes.sha512,
41-
rounds = tonumber(a.rounds or defaults.rounds) or 1
38+
size = CIPHER_MODES[a.size or defaults.size] or 256,
39+
mode = CIPHER_MODES[a.mode or defaults.mode] or "cbc",
40+
hash = hashes[a.hash or defaults.hash] or hashes.sha512,
41+
rounds = tonumber(a.rounds or defaults.rounds) or 1
4242
}, cipher)
4343
end
4444

lib/resty/session/encoders/base64.lua

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
local base64enc = ngx.encode_base64
2-
local base64dec = ngx.decode_base64
1+
local ngx = ngx
2+
local base64enc = ngx.encode_base64
3+
local base64dec = ngx.decode_base64
34

45
local ENCODE_CHARS = {
56
["+"] = "-",

lib/resty/session/storage/memcache.lua

+10-9
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ local concat = table.concat
55
local floor = math.floor
66
local sleep = ngx.sleep
77
local now = ngx.now
8+
local var = ngx.var
89

910
local function enabled(val)
1011
if val == nil then return nil end
1112
return val == true or (val == "1" or val == "true" or val == "on")
1213
end
1314

1415
local defaults = {
15-
prefix = ngx.var.session_memcache_prefix or "sessions",
16-
socket = ngx.var.session_memcache_socket,
17-
host = ngx.var.session_memcache_host or "127.0.0.1",
18-
port = tonumber(ngx.var.session_memcache_port) or 11211,
19-
uselocking = enabled(ngx.var.session_memcache_uselocking or true),
20-
spinlockwait = tonumber(ngx.var.session_memcache_spinlockwait) or 10000,
21-
maxlockwait = tonumber(ngx.var.session_memcache_maxlockwait) or 30,
16+
prefix = var.session_memcache_prefix or "sessions",
17+
socket = var.session_memcache_socket,
18+
host = var.session_memcache_host or "127.0.0.1",
19+
port = tonumber(var.session_memcache_port) or 11211,
20+
uselocking = enabled(var.session_memcache_uselocking or true),
21+
spinlockwait = tonumber(var.session_memcache_spinlockwait) or 10000,
22+
maxlockwait = tonumber(var.session_memcache_maxlockwait) or 30,
2223
pool = {
23-
timeout = tonumber(ngx.var.session_memcache_pool_timeout),
24-
size = tonumber(ngx.var.session_memcache_pool_size)
24+
timeout = tonumber(var.session_memcache_pool_timeout),
25+
size = tonumber(var.session_memcache_pool_size)
2526
}
2627
}
2728

0 commit comments

Comments
 (0)