Skip to content

HyperDX Uses a Public Default Express Session Secret #3089

Closed

Description

@28Hus

HyperDX Uses a Public Default Express Session Secret

Summary

HyperDX uses the public value hyperdx is cool 👋 when
EXPRESS_SESSION_SECRET is not configured:

const DEFAULT_EXPRESS_SESSION = 'hyperdx is cool 👋';

export const EXPRESS_SESSION_SECRET = (env.EXPRESS_SESSION_SECRET ||
  DEFAULT_EXPRESS_SESSION) as string;

Source: config.ts

The value is used as the signing secret for the express-session cookie:

const sess = {
  // ...
  secret: config.EXPRESS_SESSION_SECRET,
  store: new MongoStore({ mongoUrl: config.MONGO_URI }),
};

app.use(session(sess));

Source: api-app.ts

Anyone who knows the public source can generate valid signatures for arbitrary
connect.sid values. This weakens the integrity protection of the session
cookie.

Authentication flow

The Session Cookie carries a Session ID. HyperDX stores the Passport user
identifier in the corresponding server-side session, and Passport later loads
the user from MongoDB:

passport.serializeUser(function (user, done) {
  done(null, (user as any)._id);
});

passport.deserializeUser(function (id, done) {
  findUserById(id).then(user => {
    if (user == null) return done(null, false);
    done(null, user);
  });
});

Source: passport.ts

Protected routes rely on req.isAuthenticated():

if (req.isAuthenticated()) {
  return next();
}
res.sendStatus(401);

Source: auth.ts

Proof of concept

Run this only against a local HyperDX test instance. The following creates a
correctly signed cookie for an attacker-chosen session ID:

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

const sessionId = 'attacker-chosen-session-id';
const signed = `s:${sessionId}.${signature.sign(
  sessionId,
  'hyperdx is cool 👋',
)}`;

console.log(encodeURIComponent(signed));

Send the result to a protected API route:

curl -i \
  -H 'Cookie: connect.sid=<url-encoded-signed-cookie>' \
  http://127.0.0.1:8000/me

Expected result: 401 Unauthorized. The cookie signature is valid, but the
chosen session ID has no corresponding authenticated session in MongoDB.

As a positive control, log in to the local test instance, retain the returned
connect.sid cookie, and request /me. The request succeeds because Passport
can recover the user from the server-side session.

Impact

The default hyperdx is cool 👋 secret allows an attacker to sign arbitrary
session-cookie values without knowing the deployment secret.

The secret alone does not prove arbitrary user impersonation in the current
flow. A successful impersonation would additionally require a session-state
weakness, such as disclosure or fixation of a valid session ID, or the ability
to create or modify the referenced MongoDB session.

The issue is directly exploitable in deployments that omit
EXPRESS_SESSION_SECRET and also expose one of those session-state
conditions. Otherwise, the demonstrated impact is the use of a public default
for a production authentication secret.

Remediation

  • Remove hyperdx is cool 👋 as the fallback in authenticated deployments.
  • Require a deployment-specific high-entropy EXPRESS_SESSION_SECRET.
  • Fail closed at startup when the secret is missing or equals the public
    placeholder.
  • Rotate the secret and invalidate existing sessions after applying the fix.

Activity

  1. added
    bugSomething isn't working
    on Sep 7, 2026
  2. brandon-pereira commented on Sep 7, 2026

    @brandon-pereira
    Member

    Hey there, having the default is handy because it allows people to experiment with the product very easily (just run one command to spin up the product).

    However, I agree with the security risk, I propose we add/make it more clear that the env variable is using the default value when the container starts up so that users can see they are using a less secure environment.

    If we need to, we can move this to a required value in the future.

    We are open to a contribution for this feature if you are interested/open :)

  3. 28Hus commented on Sep 8, 2026

    @28Hus
    ContributorAuthor

    Hey there, having the default is handy because it allows people to experiment with the product very easily (just run one command to spin up the product).

    However, I agree with the security risk, I propose we add/make it more clear that the env variable is using the default value when the container starts up so that users can see they are using a less secure environment.

    If we need to, we can move this to a required value in the future.

    We are open to a contribution for this feature if you are interested/open :)

    @brandon-pereira Thank you for your patient reply! From the perspective of product usability, this design is indeed effective. To ensure usability while enhancing security, we can first remove all fallback values ​​and then set the key to empty by default during deployment. When the backend detects that the current deployment key value is empty, it will generate a strongly random key unless the user actively configures it. If you agree with this design, I'd be happy to submit a proposal!

  4. brandon-pereira commented on Sep 10, 2026

    @brandon-pereira
    Member

    @28Hus that makes sense - we should also be very clear when this happens as it can cause auth issues if someone restarts their server. Additionally, we need to make it clear in the changeset that the env variable is now strongly recommended (and some details of the symptoms people may see when upgrading if not set)

    We would appreciate a PR for this change if you have the capacity - thanks for raising the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingexternal

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions