From ea417b766e38513950ea2057e4af75aa865af57e Mon Sep 17 00:00:00 2001 From: tdruez Date: Mon, 5 Jan 2026 16:04:14 +0400 Subject: [PATCH 1/3] Add documentation about "Replay Attack Protection" Signed-off-by: tdruez --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 60b2874..d5b005d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ requiring additional configuration. > You can generate a new secured HMAC key using: > ``python -c "import secrets; print(secrets.token_hex(64))"`` - ## Usage ### Adding the CAPTCHA Field to Your Form @@ -124,12 +123,78 @@ class MyForm(forms.Form): > registering the view. > For example: ``AltchaChallengeView.as_view(max_number=2000000)`` +### Replay Attack Protection + +Django Altcha **automatically protects against replay attacks** by ensuring each +challenge can only be used once. +When a challenge is successfully validated, it is stored in a +cache and any subsequent attempt to reuse the same challenge will be rejected. + +This protection is enabled by default and requires no additional configuration for +single-process deployments. + +> [!IMPORTANT] +> The default in-memory cache is **not shared across workers**. If you run multiple +> workers (e.g., with gunicorn or uwsgi), you must configure a shared cache backend +> using the `ALTCHA_CACHE_ALIAS` setting. +> See the [Settings](#altcha_cache_alias) section for details. + ## Settings ### ALTCHA_HMAC_KEY **Required.** This key is used to HMAC-sign ALTCHA challenges and **must be kept secret**. +### ALTCHA_CACHE_ALIAS + +Cache alias used for replay attack protection. + +By default, challenges are stored in a local in-memory cache to prevent reuse. +This works well for single-process deployments, but **does not protect against +replay attacks in multi-worker setups** (e.g., gunicorn or uwsgi with multiple workers). + +For production deployments with multiple workers, configure a shared cache backend: + +**Using Redis or Memcached:** + +```python +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.redis.RedisCache', + 'LOCATION': 'redis://127.0.0.1:6379', + } +} +ALTCHA_CACHE_ALIAS = 'default' +``` + +**Using Database Caching:** + +If you prefer not to set up Redis or Memcached, +[Django's built-in database cache](https://docs.djangoproject.com/en/dev/topics/cache/#database-caching) +is a simple alternative: + +```python +CACHES = { + 'altcha': { + 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', + 'LOCATION': 'altcha_cache', + } +} +ALTCHA_CACHE_ALIAS = 'altcha' +``` + +Then create the cache table: + +```bash +python manage.py createcachetable +``` + +### ALTCHA_CHALLENGE_EXPIRE + +Challenge expiration duration in milliseconds. +Defaults to 20 minutes as per +[Altcha security recommendations](https://altcha.org/docs/v2/security-recommendations/). + ### ALTCHA_JS_URL URL of the Altcha JavaScript file. @@ -150,12 +215,7 @@ Only loaded when `ALTCHA_INCLUDE_TRANSLATIONS` is `True`. ### ALTCHA_VERIFICATION_ENABLED Set to `False` to skip Altcha validation altogether. - -### ALTCHA_CHALLENGE_EXPIRE - -Challenge expiration duration in milliseconds. -Default to 20 minutes as per Altcha security recommendations. -See https://altcha.org/docs/v2/security-recommendations/ +Defaults to `True`. ## Contributing From 1d50730209196a6a9238445fb529b946bea19a35 Mon Sep 17 00:00:00 2001 From: tdruez Date: Mon, 5 Jan 2026 16:23:07 +0400 Subject: [PATCH 2/3] Simplify documentation using the README as single source Signed-off-by: tdruez --- README.md | 4 +- docs/source/conf.py | 3 +- docs/source/{index.rst => index.md} | 18 ++--- docs/source/installation.rst | 111 ---------------------------- pyproject.toml | 1 + 5 files changed, 11 insertions(+), 126 deletions(-) rename docs/source/{index.rst => index.md} (59%) delete mode 100644 docs/source/installation.rst diff --git a/README.md b/README.md index d5b005d..ba198a6 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,6 @@ single-process deployments. > The default in-memory cache is **not shared across workers**. If you run multiple > workers (e.g., with gunicorn or uwsgi), you must configure a shared cache backend > using the `ALTCHA_CACHE_ALIAS` setting. -> See the [Settings](#altcha_cache_alias) section for details. ## Settings @@ -225,4 +224,5 @@ Feel free to submit issues or pull requests! ## License This project is licensed under the **MIT License**. -See the [LICENSE](./LICENSE) file for details. +See the [LICENSE](https://github.com/aboutcode-org/django-altcha/blob/main/LICENSE) +file for details. diff --git a/docs/source/conf.py b/docs/source/conf.py index 360287f..f603de2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,6 +34,7 @@ "sphinx_rtd_dark_mode", "sphinx.ext.extlinks", "sphinx_copybutton", + "myst_parser", ] @@ -75,7 +76,7 @@ html_context = { "display_github": True, - "github_user": "nexB", + "github_user": "aboutcode-org", "github_repo": "django-altcha", "github_version": "develop", # branch "conf_py_path": "/docs/source/", # path in the checkout to the docs root diff --git a/docs/source/index.rst b/docs/source/index.md similarity index 59% rename from docs/source/index.rst rename to docs/source/index.md index 3ea86c2..acd2290 100644 --- a/docs/source/index.rst +++ b/docs/source/index.md @@ -1,5 +1,4 @@ -Welcome to django-altcha's documentation! -========================================= +# Welcome to django-altcha's documentation! Django Altcha is a Django library that provides easy integration of Altcha CAPTCHA into your Django forms, enhancing user verification with configurable options. @@ -8,15 +7,10 @@ By default, CAPTCHA validation operates in a **fully self-hosted mode**, **eliminating the need for external services** while ensuring privacy and control over the verification process. -.. toctree:: - :maxdepth: 2 - :caption: Contents: +```{include} ../../README.md``` - installation +## Indices and tables -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +- {ref}`genindex` +- {ref}`modindex` +- {ref}`search` diff --git a/docs/source/installation.rst b/docs/source/installation.rst deleted file mode 100644 index ecefa3e..0000000 --- a/docs/source/installation.rst +++ /dev/null @@ -1,111 +0,0 @@ -Installation -============ - -1. **Install the package:** - -.. code-block:: bash - - pip install django-altcha - -2. **Add to INSTALLED_APPS:** - -Update your Django project's ``settings.py``: - -.. code-block:: python - - INSTALLED_APPS = [ - # Other installed apps - "django_altcha", - ] - -3. **Set your secret HMAC key:** - -This key is used to HMAC-sign ALTCHA challenges and **must be kept secret**. - -**Treat it like a password**: use a secure, 64-character hex string. - -Update your Django project's ``settings.py``: - -.. code-block:: python - - ALTCHA_HMAC_KEY="your_secret_hmac_key" - -.. note:: - You can generate a new secured HMAC key using: - ``python -c "import secrets; print(secrets.token_hex(64))"`` - -Usage -===== - -Adding the CAPTCHA Field to Your Form -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To add a Altcha CAPTCHA field to a Django form, import ``AltchaField`` and add it to -your form definition: - -.. code-block:: python - - from django import forms - from django_altcha import AltchaField - - class MyForm(forms.Form): - captcha = AltchaField() - -Configuration Options ---------------------- - -You can pass configuration options to ``AltchaField`` that are supported by Altcha. -These options are documented at -`Altcha's website integration guide `_. - -Example with additional options: - -.. code-block:: python - - class MyForm(forms.Form): - captcha = AltchaField( - floating=True, # Enables floating behavior - debug=True, # Enables debug mode (for development) - # Additional options supported by Altcha - ) - -Settings -======== - -ALTCHA_HMAC_KEY -~~~~~~~~~~~~~~~ - -**Required.** -This key is used to HMAC-sign ALTCHA challenges and **must be kept secret**. - -ALTCHA_JS_URL -~~~~~~~~~~~~~ - -URL of the Altcha JavaScript file. -Defaults to the bundled django-altcha file. - -ALTCHA_INCLUDE_TRANSLATIONS -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Whether to include `Altcha translations `_. -Defaults to ``False``. - -ALTCHA_JS_TRANSLATIONS_URL -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -URL of the Altcha translations JavaScript file. -Defaults to the bundled django-altcha file. - -Only loaded when ``ALTCHA_INCLUDE_TRANSLATIONS`` is ``True``. - -ALTCHA_VERIFICATION_ENABLED -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Set to ``False`` to skip Altcha validation altogether. - -ALTCHA_CHALLENGE_EXPIRE -~~~~~~~~~~~~~~~~~~~~~~~ - -Challenge expiration duration in milliseconds. -Default to 20 minutes as per Altcha security recommendations. -See https://altcha.org/docs/v2/security-recommendations/ diff --git a/pyproject.toml b/pyproject.toml index 0ce2725..faaae20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ docs = [ "sphinx-autobuild", "sphinx-rtd-dark-mode>=1.3.0", "sphinx-copybutton", + "myst-parser", ] [project.urls] From c89d3d12f7bc73e2c51d1cc0cdb6ae2c4954526e Mon Sep 17 00:00:00 2001 From: tdruez Date: Mon, 5 Jan 2026 16:25:41 +0400 Subject: [PATCH 3/3] Fix the include syntax Signed-off-by: tdruez --- docs/source/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/index.md b/docs/source/index.md index acd2290..b1bf228 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -7,7 +7,8 @@ By default, CAPTCHA validation operates in a **fully self-hosted mode**, **eliminating the need for external services** while ensuring privacy and control over the verification process. -```{include} ../../README.md``` +```{include} ../../README.md +``` ## Indices and tables