Skip to content

Guide

A security checklist to work through before you launch

11 minute read · Published

Twenty-two questions, grouped into six areas. Each one has a yes or a no, and each one says what happens if the answer is no — because the point of a checklist is to let you decide what you are willing to launch without, not to make you feel behind.

Nothing here is specific to a framework, and none of it requires a security background. If you can read your own code and load your own site, you can answer all twenty-two. Work top to bottom: the order is roughly by how much a wrong answer costs you.

Secrets and credentials

  1. Is every API key and database URL read from the environment? Search your source for the prefixes your providers use — sk_, AKIA, ghp_, -----BEGIN. If a key is in the source, anybody with repository access has it, which usually includes more people than you think.
  2. Has your git history been checked, not just your working tree? git log --all -p -S "sk_live". Deleting a secret in a later commit does not remove it from the repository, and rewriting history does not un-disclose it. If you find one, rotate it before you do anything else.
  3. Are your production credentials different from the ones on your laptop? Sharing them means a mistake in development is a mistake in production, and it means you cannot revoke a laptop without taking the site down.
  4. Do you know how to rotate each one? Not in theory. Which page, which button, and what breaks in the minutes between the old key dying and the new one being deployed. The first time you do this should not be during an incident.

Authentication

  1. Are session cookies HttpOnly, Secure and scoped with SameSite? Load your site and read the response headers. Without HttpOnly, any script on the page can read the session; without Secure, it travels over plain HTTP if anything downgrades the connection.
  2. Does a password reset invalidate existing sessions? Somebody resetting a password usually believes their account was accessed. If the attacker’s session survives the reset, the reset did nothing.
  3. Is there a rate limit on sign-in, sign-up and password reset? Without one, an attacker can try a list of leaked passwords against your users as fast as your server will answer, and your reset endpoint becomes a way to send mail from your domain.
  4. Do failed sign-ins say the same thing whether or not the account exists? “No account with that email” turns your sign-in form into a way to check which of a million addresses are your customers.

Authorization

  1. Does every endpoint that reads a record by id check who is asking? This is the single most common serious flaw in an application built quickly. Go through each route that takes an id from the request and ask what stops somebody sending a different one. “The interface only shows them their own” is not an answer; the interface is not a control.
  2. Is the ownership check part of the query rather than a check after it? A lookup constrained to { id, ownerId: session.userId } cannot return somebody else’s row. A lookup followed by an if can be copied without its second half.
  3. Do write endpoints check the same thing as read endpoints? Update and delete are frequently overlooked because the corresponding read was fixed first and felt like the hard part.
  4. Are admin routes protected by a check on the server? Hiding the link, or checking a role in the browser, leaves the route open to anybody who types the URL.
  5. Can a user change fields they should not by sending extra properties? If you pass a request body into an update call, a request containing role or plan may set them. Validate to an explicit list of fields rather than filtering out the ones you thought of.

Payments

  1. Does your webhook handler verify the signature before reading the body? And reject on failure, rather than log and continue. An unverified handler lets anybody who knows the URL tell you an invoice was paid.
  2. Do charged amounts come from your own database? If the browser sends an amount and the server charges it, your prices are editable in developer tools. Send a product identifier; look the price up.
  3. Is a repeated webhook event harmless? Providers retry. A handler that adds a month of access every time it runs will eventually give away several. Record the event id and ignore one you have already processed.
  4. Is access derived from subscription state rather than from a past event? If a cancellation or a failed payment never reaches you, entitlements should lapse on their own rather than persist because nothing told them to stop.

Dependencies and data

  1. Have you run your package manager’s audit and read the output? Read it rather than count it: a build-time dependency with a high-severity advisory is usually less urgent than a request-handling one with a medium.
  2. Is there a decision recorded for anything you are not fixing? An accepted risk with a date is a decision. An accepted risk without one is something you forgot.
  3. Are database backups running, and have you restored one? A backup you have never restored is a belief about a backup. Restore into a scratch database once, before you need to.
  4. Do your logs stay free of passwords, tokens and card details? Logging a whole request body is the usual way these arrive. Check what you log on your error path in particular, since that is where people dump everything they have.

Deployment configuration

  1. Is production actually configured as production — no debug output, no permissive CORS, no stack traces, no development-only routes? Load the deployed site signed out and try a route you believe is internal. Search your configuration for "*" in anything CORS-related, and for every comparison against NODE_ENV that turns a check off.

What to do with the noes

Write them down with the consequence next to each, then decide. Some of these are worth delaying a launch for and some are not, and the difference is whether the damage can be undone by an apology. A disclosed payment key, a customer reading another customer’s data and a database you cannot restore are the three that cannot.

Everything else is a list you work through in the first weeks, and a list is a much better position than a vague sense that you probably should have checked something.

Doing this again next month

The hard part is not the first pass. It is the fourth feature, when the checklist is in a document nobody has opened and the answers you gave in week one are no longer true.

That repetition is what automation is actually for. Audixa runs the checks in the secrets, dependency, access-control and configuration sections against your repository, and — once you have proved you control the domain — against what your site serves. Every result comes with the code or the response header it matched, so you can tell whether it is real. See the full list of what it checks, or read the companion guide on why these mistakes recur.

What no tool can answer for you is question nine. Whether a given user should be able to see a given record is a fact about your product, not about your code, and it is the question worth spending your own hour on.

Have the repeatable checks run for you

One project and one scan a month are free. You see how many findings there are and how severe they are; titles and evidence unlock on a paid plan.

Analyze my projectBack to the guides

Start free. No automatic changes to your code.