Hardening a deployment

Go through a deployment once, in order, and close the configuration gaps that no code review finds.

15of 16
≈ 11Min read
4Parts

The checklist

None of this is clever and all of it is forgettable, which is why it belongs on a list that somebody runs before a launch and once a quarter after it.

AreaWhat good looks like
TLShttps everywhere, http redirects, HSTS with a long max-age and subdomains
HeadersCSP without unsafe-inline, nosniff, a referrer policy, frame-ancestors none
CookiesHttpOnly, Secure, SameSite, a sensible Max-Age, scoped to a path
CORSan allowlist, never a reflected origin, no wildcard with credentials
Errorsa generic message to the user, the detail in the log, never a stack trace
Adminnot on the public origin, behind a second factor, logged
Defaultsno default credentials, no sample app, no directory listing, no .git served

The leak that is just a default

Turn the framework's production mode on — it is what disables the detailed error page — remove the version headers, and check that the deployment does not serve dotfiles. These take minutes and they remove the reconnaissance step entirely.

what an attacker checks in the first ten seconds
/.git/config                 # the whole source history, if the directory is served
/.env                        # the whole configuration
/server-status, /actuator    # internals, sometimes with credentials
X-Powered-By: Express        # the version to look up
/api/docs                    # every endpoint, helpfully
a stack trace on a 500       # file paths, library versions, sometimes SQL

Errors, and what the user is told

The request id is the whole trick: the user can quote it to support, and support can find the detail without the user ever seeing it. It also stops the temptation to put the real error in the response "just for now".

two audiences, two messages
// to the user
{ "error": "Something went wrong", "requestId": "req_8f3d21" }

// to the log
{ event: "unhandled", requestId: "req_8f3d21", stack, query, userId }

And the things that are not headers

  • Backups — encrypted, restorable, and actually restored once. An untested backup is a hope.
  • Access — who can reach production, with what, and what happens when they leave.
  • Dependencies — the weekly upgrade from that lesson, running.
  • A way to be told. A security.txt file, a monitored address, and an answer within a day. Most vulnerabilities are reported by someone being helpful; make it easy.

Practice