Free tool · no signup
Node.js and Express security checklist with a score
Tick what is true for your production service and get a score out of 100 and a short list of the gaps to close first. Every item says why it matters.
By the Bug Circuit security team · Last updated
Tick what is true for your production service today. It is written for Express but most items apply to any Node.js server. Weights reflect how often each gap is behind a real compromise.
Runtime and dependencies
Input and requests
Authentication and access
Deployment
0 of 30 in place. The service is easy to compromise. Start with the Node.js version, dependencies and input handling today.
Fix these first
- Production runs a Node.js line that still gets security fixes, preferably an even-numbered LTS line
- npm audit (or another scanner) runs in CI and critical and high advisories are fixed or reviewed
- Every request body, query and parameter is checked against a schema (zod, joi, ajv or similar) before use
- Database queries use parameters or an ORM, and request objects are never passed straight into a query (NoSQL operator injection)
- User input never reaches child_process.exec, eval, new Function or vm
# Node.js and Express security checklist Score: 0% (High risk). 0 of 30 items in place. ## Runtime and dependencies - [ ] Production runs a Node.js line that still gets security fixes, preferably an even-numbered LTS line - [ ] npm audit (or another scanner) runs in CI and critical and high advisories are fixed or reviewed - [ ] Builds use npm ci from a committed lockfile - [ ] Install scripts are limited in CI (for example --ignore-scripts, or an allow-list) for packages that do not need them - [ ] Dependabot or Renovate opens update pull requests, and they are merged within a week or two - [ ] Unused packages are removed, and a new dependency gets a quick look before it is added - [ ] If you publish packages, the npm account has two-factor authentication and publish tokens are narrow and short-lived ## Input and requests - [ ] Every request body, query and parameter is checked against a schema (zod, joi, ajv or similar) before use - [ ] Database queries use parameters or an ORM, and request objects are never passed straight into a query (NoSQL operator injection) - [ ] User input never reaches child_process.exec, eval, new Function or vm - [ ] File paths built from input are resolved and checked to stay inside the intended folder - [ ] Outbound requests to user-supplied URLs go through an allow-list and block private addresses - [ ] User-supplied JSON is not merged into objects with unsafe merge helpers (prototype pollution) - [ ] Body size limits are set (express.json limit), with timeouts on slow requests - [ ] Regular expressions that run on user input are simple or time-limited ## Authentication and access - [ ] Each endpoint checks on the server that the signed-in user may touch that record - [ ] Passwords are hashed with bcrypt, scrypt or Argon2, never stored or logged in plain text - [ ] Session cookies are httpOnly, secure and sameSite, and the session secret comes from the environment - [ ] JWTs pin the allowed algorithm, expire quickly and are verified with the right key - [ ] Login, password reset and other costly endpoints are rate limited - [ ] Admin and staff accounts use two-factor authentication - [ ] CORS allows a named list of origins, and the Origin header is not simply echoed back ## Deployment - [ ] NODE_ENV is production, and error responses never include stack traces - [ ] Secrets live in the environment or a secrets manager, and no .env file or key is in the repository or the image - [ ] Security headers are set (helmet or equivalent), and x-powered-by is disabled - [ ] The process runs as a non-root user in a small, current base image - [ ] TLS ends at a reverse proxy or load balancer, and trust proxy is set correctly - [ ] Errors and sign-in events are logged somewhere you read, and logs hold no passwords or tokens - [ ] Data is backed up away from the server, and a restore has been tested - [ ] A person has tested the service by hand in the last 12 months ## Fix first 1. Production runs a Node.js line that still gets security fixes, preferably an even-numbered LTS line. End-of-life lines get no patches for new flaws in Node.js itself, and odd-numbered lines are supported only for months. 2. npm audit (or another scanner) runs in CI and critical and high advisories are fixed or reviewed. Most Node.js compromises come from a vulnerable or malicious dependency, not from your own code. 3. Every request body, query and parameter is checked against a schema (zod, joi, ajv or similar) before use. Unchecked input is the start of injection, type confusion and many crashes. 4. Database queries use parameters or an ORM, and request objects are never passed straight into a query (NoSQL operator injection). Passing req.body into a MongoDB filter lets an attacker send operators such as $ne to skip a login check. 5. User input never reaches child_process.exec, eval, new Function or vm. It gives an attacker a shell on the server. 6. Each endpoint checks on the server that the signed-in user may touch that record. Changing an id in the URL to reach another customer's data is the most common serious bug. 7. Passwords are hashed with bcrypt, scrypt or Argon2, never stored or logged in plain text. A stolen database should not hand over working passwords. 8. Secrets live in the environment or a secrets manager, and no .env file or key is in the repository or the image. A leaked repository or image should not expose database and API keys.
How it works
How the score works
Each item has a weight of 1, 2 or 3. The items with weight 3 are marked key. They are the gaps behind the largest share of real compromises: an unsupported Node.js version, known-vulnerable dependencies, unchecked input, user input reaching a shell, missing ownership checks, weak password storage, secrets in the repository and no tested backups. The score is the weight of what you ticked divided by the total weight, so ticking easy items does not hide a serious gap.
The bands are 90 and above (strong), 70 to 89 (decent), 40 to 69 (exposed) and below 40 (high risk). They show urgency, not a guarantee. A service can score well and still carry a flaw in its own logic, which is why the last item asks for a hand test.
Why these checks
Node.js services are mostly compromised through the dependency tree, through input that is trusted too early and through secrets that were left where others could read them. A typical service installs hundreds of packages, so a single bad release can reach production through a routine update. The list is built around those routes, and each item says how it goes wrong.
Several items can be checked by machine. The Node.js version checker shows whether your line is supported and which security releases you have missed. The npm audit tool reads a lockfile and lists known advisories and malicious packages. The JWT decoder helps with the token items.
Using the result
Start with the five items under Fix these first. Tick an item only when it is true today and you have checked it, not when you plan to do it. Copy the Markdown report to keep a record, and run the checklist again after a major upgrade or a new host. Your ticks are saved in this browser only, and nothing is sent anywhere.
Worked examples
A typical neglected service
Production runs an LTS line, passwords are hashed and NODE_ENV is production. Nothing else.
12% High risk (3 of 30 in place) Fix first: npm audit in CI, request validation, safe database queries
The basics are in place, but dependencies and input are unchecked.
A tidier service
The basics, plus audit in CI, npm ci, schema validation, safe queries, no user input in exec, ownership checks, secure cookies, rate limits, secrets in the environment, helmet, update pull requests, logs, install scripts limited and tested backups.
65% Exposed (17 of 30 in place) Fix first: remove unused packages, guard file paths, block requests to private addresses
Strong hygiene. What is left is the long tail, which attackers still use.
What the report looks like
Copy the Markdown result.
# Node.js and Express security checklist Score: 12% (High risk). 3 of 30 items in place. - [x] Production runs a supported Node.js line - [ ] npm audit runs in CI...
Useful for a developer, a client or an insurer.
Frequently asked questions
Is Express secure by default?
What is the most common Node.js security problem?
Does this work for Fastify, Koa or Next.js?
How often should I run npm audit?
Do I need helmet?
What if my score is low?
Can a checklist find a hacked service?
Keep reading
See what attackers see, for free
Run the free passive check on your website. No login, no impact on your site, results in seconds.
Ready for the full manual audit? See pricing