#1687·ungit

Ungit Uses a Hardcoded Express Session Secret When Authentication Is Enabled

Author: 28HusCreated Sep 7, 2026Updated Sep 7, 2026

Ungit Uses a Hardcoded Express Session Secret When Authentication Is Enabled

Package

Ungit

Affected versions

Versions containing the affected code when authentication is enabled. The affected code is present at commit b1df1a168b9208766342070ebd42782036dfd18d.

Patched versions

Unknown.

Description

Summary

Ungit configures express-session with the hardcoded secret ungit:

javascript
session({
  store: new MemoryStore({
    checkPeriod: 86400000,
  }),
  secret: 'ungit',
  resave: true,
  saveUninitialized: true,
})

Source: source/server.js.

When authentication is enabled, Ungit stores the authenticated username in a server-side Express session. Protected Git APIs call ensureAuthenticated and check req.isAuthenticated().

The default session store is MemoryStore. Therefore, the public secret does not by itself create an authenticated session for an arbitrary username. An attacker would also need a valid target Session ID, or a separate session fixation/session disclosure primitive. The current evidence supports a hardcoded session-integrity secret, but does not prove a direct authentication bypass from the secret alone.

Vulnerable configuration

Authentication is disabled by default and can be enabled through the Ungit configuration:

javascript
authentication: false,
users: {},

Source: source/config.js.

Authentication flow

The configured username/password map is used by Passport LocalStrategy:

javascript
passport.use(
  new LocalStrategy((username, password, done) => {
    if (users[username] !== undefined && password === users[username]) {
      done(null, username);
    } else {
      done(null, false, { message: 'No such username/password' });
    }
  })
);

Source: source/server.js.

Passport serializes only the username into the server-side session:

javascript
passport.serializeUser((username, done) => {
  done(null, username);
});

passport.deserializeUser((username, done) => {
  done(null, users[username] !== undefined ? username : null);
});

Source: source/server.js.

The protected API checks the server-side authentication state:

javascript
ensureAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).json({
    errorCode: 'authentication-required',
  });
};

Source: source/server.js.

For example, Git operations are protected by this middleware:

javascript
app.get(`${exports.pathPrefix}/status`, ensureAuthenticated, ensurePathExists, handler);
app.post(`${exports.pathPrefix}/clone`, ensureAuthenticated, ensurePathExists, handler);
app.post(`${exports.pathPrefix}/push`, ensureAuthenticated, ensurePathExists, handler);

Source: source/git-api.js and source/git-api.js.

Proof of concept

Run this only against a local Ungit instance with authentication: true and a test user.

First, create a correctly signed cookie for a random Session ID using the public value ungit:

javascript
const signature = require('cookie-signature');

const sessionId = 'attacker-chosen-session-id';
const signedCookie = `s:${sessionId}.${signature.sign(sessionId, 'ungit')}`;
console.log(`connect.sid=${encodeURIComponent(signedCookie)}`);

Send it to the local instance:

bash
curl -i \
  -H 'Cookie: connect.sid=s%3Aattacker-chosen-session-id.<signature>' \
  http://127.0.0.1:8448/api/loggedin

Expected result: {"loggedIn":false}. The server does not find the chosen Session ID in MemoryStore, creates a new unauthenticated session, and does not treat the attacker-chosen ID as a username.

As a control, log in with the configured test username and password, retain the returned connect.sid, and request /api/loggedin again. The result is {"loggedIn":true}. This demonstrates that the authentication state is stored server-side rather than in the signed cookie.

Impact

The hardcoded value weakens the integrity protection of Ungit's session cookies. If a valid Session ID is disclosed through logs, monitoring, a proxy, or another vulnerability, knowledge of ungit allows an attacker to recreate the cookie signature for that ID.

The hardcoded value alone does not prove arbitrary-user impersonation because the session data is held in MemoryStore and contains the authenticated username server-side. A direct authentication-bypass report would require an additional finding such as predictable or attacker-controlled Session IDs, session fixation, or Session ID disclosure.

Recommended fix

  1. Replace secret: 'ungit' with a required, deployment-specific, high-entropy secret.
  2. Fail closed when authentication is enabled and no session secret is configured.
  3. Use a production session store instead of the in-process MemoryStore for multi-process or exposed deployments.
  4. Regenerate the Session ID after successful login and invalidate sessions after password changes.
  5. Restrict exposed Ungit instances with network controls such as localhost binding or an authenticated reverse proxy.

References