Skip to content

Commit 854b84c

Browse files
authored
Handle signed cookies (#222)
1 parent 49252b0 commit 854b84c

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,17 @@ function fastifySecureSession (fastify, options, next) {
246246
// the hooks must be registered after @fastify/cookie hooks
247247

248248
fastify.addHook('onRequest', (request, reply, next) => {
249-
for (const [sessionName, { cookieName }] of sessionNames.entries()) {
250-
const cookie = request.cookies[cookieName]
249+
for (const [sessionName, { cookieName, cookieOptions }] of sessionNames.entries()) {
250+
let cookie = request.cookies[cookieName]
251+
252+
if (cookie !== undefined && cookieOptions.signed === true) {
253+
const unsignedCookie = fastify.unsignCookie(cookie)
254+
255+
if (unsignedCookie.valid === true) {
256+
cookie = unsignedCookie.value
257+
}
258+
}
259+
251260
const result = fastify.decodeSecureSession(cookie, request.log, sessionName)
252261

253262
request[sessionName] = result || new Proxy(new Session({}), sessionProxyHandler)

test/signed.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict'
2+
3+
const tap = require('tap')
4+
const fastify = require('fastify')({ logger: false })
5+
const sodium = require('sodium-native')
6+
7+
tap.test('signed session cookie should works if not tampered with', function (t) {
8+
const key = Buffer.alloc(sodium.crypto_secretbox_KEYBYTES)
9+
sodium.randombytes_buf(key)
10+
11+
fastify.register(require('../'), {
12+
key,
13+
cookieName: '__Host-session',
14+
cookie: {
15+
secure: true,
16+
httpOnly: true,
17+
path: '/',
18+
signed: true
19+
}
20+
})
21+
22+
fastify.post('/', (request, reply) => {
23+
request.session.set('userId', '123')
24+
reply.send('done')
25+
})
26+
27+
t.teardown(fastify.close.bind(fastify))
28+
t.plan(7)
29+
30+
fastify.get('/', (request, reply) => {
31+
const data = request.session.get('userId')
32+
if (!data) {
33+
reply.code(404).send()
34+
return
35+
}
36+
reply.send(data)
37+
})
38+
39+
fastify.inject(
40+
{
41+
method: 'POST',
42+
url: '/',
43+
payload: {}
44+
},
45+
(error, response) => {
46+
t.error(error)
47+
t.equal(response.statusCode, 200)
48+
t.ok(response.headers['set-cookie'])
49+
50+
const { name } = response.cookies[0]
51+
t.equal(name, '__Host-session')
52+
53+
const originalCookie = response.headers['set-cookie']
54+
55+
fastify.inject(
56+
{
57+
method: 'GET',
58+
url: '/',
59+
headers: {
60+
cookie: originalCookie
61+
}
62+
},
63+
(error, response) => {
64+
t.error(error)
65+
t.same(response.payload, '123')
66+
}
67+
)
68+
69+
const cookieContent = originalCookie.split(';')[0]
70+
71+
// Change the last 5 characters to AAAAA, to tamper with the cookie
72+
const cookieContentTampered = cookieContent.slice(0, -5) + 'AAAAA'
73+
74+
const tamperedCookie = originalCookie.replace(cookieContent, cookieContentTampered)
75+
76+
fastify.inject(
77+
{
78+
method: 'GET',
79+
url: '/',
80+
headers: {
81+
cookie: tamperedCookie
82+
}
83+
},
84+
(error, response) => {
85+
if (error) {
86+
t.fail('Unexpected error: ' + error.message)
87+
} else {
88+
t.equal(response.statusCode, 404, 'Should fail with tampered cookie')
89+
}
90+
}
91+
)
92+
}
93+
)
94+
})

0 commit comments

Comments
 (0)