Secure Defaults & Hardening
The default behaviour is what you get under pressure, in a hurry, and late at night. So the default must be the safe one. A system is hardened not by the controls you can switch on, but by the ones that are already on, locked, and hard to turn off.
Most breaches do not use a clever zero-day. They walk through a setting someone left at its open default — open CORS, TLS validation disabled "for testing", a detailed error page in production, an admin endpoint with no auth. Hardening means shipping with every setting at its safest position, and requiring a deliberate, reviewed change to loosen it.
The Finperiti sample showed how this goes wrong: wide-open CORS, RequireHttpsMetadata=false outside dev, and a whole controller left unauthenticated. None of these were attacks. They were defaults nobody closed. Close them by design.
Ship locked down
- AlwaysMake authentication the default for every route. Require an explicit, reviewed annotation to make something public, never the other way round.
- DoRestrict CORS to an explicit allow-list of known origins. Wildcards, reflected origins, and
AllowAnyOriginwith credentials are misconfigurations, not conveniences. - DoEnforce HTTPS/TLS everywhere, with HSTS, secure cookies, and modern cipher suites. Redirect or reject plaintext.
- DoReturn generic error responses to external callers by default. Detailed diagnostics go to logs, never over the wire.
- DoSet security headers as a baseline (HSTS, X-Content-Type-Options, a restrictive CSP where it applies, no framing).
- DoApply rate-limiting and request-size limits by default, especially on auth, KYC, payment, and webhook endpoints, to reduce brute-force, credential-stuffing, and abuse.
- Do notLeave debug endpoints, Swagger write operations, seed data, or test backdoors reachable in production.
- NeverDisable TLS/certificate validation, or run
RequireHttpsMetadata=false, against anything outside local development.
Opening a default safely
- DoTreat every loosened default as a change: code-reviewed, explained in the PR, scoped as narrowly as possible, and reversible.
- DoScope relaxations to the exact environment that needs them. A dev-only convenience must be impossible to reach in production by design, not by a runtime
if. - ConsiderChecking hardening in tests. A test that fails if an endpoint becomes anonymous, or if CORS widens, turns a silent regression into a failed build.
- Do notCopy an open setting from a sample, tutorial, or local config into a deployed environment without re-checking it.
- NeverMake a route anonymous, widen CORS to all origins, or weaken a security default to "unblock" something, unless that change is reviewed and owned.
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
With credentials, this lets any website on the internet make authenticated calls on a user's behalf. Pin to an explicit origin list.
policy.WithOrigins(allowed).AllowCredentials()
.WithHeaders("Authorization", "Content-Type");
Only known front-ends can call the API, and only with the headers they actually need.
Self-review checklist
- AskIf I deployed this exactly as written, which setting is more open than it needs to be?
- AskIs any "for testing" relaxation reachable in production?
- AskWould a new endpoint added next week be secure by default, or open by default?
- AskDoes anything sensitive leak in an error response or a debug page?