Skip to content

Hard-Coded Django SECRET_KEY Fallback Reaches Production #2502

Open

Description

@28Hus

Hard-Coded Django SECRET_KEY Fallback Reaches Production

Conclusion

backend/config/settings/base.py accepts a public hard-coded fallback for SECRET_KEY. production.py imports the complete base configuration, and the Docker image sets another public placeholder. A deployment that does not override the environment therefore runs with a known Django secret.

The current code does expose Django’s password-reset endpoints, but knowing SECRET_KEY alone is not enough to create a valid reset token for an arbitrary victim. Django binds the token to the target user’s password hash, last_login, email, primary key, and timestamp. I found no anonymous path in Doccano that discloses the required password hash or equivalent server-side state.

Therefore, this is a confirmed hard-coded-secret/security-configuration defect (CWE-321)

Affected code and default values

The fallback in base.py is:

SECRET_KEY = env("SECRET_KEY", "v8sk33sy82!uw3ty=!jjv5vp7=s2phrzw(m(hrn^f7e_#1h2al")
Image

base.py#L26-L32

The production settings import it without replacing or validating it:

from .base import *

production.py#L1-L3

The Docker image also embeds a public production placeholder:

ENV SECRET_KEY="change-me-in-production"

Dockerfile#L67-L72

The production Compose service selects config.settings.production but does not define SECRET_KEY, so the image-level placeholder is inherited:

docker-compose.prod.yml#L10-L19

The project documentation itself tells operators to replace the fixed default, but the application does not fail closed when they do not:

developer_guide.md#L37-L43

Impact

The hard-coded key compromises the secrecy of any Django signing feature that relies on it. The impact becomes an authentication issue if Doccano or a deployment uses the key for a signed-cookie session backend, another secret-only authentication artifact, or a reset-token implementation that does not bind tokens to server-side user state.

Remediation

  • Remove both hard-coded values and require SECRET_KEY to be supplied by the deployment environment or a secret manager.
  • Fail startup when the variable is missing, too short, or equal to a known placeholder.
  • Rotate the exposed values in every deployment. Invalidate sessions and reset tokens issued under the old key; do not retain the public key indefinitely as a fallback.
  • Add a deployment test that verifies production cannot start with the repository or Docker defaults.
  • Keep password-reset tokens bound to user state and verify that any future signed authentication artifact uses an independent, high-entropy secret.

Historical disclosure and duplicate check

The repository’s public Security page reports that no SECURITY.md policy is configured, and the public Security Advisories page reports no published repository advisories.

I reviewed public Issues, Pull Requests, exact-secret searches, and searches for SECRET_KEY, password-reset tokens, and authentication bypasses. I found no exact prior report of this root cause. The following are related but not duplicates:

  • Issue #748 discusses making default admin credentials configurable and warns that guessed defaults are unsafe; it does not report the Django SECRET_KEY path.
  • Issue #1158 contains a Docker build log showing SECRET_KEY="change-me-in-production"; it is a deployment/build issue, not a security report for password-reset forgery.
  • CVE-2024-40441 concerns a distinct Doccano privilege-escalation issue involving model_attribs and is not a duplicate.

This public-history check cannot exclude an undisclosed private report.

Related works

These cases are relevant references for hard-coded secrets and secret-backed authentication failures:

This report is part of my ongoing security research. If you have any questions or would like clarification, please feel free to @mention me at any time. I would be genuinely pleased to contribute, even in a small way, to improving Doccano’s security.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions