Flask extension that masks 404 responses to reduce directory fuzzing signal quality.
Project description
Flask AntiFuzz
Flask AntiFuzz is a simple Flask extension that reduces the usefulness of directory fuzzing signals by masking 404 Not Found responses as normal-looking HTML responses with variable hidden padding.
This extension can be considered a protection against directory fuzzing aimed at identifying sensitive and insecure endpoints. It will also help mitigate certain types of credential stuffing attacks, when an attacker targets noticeable variations in the server response size and other scenarios where the size of the server response is important.
From the attacker's side (example):
Installation
pip install flask-antifuzz
Usage
from flask import Flask
from flask_antifuzz import Antifuzz
app = Flask(__name__)
antifuzz = Antifuzz(app)
The extension also supports the app factory pattern:
from flask import Flask
from flask_antifuzz import Antifuzz
antifuzz = Antifuzz()
def create_app():
app = Flask(__name__)
antifuzz.init_app(app)
return app
Demo Applications
The repository includes two small Flask applications for comparison:
| Path | Purpose |
|---|---|
app/ |
Protected demo application with Antifuzz(app) enabled. |
fuzzed/app/ |
Intentionally unprotected baseline application for directory-fuzzing comparisons. |
The fuzzed/app/ service is kept in git on purpose. It is not included in the
published wheel.
Configuration
Set values on app.config before initializing the extension.
| Key | Default | Description |
|---|---|---|
ANTIFUZZ_ENABLED |
True |
Enables or disables the 404 masking handler. |
ANTIFUZZ_STATUS_CODE |
200 |
Status code returned for missing paths while enabled. |
ANTIFUZZ_MIN_RESPONSE_BYTES |
None |
Manual minimum target response size. Must be set with ANTIFUZZ_MAX_RESPONSE_BYTES. |
ANTIFUZZ_MAX_RESPONSE_BYTES |
None |
Manual maximum target response size. Must be set with ANTIFUZZ_MIN_RESPONSE_BYTES. |
ANTIFUZZ_AUTO_SIZE_ENABLED |
True |
Automatically derives response size bounds from template/static HTML-like files. |
ANTIFUZZ_FALLBACK_MIN_RESPONSE_BYTES |
6144 |
Minimum target response size when auto sizing finds no files. |
ANTIFUZZ_FALLBACK_MAX_RESPONSE_BYTES |
163840 |
Maximum target response size when auto sizing finds no files. |
ANTIFUZZ_SIZE_MARGIN_RATIO |
0.25 |
Extra proportional size margin added around discovered file sizes. |
ANTIFUZZ_SIZE_MIN_MARGIN_BYTES |
512 |
Minimum byte margin added around discovered file sizes. |
ANTIFUZZ_SIZE_FILE_EXTENSIONS |
(".html", ".htm", ".jinja", ".jinja2", ".j2") |
File extensions used for auto sizing. |
ANTIFUZZ_PAD_ALL_HTML_RESPONSES |
True |
Adds the same hidden padding shape to normal HTML responses. |
ANTIFUZZ_MIN_PADDING_BYTES |
96 |
Minimum hidden padding added to each padded HTML response. |
ANTIFUZZ_RAND_404_TITLE |
False |
Randomizes the <title> value on masked 404 HTML responses. |
ANTIFUZZ_404_TITLE_MIN_LENGTH |
5 |
Minimum randomized 404 title length. |
ANTIFUZZ_404_TITLE_MAX_LENGTH |
10 |
Maximum randomized 404 title length. |
ANTIFUZZ_RENDERER |
None |
Optional callable for an explicit extension-managed 404 response. |
Response Sizing
You do not need to configure response sizes for a typical Flask app. Manual size
settings default to None, so AntiFuzz starts in auto-sizing mode.
By default, AntiFuzz scans the Flask application's configured template_folder
and static_folder during initialization. It looks for HTML/Jinja-like files,
measures their source file sizes, then adds a safety margin to build the random
response-size range used for masked 404 responses.
The configured size range is a final response-size target, not the amount of padding to add. AntiFuzz subtracts the current HTML body size before adding padding. If the response is already above the maximum, or if there is not enough remaining room for the hidden block, it leaves the body unchanged instead of growing the response past the configured maximum.
Manual sizing takes priority over auto sizing:
app.config["ANTIFUZZ_MIN_RESPONSE_BYTES"] = 3000
app.config["ANTIFUZZ_MAX_RESPONSE_BYTES"] = 9000
Antifuzz(app)
Use manual sizing for highly dynamic pages where rendered HTML is much larger than the source Jinja template.
The fallback range is based on HTTP Archive's 2025 Web Almanac HTML response size distribution: common HTML responses cluster around a 33-35 KB median, with the 90th percentile around 148-155 KB.
Padding Placement
AntiFuzz pads all non-streamed text/html responses by default, not only 404
responses. This keeps the defensive page from being identifiable by the mere
presence of hidden padding.
The padding is inserted immediately before </body>. If a response has no
closing body tag, the padding is appended to the end of the response. The block
is a plain hidden div with text-like random payload and does not include any
project-specific signatures.
404 Handling
AntiFuzz does not replace your application's 404 page. Flask or your own
@app.errorhandler(404) still creates the response body. AntiFuzz then changes
the response status from 404 to ANTIFUZZ_STATUS_CODE and applies the same
HTML padding layer used for normal pages.
If ANTIFUZZ_RAND_404_TITLE is enabled, AntiFuzz also replaces the 404 page's
<title> value with a random ASCII title. If the page has a <head> but no
<title>, AntiFuzz inserts one.
Custom Responses
Prefer Flask's normal error-handler API for application-owned 404 pages:
@app.errorhandler(404)
def not_found(error):
return render_template("error404.html"), 404
AntiFuzz will keep that body, change the status code, and apply padding. Use
ANTIFUZZ_RENDERER only when you explicitly want the extension to own 404
rendering:
from flask import render_template
def antifuzz_renderer(error):
return render_template("error404.html"), 200
app.config["ANTIFUZZ_RENDERER"] = antifuzz_renderer
Antifuzz(app)
Development and testing
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
python -m pip install -e ".[test]"
python -m pytest
Build and validate package artifacts:
python -m pip install build twine
python -m build
python -m twine check dist/*
Run the demo applications:
flask --app app/main.py run --port 5000
flask --app fuzzed/app/main.py run --port 5001
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flask_antifuzz-0.1.0.tar.gz.
File metadata
- Download URL: flask_antifuzz-0.1.0.tar.gz
- Upload date:
- Size: 16.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8548c630f34de552324c2d48713028b6def58523249cdf0c0ead36fd065e2449
|
|
| MD5 |
9f241116f71abe8c80b5044a2c10f716
|
|
| BLAKE2b-256 |
aaf5d20f2ed02c32fa4877c21f8fbc7addb3465564b36811c1f7590c30db538a
|
File details
Details for the file flask_antifuzz-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flask_antifuzz-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7955040bd4603743d89b8b9ece43a8ec1542db1737889b44531669bf709d4457
|
|
| MD5 |
c4d57500b466ebaf384ea954d8799078
|
|
| BLAKE2b-256 |
6b2af0189d0eb8e565cc64ba01c94e29b2fbcdee0bc7621cd58bcc7c4c5e04f7
|