Skip to main content

Amnesia honeywords implementation for Django — breach detection without a separate honeychecker.

Project description

django-amnesia-honeywords

A Django authentication backend implementing the Amnesia honeywords scheme for breach detection — without requiring a separate honeychecker service.

When attackers crack a credential database and attempt online login with a stolen credential, the backend detects whether the submitted password is a marked credential or an unmarked decoy (honeyword).

Overview

For each user, instead of storing a single password hash, the system stores k hashed candidates (1 real + k-1 honeywords). Each candidate carries a marked boolean flag. The real password is always marked; other candidates are marked with probability p_mark.

On login:

  1. Find which candidate matches the submitted password
  2. If no match → reject (invalid password)
  3. If match but unmarked → breach detected (attacker using a stolen credential)
  4. If match and marked → success; with probability p_remark, re-mark other candidates

The key advantage: no separate Honeychecker service is needed. The system never stores which index is the real password — security comes from the probabilistic marking scheme.

Help this research

This package is part of an MSc thesis at TU Delft investigating honeyword adoption in practice. If you tried (or chose not to) deploy this package, please take 2–3 minutes to fill in the anonymous survey — your experience directly shapes the research findings.

→ Take the anonymous survey (2–3 min)

All responses are anonymous. No personal data is collected.

Features

  • Drop-in Django Authentication Backend — seamlessly integrates with Django's auth system
  • Honeyword Generation — simple mutation-based generator (extensible for custom generators)
  • Event Logging — tracks all authentication attempts with IP and User-Agent
  • Django Signals — hook into honeyword detection events
  • Configurable Policies — choose between logging, password reset, or account lockout on detection
  • No Honeychecker Required — Amnesia scheme eliminates the need for a separate service

Architecture

┌─────────────────────────────────┐
│          Django App             │
│                                 │
│  ┌───────────────────────────┐  │
│  │  HoneywordsBackend        │  │
│  │  (authentication)         │  │
│  └───────────┬───────────────┘  │
│              │                  │
│  ┌───────────▼───────────────┐  │
│  │  amnesia_check()          │  │
│  │  - find matching cred     │  │
│  │  - check marked flag      │  │
│  │  - probabilistic remark   │  │
│  └───────────┬───────────────┘  │
│              │                  │
│  ┌───────────▼───────────────┐  │
│  │  AmnesiaSet               │  │
│  │  └─ k AmnesiaCredentials  │  │
│  │     (hash + marked flag)  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

Installation

pip install django-amnesia-honeywords

What this package does (and does not do)

What it does

  • Provides a Django authentication backend (django_honeywords.backend.HoneywordsBackend) that checks passwords against an Amnesia honeyword set.
  • Stores k password candidates per user (hashed) with probabilistic marking; on login it can detect use of an unmarked candidate as a breach signal.
  • Logs outcomes (real/honey/invalid) and emits a honeyword_detected signal for alerting/automation.

What it does not do automatically

  • Installing the package does not change your project's authentication by itself. You must explicitly set AUTHENTICATION_BACKENDS to use HoneywordsBackend.
  • It cannot initialize existing users from an already-hashed password. You need the user's plaintext password (signup / password change / controlled migration).

Important integration notes

  • When you initialize honeywords for a user, this package sets the user's Django password to unusable (set_unusable_password()) to reduce bypass risk if ModelBackend is enabled.
  • If you enable django.contrib.auth.backends.ModelBackend alongside HoneywordsBackend, then users without an AmnesiaSet can still authenticate using the default Django password backend. In production, prefer using only HoneywordsBackend, or ensure all users are initialized.

Or install from source:

git clone https://github.com/iliopdavid/django-amnesia-honeywords.git
cd django-amnesia-honeywords
pip install -e .

Quick Start

1. Add to Django Settings

INSTALLED_APPS = [
    # ...
    "django_honeywords.apps.DjangoHoneywordsConfig",
]

AUTHENTICATION_BACKENDS = [
    "django_honeywords.backend.HoneywordsBackend",
]

# Honeywords configuration (all optional — sensible defaults provided)
HONEYWORDS = {
    "AMNESIA_K": 20,            # Number of candidates per user
    "AMNESIA_P_MARK": 0.1,      # Probability of marking a honeyword
    "AMNESIA_P_REMARK": 0.01,   # Probability of re-marking on success
    "ON_HONEYWORD": "log",      # "log" | "reset" | "lock"
    "LOG_REAL_SUCCESS": False,   # Log successful *marked* credential logins (real or marked honeyword)
    "LOCK_BASE_SECONDS": 60,    # Base lockout duration
    "LOCK_MAX_SECONDS": 3600,   # Maximum lockout duration
}

2. Run Migrations

python manage.py migrate django_honeywords

3. Initialize Users (Important)

This package cannot derive honeywords from an existing hash: you must initialize users while you have their plaintext password (signup, password change, migration script).

  • Management command (for migration / admin scripts):
python manage.py amnesia_init_user <username> --password <password>
  • Programmatic initialization:
from django_honeywords.amnesia_service import amnesia_initialize_from_settings

amnesia_initialize_from_settings(user, "real_password")

4. (Recommended) Remove ModelBackend in production

For most deployments, configure only the honeywords backend:

AUTHENTICATION_BACKENDS = [
    "django_honeywords.backend.HoneywordsBackend",
]

If you keep ModelBackend enabled, make sure you understand the bypass implications for users who are not initialized.

Configuration Options

Setting Default Description
AMNESIA_K 20 Number of candidate passwords per user (1 real + k-1 honeywords)
AMNESIA_P_MARK 0.1 Probability of marking each honeyword during initialization
AMNESIA_P_REMARK 0.01 Probability of re-marking other candidates on successful login
ON_HONEYWORD "log" Action on honeyword detection: "log", "reset", or "lock"
LOG_REAL_SUCCESS False Whether to log successful authentications with marked credentials
LOCK_BASE_SECONDS 60 Base duration for account lockout
LOCK_MAX_SECONDS 3600 Maximum lockout duration (exponential backoff capped here)

Operational Notes

  • System checks: run python manage.py check to surface configuration warnings (e.g., wildcard hosts, test hashers, backend fallbacks).
  • Event semantics: logging a “real” outcome corresponds to a marked credential login (real password or marked honeyword). Amnesia intentionally cannot distinguish those.
  • Performance: authentication checks up to k candidates (linear scan). Choose k and password hasher parameters accordingly.

Components

Django Models

  • AmnesiaSet — links a user to their set of k candidates with marking parameters
  • AmnesiaCredential — individual password hash with marked flag and index
  • HoneywordEvent — audit log of authentication attempts (outcome: real/honey/invalid)
  • HoneywordUserState — tracks lockout and password-reset state per user

Services

amnesia_service.py

  • amnesia_initialize(user, password, k, p_mark, p_remark) — generate and store candidates for a user
  • amnesia_initialize_from_settings(user, password) — same, using values from HONEYWORDS settings
  • amnesia_check(user, password) — returns "success", "breach", or "invalid"

generator.py

  • SimpleMutationGenerator — basic character mutation generator for honeywords
    • Creates variants by randomly mutating single characters
    • Extensible: implement your own generator with a honeywords(real, k) method

backend.py

  • HoneywordsBackend — Django authentication backend
    • Authenticates users via amnesia_check()
    • Enforces policy (log/reset/lock) on honeyword detection
    • Fires honeyword_detected signal on breach

policy.py

  • is_locked(user) — check if user is currently locked out
  • apply_reset(user) — mark user as requiring password reset
  • apply_lock(user) — apply exponential backoff lockout

events.py

  • log_event(user, username, outcome, request) — record authentication attempt with metadata

Signals

Connect to the honeyword_detected signal to implement custom alerting:

from django_honeywords.signals import honeyword_detected

def on_honeyword(sender, user, username, request, event, **kwargs):
    send_security_alert(
        message=f"Honeyword detected for user {username}",
        ip=event.ip_address,
        user_agent=event.user_agent,
    )

honeyword_detected.connect(on_honeyword)

Management Commands

amnesia_init_user

Initialize honeywords for an existing user:

python manage.py amnesia_init_user alice --password "SecurePass123"

Arguments:

  • username — username of the user
  • --password — the user's real password (required)

Parameters k, p_mark, and p_remark are read from the HONEYWORDS settings.

Development

Running Tests

# Install dev dependencies
pip install -e ".[dev]"

# Run all tests
pytest

# Run specific test groups
pytest tests/test_amnesia_a1_models.py    # Model creation
pytest tests/test_amnesia_a2_core.py      # Core amnesia logic
pytest tests/test_amnesia_a3_backend.py   # Authentication backend
pytest tests/test_amnesia_a4_command.py   # Management command

Deployment Notes

  • Do not deploy the example_project/settings.py configuration as-is.
  • Use example_project/settings_prod.py as a production settings template (set DJANGO_SECRET_KEY and DJANGO_ALLOWED_HOSTS).
  • Avoid enabling django.contrib.auth.backends.ModelBackend in production unless you fully understand the bypass risk for users who were not initialized with an AmnesiaSet.
  • Do not use MD5PasswordHasher in production (it is used only in example_project/settings_test.py to keep tests fast).

Recommended production policy:

  • Prefer ON_HONEYWORD = "lock" (or "log") unless you have a complete password-reset UX wired up.
  • If you set ON_HONEYWORD = "reset", your application must provide password reset/change views and messaging so users can recover.

Documentation

  • See docs/deployment.md for a production deployment checklist.
  • See docs/integration.md for guidance on initializing users during signup/password-change flows.
  • See docs/releasing.md for GitHub + PyPI publishing steps.

Project Structure

django-amnesia-honeywords/
├── src/django_honeywords/
│   ├── apps.py              # Django app config
│   ├── amnesia_service.py   # Core amnesia service
│   ├── backend.py           # Authentication backend
│   ├── conf.py              # Settings with defaults
│   ├── events.py            # Event logging
│   ├── generator.py         # Honeyword generation
│   ├── models.py            # Database models
│   ├── policy.py            # Reset/lock policies
│   ├── signals.py           # Django signals
│   └── management/commands/ # Management commands
├── tests/                   # Test suite
└── example_project/         # Example Django project for testing

Security Considerations

  1. Honeyword Quality: The included SimpleMutationGenerator creates basic variants. For stronger security, implement a custom generator that produces indistinguishable honeywords.

  2. Audit Logging: All authentication attempts are logged to HoneywordEvent. Regularly review these logs and set up alerts for honeyword detections.

  3. Parameter Tuning: The p_mark and p_remark parameters control the trade-off between false positive rate and detection sensitivity. See the Amnesia paper for guidance on choosing values.

  4. Password Reset Flow: When ON_HONEYWORD = "reset", users flagged with must_reset should be redirected to a password change page. Integrate this with your application's auth flow.

For Researchers

This package is a research artifact developed as part of an MSc thesis at Delft University of Technology:

Honeywords in Practice: Investigating Adoption and Barriers to Deployment David Iliopoulos, TU Delft, 2026

Research goals

The package serves two purposes:

  1. Demonstrate the deployment feasibility of the Amnesia honeyword scheme in a real-world Django context.
  2. Empirically study adoption friction — what makes developers adopt or avoid honeyword systems in practice.

Participate

If you installed, evaluated, or chose not to deploy this package, your experience is valuable data. Please fill in the anonymous survey:

→ Anonymous adoption survey (2–3 min)

Citation

If you use this package or reference it in academic work:

@software{iliopoulos2026honeywords,
  author  = {Iliopoulos, David},
  title   = {django-amnesia-honeywords},
  year    = {2026},
  url     = {https://github.com/iliopdavid/django-amnesia-honeywords}
}

References

  • Juels, A., & Rivest, R. L. (2013). Honeywords: Making password-cracking detectable. ACM CCS 2013.
  • Wang, K. C., & Reiter, M. K. (2021). Using amnesia to detect credential database breaches. In 30th USENIX Security Symposium (USENIX Security 21) (pp. 839-855).

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_amnesia_honeywords-0.0.3.tar.gz (24.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_amnesia_honeywords-0.0.3-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file django_amnesia_honeywords-0.0.3.tar.gz.

File metadata

File hashes

Hashes for django_amnesia_honeywords-0.0.3.tar.gz
Algorithm Hash digest
SHA256 b0390ce8d74919e75d81c16990a7da0252f6888755d59520a2de79c157600b5a
MD5 2680de0739d93d3ea393ffd7901f051a
BLAKE2b-256 3536e219d7a9b66655dfcf08e32bead340cebb5812a72f030354b57cb4b3ba61

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_amnesia_honeywords-0.0.3.tar.gz:

Publisher: publish.yml on iliopdavid/django-amnesia-honeywords

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_amnesia_honeywords-0.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for django_amnesia_honeywords-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3848b7f030cd60cdb00d63e56dbde6a20428fce1d0a5840a0482c5cb46386e41
MD5 89a0354a650b404416058f68de8c8f96
BLAKE2b-256 7c390caa655a2e556e32cffcaadfd9ae229c90e6d94366a0455a3e22e57af5ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_amnesia_honeywords-0.0.3-py3-none-any.whl:

Publisher: publish.yml on iliopdavid/django-amnesia-honeywords

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page