Our own two sites, earth1.co and e1-4.com, are the training range. We test them, show where they are weak, fix them in the open, and teach how.
The Range, Live
Both sites, graded now from the headers they send. When we fix something, the grade here changes.
The live grades need JavaScript switched on.
Case Studies
Real code from our sites, shortened where it helps: what each defence stops, and what is still open.
earth1.co
Fixed in Code, Not Yet in Production
earth1.co's code sends a strict set of security headers on every page. Its live grade above still shows them missing, because the server it runs on has not picked up the last releases. A fix that has not shipped protects no one: the gap between the code and production is itself a weakness.
Prevention: deploy automatically from the main branch, check after every release that production serves what the code says, and alert when it does not.
earth1.co and e1-4.com
Telling Attackers What We Run
Both sites answered every request with "X-Powered-By: Next.js". It harms nothing by itself, but it tells an attacker which framework to look up flaws for. One line in each site's settings removes it.
// No "X-Powered-By: Next.js": it tells an attacker which framework, and so which flaws, to try.
poweredByHeader: false,
Prevention: send no version banners from any server, framework or proxy, and keep the software patched so a banner would not help anyway.
e1-4.com
A Script Allow-List, Still One Step Short
e1-4.com sends a Content-Security-Policy: scripts only from itself, nothing framed, no plugins, forms only to itself. That stops most cross-site scripting. It still allows 'unsafe-inline' scripts, because the framework writes small inline scripts into each page, so an injected inline script would also run.
The fix: give each response a random nonce, put it on the framework's own inline scripts, and replace 'unsafe-inline' with 'nonce-…'. Then only the scripts we wrote run. This is the next change on the range.
earth1.co
A Password Reset Link That Cannot Be Forged or Reused
earth1.co's reset link carries the email, an expiry an hour ahead and a signature. The signature is an HMAC over those and the account's current password hash, keyed with a secret only the server holds. Nobody can make a link without the secret, a link dies after an hour, and once the password changes the hash changes, so the same link never works twice.
const std::string data = base64url(normEmail(email)) + "." + std::to_string(now_() + kResetMs);
return data + "." + mac("reset." + data + "." + fieldText(*e, "pass", "undefined"));
// Checking it: a constant-time compare, so timing reveals nothing.
if (!timingSafeEqual(mac("reset." + data + "." + passHash), signature)) return std::nullopt;
if (expiry < now_()) return std::nullopt;
Prevention: sign anything a visitor carries back to you, put an expiry inside what you sign, compare signatures in constant time, and tie one-time links to state that changes when they are used.
Rules of the Range
We test only sites we own: earth1.co, e1-4.com and onecoalescent.app.
We publish a weakness in detail only once it is fixed, or describe a defence already in place.
Anything that could break a site or touch real people's data is tried on a copy, never on the live site.
Testing anyone else's systems without written permission is a crime in the UK under the Computer Misuse Act 1990, and in most countries. We never do it, and neither should you.