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:
- Find which candidate matches the submitted password
- If no match → reject (invalid password)
- If match but unmarked → breach detected (attacker using a stolen credential)
- 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.
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
kpassword 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 ahoneyword_detectedsignal 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_BACKENDSto useHoneywordsBackend. - 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 ifModelBackendis enabled. - If you enable
django.contrib.auth.backends.ModelBackendalongsideHoneywordsBackend, then users without an AmnesiaSet can still authenticate using the default Django password backend. In production, prefer using onlyHoneywordsBackend, 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 checkto 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
kcandidates (linear scan). Choosekand password hasher parameters accordingly.
Components
Django Models
AmnesiaSet— links a user to their set ofkcandidates with marking parametersAmnesiaCredential— individual password hash withmarkedflag and indexHoneywordEvent— 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 useramnesia_initialize_from_settings(user, password)— same, using values fromHONEYWORDSsettingsamnesia_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_detectedsignal on breach
- Authenticates users via
policy.py
is_locked(user)— check if user is currently locked outapply_reset(user)— mark user as requiring password resetapply_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.pyconfiguration as-is. - Use
example_project/settings_prod.pyas a production settings template (setDJANGO_SECRET_KEYandDJANGO_ALLOWED_HOSTS). - Avoid enabling
django.contrib.auth.backends.ModelBackendin production unless you fully understand the bypass risk for users who were not initialized with anAmnesiaSet. - Do not use
MD5PasswordHasherin production (it is used only inexample_project/settings_test.pyto 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/configuration.mdfor the full settings reference. - See
docs/deployment.mdfor a production deployment checklist. - See
docs/integration.mdfor guidance on initializing users during signup/password-change flows.
Project Structure
django-amnesia-honeywords/
├── src/django_honeywords/
│ ├── admin.py # Django admin registrations
│ ├── apps.py # Django app config
│ ├── amnesia_service.py # Core amnesia service
│ ├── backend.py # Authentication backend
│ ├── checks.py # Deployment system checks
│ ├── 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
│ ├── migrations/ # Database migrations
│ └── management/commands/ # Management commands
├── docs/ # Configuration, deployment, integration guides
├── tests/ # Test suite
└── example_project/ # Example Django project for testing
Security Considerations
-
Honeyword Quality: The included
SimpleMutationGeneratorcreates basic variants. For stronger security, implement a custom generator that produces indistinguishable honeywords. -
Audit Logging: All authentication attempts are logged to
HoneywordEvent. Regularly review these logs and set up alerts for honeyword detections. -
Parameter Tuning: The
p_markandp_remarkparameters control the trade-off between false positive rate and detection sensitivity. See the Amnesia paper for guidance on choosing values. -
Password Reset Flow: When
ON_HONEYWORD = "reset", users flagged withmust_resetshould be redirected to a password change page. Integrate this with your application's auth flow.
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
Who do I talk to?
Having problems? Email me: davidiliopoulos@gmail.com
Citing this work
If you use this repository for academic research, you are highly encouraged (though not required) to cite our paper:
@inproceedings{iliopoulos2026honeywords,
title={Honeywords in the Wild: A First Empirical Study of Deployment Readiness and Adoption},
author={Iliopoulos, David and Smaragdakis, Georgios and Dionysiou, Antreas},
booktitle={Proceedings of the 11th International Workshop on Traffic Measurements for Cybersecurity (WTMC)},
year={2026}
}
Release files for django-amnesia-honeywords 0.0.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_amnesia_honeywords-0.0.5.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_amnesia_honeywords-0.0.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.2 kB
Release files / django_amnesia_honeywords-0.0.5.tar.gz
| Download URL | django_amnesia_honeywords-0.0.5.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
13b5dcbaedb40afc4e822253ec4012ce93bdf8e385916474a46408cdd7de69c6
|
|
BLAKE2b-256 checksum How to use checksums |
9f4c6eff01a889c9e920142dd92b840784561ba536e2f54d7828e607c9fc7b33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / django_amnesia_honeywords-0.0.5-py3-none-any.whl
| Download URL | django_amnesia_honeywords-0.0.5-py3-none-any.whl |
|---|---|
| Size | 22.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fb4df2ac9b2cab978a68e297adf7fcb429617ab63aec2503a93f1d6ef56f466c
|
|
BLAKE2b-256 checksum How to use checksums |
70929dbfd5e5c83f2ab55d26eac102c86492a6dc28d03fe5a17c9689a6dded3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency log