CVE-2024-34709 (published 2024-05-14T15:39Z)

Directus is a real-time API and App dashboard for managing SQL database content. Prior to 10.11.0, session tokens function like the other JWT tokens where they are not actually invalidated when logging out. The `directus_session` gets destroyed and the cookie gets deleted but if the cookie value is captured, it will still work for the entire expiry time which is set to 1 day by default. Making it effectively a long lived unrevokable stateless token instead of the stateful session token it was meant to be. This vulnerability is fixed in 10.11.0.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/directus/directus
commit_details: repo_url: https://github.com/directus/directus
author: Brainslug
author_date: 2024-05-02 14:53:41+02:00
msg: Improved session token validation (#22353)

Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
repository: repo_url: https://github.com/directus/directus
repo_name: directus/directus
description: The Modern Data Stack 🐰 — Directus is an instant REST+GraphQL API and intuitive no-code data collaboration app for any SQL database.
repo_language: TypeScript
owner: directus
file_changes: filename: late-lions-pump.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,5 @@
+---
+"@directus/api": patch
+---
+
+Improved session token validation

programming_language: Markdown
filename: get-accountability-for-token.ts
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,7 @@ import { InvalidCredentialsError } from '@directus/errors';
 import type { Accountability } from '@directus/types';
 import getDatabase from '../database/index.js';
 import isDirectusJWT from './is-directus-jwt.js';
+import { verifySessionJWT } from './verify-session-jwt.js';
 import { verifyAccessJWT } from './jwt.js';
 
 export async function getAccountabilityForToken(
@@ -24,6 +25,10 @@ export async function getAccountabilityForToken(
 		if (isDirectusJWT(token)) {
 			const payload = verifyAccessJWT(token, env['SECRET'] as string);
 
+			if ('session' in payload) {
+				await verifySessionJWT(payload);
+			}
+
 			accountability.role = payload.role;
 			accountability.admin = payload.admin_access === true || payload.admin_access == 1;
 			accountability.app = payload.app_access === true || payload.app_access == 1;

programming_language: TypeScript
filename: verify-session-jwt.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,26 @@
+import getDatabase from '../database/index.js';
+import { InvalidTokenError } from '@directus/errors';
+import type { DirectusTokenPayload } from '../types/index.js';
+
+/**
+ * Verifies the associated session is still available and valid.
+ *
+ * @throws If session not found.
+ */
+export async function verifySessionJWT(payload: DirectusTokenPayload) {
+	const database = getDatabase();
+
+	const session = await database
+		.select(1)
+		.from('directus_sessions')
+		.where({
+			token: payload['session'],
+			user: payload['id'],
+		})
+		.andWhere('expires', '>=', new Date())
+		.first();
+
+	if (!session) {
+		throw new InvalidTokenError();
+	}
+}

programming_language: TypeScript