Most break-ins on web apps are not clever. They use a missing permission check, an old package or a key that was pushed to a public repository. This checklist covers the things worth checking on every project, in plain terms, with short examples you can copy.
Work through it before launch, then again whenever the app changes in a big way.
Start from the OWASP Top 10:2025
The OWASP Top 10 is the most widely used list of web application risks. The current edition is the 2025 list. It replaces the 2021 edition, which many older articles still quote. The ten entries are:
- A01 Broken Access Control. Users can see or change things they should not.
- A02 Security Misconfiguration. Default settings, debug pages, open storage.
- A03 Software Supply Chain Failures. Risky or outdated packages, build tools and sources.
- A04 Cryptographic Failures. Weak or missing encryption of sensitive data.
- A05 Injection. SQL, command or script injection through user input.
- A06 Insecure Design. Flaws built into how a feature works.
- A07 Authentication Failures. Weak sign-in, session handling or recovery.
- A08 Software or Data Integrity Failures. Trusting code or data that nobody verified.
- A09 Security Logging and Alerting Failures. Attacks that nobody sees.
- A10 Mishandling of Exceptional Conditions. Errors that leak data or leave the app open.
The list is a starting point, not a full standard. For a deeper checklist, the OWASP ASVS sets out testable requirements, and the OWASP Cheat Sheet Series explains how to do each one.
Access control
- Check permissions on the server for every request. Hiding a button is not a check.
- Deny by default. A new route should be closed until you open it on purpose.
- Check ownership of each record. Changing
/invoices/41to/invoices/42must not show someone else's invoice. - Keep admin areas behind their own sign-in rules, and give staff only the access they need.
Input and output
- Use parameterised queries or your framework's query builder. Never build SQL by joining strings with user input.
- Encode output for where it lands: HTML, an attribute, JavaScript or a URL. Template engines do this by default, so be careful with the "raw" or "safe" switches.
- Validate on the server. Checks in the browser help users but stop no attacker.
- Protect forms that change data with a CSRF check. Most frameworks include one; leave it on.
Sign-in and sessions
- Offer passkeys or a second factor, at least for admin accounts.
- Store passwords with a slow, salted hash. The OWASP Password Storage Cheat Sheet recommends Argon2id, with bcrypt for older systems.
- Limit sign-in attempts per account and per address, and show the same message for a wrong user and a wrong password.
- Issue a new session ID after sign-in, and end sessions on sign-out and password change.
- Set session cookies with the right flags:
Set-Cookie: session=...; Path=/; Secure;
HttpOnly; SameSite=Lax
Headers and configuration
Serve everything over HTTPS, then tell browsers to insist on it with HSTS. A Content Security Policy (CSP) limits where scripts can load from, which takes most of the harm out of a script injection. This is a reasonable start for nginx:
add_header Strict-Transport-Security
"max-age=63072000; includeSubDomains" always;
add_header Content-Security-Policy
"default-src 'self'; object-src 'none';
base-uri 'self'; frame-ancestors 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy
"strict-origin-when-cross-origin" always;
Roll out a CSP with the Content-Security-Policy-Report-Only header first, so you can see what would break before you enforce it. MDN's CSP guide explains each directive. Only add HSTS preload once every subdomain works over HTTPS, because it is hard to undo.
Also check the boring parts: debug mode off, directory listing off, default accounts removed, admin tools not on the public internet, and storage buckets private.
Secrets
- Keep API keys and passwords out of the repository. Load them from environment variables or a secrets manager.
- Use different keys for development, staging and production.
- Scan your history with a tool such as gitleaks. If a key was ever committed, rotate it; deleting the file is not enough.
- Give the database user only the rights the app needs.
Dependencies and the supply chain
Commit your lockfile so every build uses the same versions. Run the audit command for your ecosystem in CI:
npm audit
composer audit
pip-audit
Turn on automated update pull requests (Dependabot or Renovate), remove packages you no longer use, and install only from the official registry. Treat build scripts and CI secrets as part of the app, because an attacker who changes the build changes the product.
Errors and logging
- Show users a plain error page. Keep stack traces in the logs.
- Fail closed. If a permission check throws an error, the answer is "no".
- Log sign-ins, failed sign-ins, permission denials and admin actions, with a time and an account.
- Never log passwords, full card numbers, session cookies or keys.
- Send an alert when the numbers jump, so someone looks the same day.
Myths worth dropping
- "We are too small to be a target." Most attacks are automated scans that try every address they find.
- "We have HTTPS, so we are secure." HTTPS protects data on the way. It does nothing for a missing permission check.
- "The firewall will catch it." A web application firewall or CDN filter helps, but it cannot fix logic flaws in your code.
- "Our framework handles security." It handles a lot, until someone turns a protection off or writes raw SQL.
- "We tested it once." Every release and every package update changes the app. Checks belong in the routine.
Common questions
- Is the 2021 OWASP list still useful?
- The ideas still apply, but the 2025 edition reorders the risks and adds Software Supply Chain Failures and Mishandling of Exceptional Conditions. Use the current list for new work.
- Do I need a CSP if my framework escapes output?
- Yes. Escaping stops most injections; a CSP limits the damage of the one you missed. The two work together.
- How often should I update packages?
- Apply security fixes as soon as you have tested them, and do routine updates on a fixed schedule so they stay small.
- Where do I start on an old project?
- Access control and secrets first. They cause the most serious incidents and are often quick to check.
Do you build web apps and want paid work? Getting paid on Internetivo explains how professionals find tasks and get paid for accepted work.