@@ -49,63 +49,47 @@ async function encryptedSession(fastify) {
4949 } ,
5050 } ) ;
5151
52- fastify . addHook ( 'onRequest' , ( request , _reply , next ) => {
53- const userEncryptionKey = getUserEncryptionKeyFromUserCookie ( request ) ;
54- if ( ! userEncryptionKey ) {
55- request . log . info ( { plugin : 'encrypted-session' } , 'user-side encryption key not found, creating new one' ) ;
56-
57- let newEncryptionKey = generateSecureEncryptionKey ( ) ;
58- setUserEncryptionKeyIntoUserCookie ( request , newEncryptionKey ) ;
59- request [ REQUEST_DECORATOR ] = createStore ( ) ;
60- newEncryptionKey = undefined ;
61- } else {
62- request . log . info ( { plugin : 'encrypted-session' } , 'user-side encryption key found, using existing one' ) ;
63-
64- const loadedEncryptionKey = Buffer . from ( userEncryptionKey , 'base64' ) ;
65-
66- const encryptedStore = request . session . get ( 'encryptedStore' ) ;
67- if ( encryptedStore ) {
68- try {
69- const { cipherText, iv, tag } = encryptedStore ;
70-
71- const decryptedCypherText = decryptSymetric ( cipherText , iv , tag , loadedEncryptionKey ) ;
72- const decryptedStore = JSON . parse ( decryptedCypherText ) ;
73- request [ REQUEST_DECORATOR ] = createStore ( decryptedStore ) ;
74- } catch ( error ) {
75- request . log . error ( { plugin : 'encrypted-session' } , 'Failed to parse encrypted session store' , error ) ;
76- request [ REQUEST_DECORATOR ] = createStore ( ) ;
77- }
78- } else {
79- // we could not parse the encrypted store, so we create a new one and it would overwrite the previously stored store.
80- request . log . info ( { plugin : 'encrypted-session' } , 'No encrypted store found, creating new empty store' ) ;
81- request [ REQUEST_DECORATOR ] = createStore ( ) ;
82- }
52+ await fastify . decorateRequest ( REQUEST_DECORATOR , {
53+ getter ( ) {
54+ return createStore ( this ) ;
8355 }
84-
85- next ( ) ;
8656 } ) ;
57+ }
8758
88- //TODO maybe move to onResponse after res is send. Lifecycle Doc https://fastify.dev/docs/latest/Reference/Lifecycle/
89- // onSend is called before the response is send. Here we take encrypt the Session object and store it in the fastify-session.
90- // Then we also want to make sure the unencrypted object is removed from memory
91- fastify . addHook ( 'onSend' , async ( request , _reply , payload ) => {
92- const encryptionKey = Buffer . from ( getUserEncryptionKeyFromUserCookie ( request ) , 'base64' ) ;
93- if ( ! encryptionKey ) {
94- // if no encryption key is found in the secure session, we cannot encrypt the store. This should not happen since an encrption key is generated when the request arrived
95- request . log . error (
96- { plugin : 'encrypted-session' } ,
97- 'No encryption key found in secure session, cannot encrypt store' ,
98- ) ;
99- throw new Error ( 'No encryption key found in secure session, cannot encrypt store' ) ;
100- }
59+ export default fp ( encryptedSession ) ;
10160
102- //we store everything in one value in the session, that might be problematic for future redis with expiration times per key. we might want to split this
103- const stringifiedData = request [ REQUEST_DECORATOR ] . stringify ( ) ;
104- const { cipherText, iv, tag } = encryptSymetric ( stringifiedData , encryptionKey ) ;
61+ function createStore ( request ) {
62+ let unencryptedStore = { } ; // Private variable
63+
64+ //read previous values
65+ let userEncryptionKey = getUserEncryptionKeyFromUserCookie ( request ) ;
66+ if ( ! userEncryptionKey ) {
67+ request . log . info ( { plugin : 'encrypted-session' } , 'user-side encryption key not found, creating new one' ) ;
10568
106- //remove unencrypted data from memory
107- delete request [ REQUEST_DECORATOR ] ;
108- request [ REQUEST_DECORATOR ] = null ;
69+ userEncryptionKey = generateSecureEncryptionKey ( ) ;
70+ setUserEncryptionKeyIntoUserCookie ( request , userEncryptionKey ) ;
71+ }
72+
73+ const loadedEncryptionKey = Buffer . from ( userEncryptionKey , 'base64' ) ;
74+ const encryptedStore = request . session . get ( 'encryptedStore' ) ;
75+ if ( encryptedStore ) {
76+ try {
77+ const { cipherText, iv, tag } = encryptedStore ;
78+
79+ const decryptedCypherText = decryptSymetric ( cipherText , iv , tag , loadedEncryptionKey ) ;
80+ const decryptedStore = JSON . parse ( decryptedCypherText ) ;
81+ unencryptedStore = decryptedStore ;
82+ } catch ( error ) {
83+ request . log . error ( { plugin : 'encrypted-session' } , 'Failed to parse encrypted session store' , error ) ;
84+ }
85+ } else {
86+ // we could not parse the encrypted store, so we create a new one and it would overwrite the previously stored store.
87+ request . log . info ( { plugin : 'encrypted-session' } , 'No encrypted store found, creating new empty store' ) ;
88+ }
89+
90+ async function save ( ) {
91+ const stringifiedData = JSON . stringify ( unencryptedStore ) ;
92+ const { cipherText, iv, tag } = encryptSymetric ( stringifiedData , loadedEncryptionKey ) ;
10993
11094 request . session . set ( 'encryptedStore' , {
11195 cipherText,
@@ -114,46 +98,38 @@ async function encryptedSession(fastify) {
11498 } ) ;
11599 await request . session . save ( ) ;
116100 request . log . info ( 'store encrypted and set into request.session.encryptedStore' ) ;
117-
118- return payload ;
119- } ) ;
120-
121- function getUserEncryptionKeyFromUserCookie ( request ) {
122- return request [ ENCRYPTED_COOKIE_REQUEST_DECORATOR ] . get ( ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY ) ;
123- }
124-
125- function setUserEncryptionKeyIntoUserCookie ( request , key ) {
126- request [ ENCRYPTED_COOKIE_REQUEST_DECORATOR ] . set ( ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY , key . toString ( 'base64' ) ) ;
127101 }
128- }
129-
130- export default fp ( encryptedSession ) ;
131102
132- // use a closure to encapsulate the session data so noone can reference it and we are the only ones keeping a reference
133- function createStore ( previousValue ) {
134- let unencryptedStore = { } ; // Private variable
135- if ( previousValue ) {
136- unencryptedStore = previousValue ;
137- }
138103 return {
139- set ( key , value ) {
104+ async set ( key , value ) {
140105 unencryptedStore [ key ] = value ;
106+ await save ( )
141107 } ,
142108 get ( key ) {
143109 return unencryptedStore [ key ] ;
144110 } ,
145- delete ( key ) {
111+ async delete ( key ) {
146112 delete unencryptedStore [ key ] ;
113+ await save ( )
147114 } ,
148- stringify ( ) {
149- return JSON . stringify ( unencryptedStore ) ;
115+ print ( ) {
116+ console . log ( "printing" , unencryptedStore )
150117 } ,
151- clear ( ) {
118+ async clear ( ) {
152119 unencryptedStore = { } ; // Clear all data
120+ await save ( )
153121 } ,
154122 } ;
155123}
156124
125+ function getUserEncryptionKeyFromUserCookie ( request ) {
126+ return request [ ENCRYPTED_COOKIE_REQUEST_DECORATOR ] . get ( ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY ) ;
127+ }
128+
129+ function setUserEncryptionKeyIntoUserCookie ( request , key ) {
130+ request [ ENCRYPTED_COOKIE_REQUEST_DECORATOR ] . set ( ENCRYPTED_COOKIE_KEY_ENCRYPTION_KEY , key . toString ( 'base64' ) ) ;
131+ }
132+
157133// generates a secure encryption key for aes-256-gcm.
158134// Returns a buffer of 32 bytes (256 bits).
159135function generateSecureEncryptionKey ( ) {
0 commit comments