Skip to content

[BUG] [GSSoC'26] GET /api/auth/logout is CSRF-vulnerable: any third-party page can force-logout authenticated users via a passive request #556

@anshul23102

Description

@anshul23102

Bug Description

The logout endpoint in backend/routes/auth.js is registered as a GET request. HTTP GET is defined as a safe, idempotent method that must not trigger state changes. Using GET for logout violates this contract and makes the endpoint exploitable via Cross-Site Request Forgery (CSRF): any page on the web can silently log out an authenticated user without any user interaction.

Affected File

backend/routes/auth.js line 43

router.get("/logout", (req, res) => {
    req.logout((err) => {
        if (err) return res.status(500).json({ message: 'Logout failed', error: err.message });
        res.status(200).json({ message: 'Logged out successfully' });
    });
});

How the Attack Works

Because GET requests are triggered automatically by the browser in many contexts, an attacker can force a logout with zero user interaction using any of the following:

<!-- Embedded in any page the victim visits -->
<img src="https://github-spy.etlify.app/api/auth/logout" width="0" height="0">

Or via a <link rel="prefetch">, a CSS background URL, or a JavaScript fetch. The browser sends the request including the session cookie, the session is destroyed, and the user finds themselves unexpectedly signed out. The attacker gains no data, but for a service where authentication is required for all useful functionality this constitutes a targeted denial-of-service against specific users.

Additionally, since the session cookie currently lacks a SameSite attribute (tracked separately in issue #554), cross-site requests automatically carry the cookie, making this attack immediately practical without any additional bypass.

Steps to Reproduce

  1. Log in via POST /api/auth/login.
  2. From any origin (or a new tab via the browser address bar), visit: GET /api/auth/logout.
  3. Navigate back to the app and observe the session has been destroyed.

The address bar access demonstrates the attack vector directly, since a <img> or <link> tag would behave identically from the browser's perspective.

Expected Behavior

Logout must be a POST endpoint protected against CSRF. Because the existing session-based auth does not use a CSRF token, the minimum safe change is:

  1. Change the route to router.post("/logout", ...).
  2. Update the frontend to send POST /api/auth/logout (with credentials: 'include' so the session cookie is included).

With the SameSite: strict cookie fix also applied, POST-based logout with credentials provides adequate CSRF protection for a same-origin-only application.

Suggested Fix

// backend/routes/auth.js
router.post("/logout", (req, res) => {
    req.logout((err) => {
        if (err) return res.status(500).json({ message: 'Logout failed', error: err.message });
        req.session.destroy(() => {
            res.clearCookie('connect.sid');
            res.status(200).json({ message: 'Logged out successfully' });
        });
    });
});

Note: req.session.destroy() and res.clearCookie() are added here because req.logout() alone does not invalidate the server-side session data or remove the cookie from the browser. Without them, the old session ID remains valid on the server until it naturally expires.

Severity

High / Security - Any authenticated user can be logged out without consent by a third-party page. Combined with the missing SameSite cookie attribute, this is trivially exploitable today.


This issue is raised under GSSoC 2026 for open-source contribution.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions