Gate: a form behind a proof-of-work challenge

The example application for Punk::Plugin::Challenge. Generated with punk new Gate and then given two rules.

export GATE_CHALLENGE_KEY=$(punk challenge key)
export GATE_SESSION_KEY=$(punk challenge key)
plackup -s Hyperman app.psgi   # -s, lowercase: -S is a socket
prove -l t/

The two secrets come from the environment; config/punk.yml references them and never holds them. The challenge secret is never generated for you, because a pool of workers would each mint their own and refuse each other's clearances.

Then:

| URL | | |---|---| | http://localhost:5000/ | the page that explains, free | | http://localhost:5000/login | the form, behind an always rule | | http://localhost:5000/api/time | JSON, behind an after rule |

app.psgi and t/01-basic.t add the distribution's blib to @INC so the demo runs before Punk::Challenge is installed. Drop those lines once it is.

What to look at

The form is behind always. Open /login in a browser and the first thing you see is "One moment". The page carries the puzzle in a data attribute and loads /challenge/challenge.js, which solves it in a Worker, posts the solution, and reloads with a clearance cookie that holds for an hour. Open it on a phone too: the difficulty is paid by the slowest phone of your slowest legitimate user, and the number in lib/Gate.pm is only right if that phone clears it in a moment.

The API is behind after. /api/time is free for thirty requests a minute per /24, and a puzzle past that instead of a 429. The counter lives in Hyperman's shared arena; under any other server there is none, the rule is inert, and the application says so once, at the first request under the rule. Run it with plackup -s Hyperman and hit it thirty-one times.

From a shell. A program gets JSON, not a page:

curl -si http://localhost:5000/login | grep X-Challenge
punk challenge solve <the puzzle>
curl -si -H 'X-Challenge-Response: <the solution>' http://localhost:5000/login

csrf is on, and verify is not checked by it. The sign-in form carries a token; the challenge's own verify route is a POST without a session that changes nothing but its own cookie, so the plugin exempts it and nothing in config/punk.yml says so.

No proxy line. The demo is served directly. Behind nginx, an ELB or a CDN, proxy; is the first line after use Punk, or every visitor on the internet is one subject and one solve clears them all.