Episode 8 · 7 min

Block attackers in seconds, outside your code

Stop a signup flood and a scraper without a deploy: scoped blocks by address or range, route, method, app, environment and time, added from the dashboard, the CLI, or your coding agent over MCP.

What you'll learn

  • ✓How SecureNow enforces blocks inside your app while the rules live outside your code
  • ✓Scope a block to one address or a CIDR range, one route and method, one environment, for a set time
  • ✓Let your coding agent investigate an attack read-only, then apply the blocks you approve
  • ✓Verify a block with test-ip and see what it actually stopped with firewall hits

Block attackers in seconds, outside your code

Just before three in the afternoon, two attacks hit Northstar's production at the same time: a script creating fake accounts from 241 addresses (more than two a second), and one address pulling the full notes export four times a second. Both were stopped about ten seconds after we said yes, without touching the code. Everything below is from that run, on the companion branch tutorial/ep8-9-firewall.

Why blocking in code is slow

A deny list in a middleware means every change is a branch, a review, a build and a deploy, and it only covers the routes someone remembered to wrap. The person who can see the attack (security, whoever is on call) usually can't change the repository at all.

How SecureNow blocks

The rules live in SecureNow. The SDK you installed in Episode 2 enforces them inside your app, before your code runs:

  1. the block list,
  2. blocks scoped to a route,
  3. challenges and rate limits (Episode 9).

Rules are held in memory, so a request never waits on the network. Every 10 seconds the SDK checks whether anything changed and pulls the new version when it has. A blocked request gets a 403 and never reaches your route:

HTTP/1.1 403 Forbidden
{"error":"Forbidden","ip":"203.0.113.77"}

If SecureNow can't be reached, your app keeps serving traffic (failMode: "open", the default).

How narrow a block can be

DimensionOptions
Whoone address, or a CIDR range (203.0.113.0/24)
Wherethe whole app, or one route matched exact, by prefix, or by regex
Methodall, or one (POST refused while GET still works)
App · environmentone app or all; production only, or every environment
How long15 minutes, 1 hour, 24 hours, 7 or 30 days, or until removed

Routes are compared after normalization (percent-decoded, lower-cased, doubled slashes collapsed), so /API/AUTH/REGISTER can't slip past a block on /api/auth/register.

Step 1 — Let the agent investigate (read-only)

claude mcp add securenow -- npx securenow mcp

The prompt:

Production for the lab-auth app is getting hammered right now. Using SecureNow, find out what is hitting it in
the last 10 minutes and who is behind it. Then propose exactly how to stop it without changing any code: which
blocks to add, scoped as narrowly as possible (IP or range, route, method, environment) and for how long.
Don't apply anything yet.

In about four minutes (traces, firewall hits, forensic queries, IP traces) it reported:

  • Signups: 241 IPs, 203.0.113.10 to .250. Each IP came back only once per ~92-second lap, so the app's own per-IP signup limit never fired. 466 fake accounts so far.
  • Export: 198.51.100.77 calling GET /api/notes/export about every 265 ms.

Its proposal: block 203.0.113.0/24 only for POST /api/auth/register, and 198.51.100.77 only for GET /api/notes/export, both in production for 24 hours. It also suggested two rate limits in case the attacker switches addresses (that's Episode 9).

Step 2 — Approve, and the agent applies it

Approved: apply rules 1 and 2 (the two blocks) now. Then verify them: test the four addresses you listed,
wait a minute, and show me the firewall hits for the last 5 minutes.

Two calls to securenow_blocklist_add:

{ "ip": "203.0.113.0/24", "method": "POST", "pathPattern": "/api/auth/register",
  "pathMatchMode": "exact", "environment": "production", "duration": "24h" }

The first block was created at 15:01:31 UTC. At 15:01:41, on the SDK's next check, both routes started answering 403. From then on every one of the 241 addresses was refused, and four ordinary users browsing the whole time made 725 requests without a single error.

Step 3 — Verify

The agent's four securenow_firewall_test_ip checks:

AddressRequestResult
203.0.113.50POST /api/auth/registerblocked, matched 203.0.113.0/24
203.0.113.50GET /api/auth/csrfallowed
198.51.100.77GET /api/notes/exportblocked
198.51.100.21GET /api/notes/exportallowed

From the CLI:

securenow firewall test-ip 203.0.113.9 --path /API/AUTH/REGISTER --method POST
# BLOCKED — matched by 203.0.113.0/24 · rule POST exact:/api/auth/register
securenow firewall hits --since 1h

Firewall hits (also securenow_firewall_hits over MCP, and the dashboard) show what each block stopped: by the end of the run, 546 signup attempts from 241 addresses and 562 exports from one.

Step 4 — Or do it from the dashboard

Blocked IPs → Add IP: the address or range, a reason, Block for, and under Advanced the app, environment, route, match mode and method. The block list then shows each entry with the requests it stopped.

Step 5 — Undo

Timed blocks expire on their own. Any block can be removed, and its history and block report are kept:

securenow blocklist unblock <id> --yes

More than your own list

  • SecureNow maintains a list of addresses with a record of abuse, synced into every app by default.
  • Addresses you mark as trusted (securenow trusted add <ip> --label office) are left out of the block list your apps receive.
  • The same list can be enforced lower down: config.firewall.tcp closes the connection before any HTTP is read, and config.firewall.iptables uses iptables on Linux. Both are off by default.

What you just did

You stopped two attacks in about ten seconds, scoped to exactly the routes they hit, without a code change or a deploy, and kept every legitimate user working. Scoped, time-limited, recorded, reversible — and outside your code.

Try it on your own app

Start the threat model, then plug SecureNow in — the whole series works on your project today.