NAME
Plack::Middleware::ProofOfWork - Proof-of-Work based bot protection for Plack applications
SYNOPSIS
use Plack::Builder;
builder {
enable "ProofOfWork",
difficulty => 4,
cookie_name => 'pow',
cookie_duration => 5;
$app;
};
DESCRIPTION
Plack::Middleware::ProofOfWork implements a Proof-of-Work mechanism to protect against automated requests (bots, scrapers, etc.). Legitimate browsers must solve a computationally intensive task before accessing the application.
The middleware uses SHA-256 hashing and requires clients to find a nonce that results in a hash with a specified number of leading zeros.
CONFIGURATION
- difficulty
-
The number of leading zeros in the hash (default: 4). Now supports fractional values for finer granularity (e.g. 4.5). Each additional zero increases difficulty by a factor of 16.
difficulty => 4 # ~65,000 attempts on average difficulty => 4.5 # ~185,000 attempts on average difficulty => 5 # ~1,000,000 attempts on averageFractional difficulty enables finer gradations between the exponential steps of integer difficulties.
-
Name of the cookie for the Proof-of-Work token (default: 'pow').
-
Cookie validity duration in days (default: 5).
- bot_patterns
-
Hash-ref of bot types with DNS verification patterns (default: Googlebot, Applebot, Bingbot).
bot_patterns => { 'googlebot' => qr/crawl.*google\.com$/, 'mybot' => qr/mybot.*example\.com$/, }The hash keys are used for User-Agent matching (case-insensitive). The regex values are used for reverse DNS hostname verification.
- bot_verification_level
-
Level of bot verification (default: 2):
0 = No bots allowed (all bots blocked, regardless of verification) 1 = User-Agent only (simple string matching) 2 = Reverse DNS (hostname must match pattern) - DEFAULT 3 = Full DNS roundtrip (reverse + forward DNS must match) bot_verification_level => 3Set to 0 to block all bots (including search engines). Level 2 (default) provides good security with reasonable performance. Level 3 provides the most security but may slow down first requests from bots due to additional DNS lookups.
- bot_dns_timeout
-
Timeout in seconds for DNS lookups during bot verification (default: 0.5).
bot_dns_timeout => 1.0Forward DNS lookup uses 2x this timeout.
- timestamp_window
-
Time window in seconds for timestamp rounding (default: cookie_duration * 86400).
- js_file
-
Path to the JavaScript file for Proof-of-Work calculation. If not specified, the bundled default file is used.
js_file => '/path/to/my/pow.js'The JavaScript file is automatically loaded from the installation (via File::ShareDir). In v0.05 there is no inline fallback - the file must exist.
- html_file
-
Path to the HTML template file for the challenge page. If not specified, the bundled default file is used.
html_file => '/path/to/my/challenge.html'The HTML template must contain the placeholder
<!-- POW_JAVASCRIPT -->where the JavaScript API and pow.js content will be inserted.This allows complete customization of the challenge page UI.
- css
-
Custom CSS to inject into the challenge page template. The CSS is inserted at the
<!-- POW_CSS -->placeholder in the HTML template's style section.css => '.spinner { border-color: red; }'This allows easy styling customization without replacing the entire HTML template.
LOGIC FLOW
The middleware checks Proof-of-Work in the following order:
Check if a PoW cookie exists
If cookie exists: Validate it - Valid → Allow request through - Invalid → Require new PoW (even for bots)
If no cookie: Check if request is from a known bot - Is bot and allow_bots=1 → Allow through - Not a bot or allow_bots=0 → Require PoW
This ensures that even bots with invalid cookies must solve the PoW again.
JAVASCRIPT API
The pow.js script receives the following constants and functions from the middleware:
Provided Constants
const DIFFICULTY // Number: difficulty (e.g. 4 or 4.5)
const POW_COOKIE_NAME // String: cookie name
const COOKIE_DURATION // Number: cookie validity in days
Provided Functions
function getSourceValue() // Returns: String with the source value
// Format: "UserAgent|Timestamp|Language|Host"
This API is inserted as a prefix before the actual pow.js script.
Required Functions in pow.js
The pow.js script MUST implement the following functions:
async function sha256(message) // SHA-256 hash calculation
function hasLeadingZeros(hash, full, div) // Validation with fractional difficulty
function setCookie(name, value, days) // Set cookie
async function computeProofOfWork() // Main PoW function
See share/pow.js for the reference implementation.
HOW IT WORKS
A client without a valid PoW cookie receives an HTML page with JavaScript.
The JavaScript calculates a Proof-of-Work based on User-Agent, language, host, and timestamp.
After successful calculation, a cookie is set and the page reloads.
The middleware validates the cookie and passes the request through.
SECURITY CONSIDERATIONS
Difficulty should be high enough to slow down automated requests, but low enough not to frustrate legitimate users.
The middleware uses User-Agent, Accept-Language, and Host as part of the challenge to prevent token reuse across different contexts.
Timestamps are rounded to the cookie window to ensure stable challenges.
AUTHOR
Your Name <your@email.com>
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.