The short answer
Most web applications are broken into through boring mistakes: a page that forgets to check who is asking, a dependency nobody updated, a password reset that can be abused, or a database credential sitting in a repository. Get accounts, access control, input handling, data protection and updates right and you have closed the paths that account for most real incidents.
Use the checklist below before launch and again whenever you add a feature that touches accounts, payments or personal data. It is written for the person paying for the app as much as for the developer, because several items are decisions rather than code.
Accounts, passwords and sessions
Start here, because account takeover is the most common way an application is abused. Passwords must be stored using a modern password hashing algorithm, never encrypted and never in plain text, and the application should never be able to show a user their own password.
- Support long passwords and check new ones against known breached password lists rather than forcing odd character rules.
- Offer two-factor authentication, and require it for administrators.
- Expire sessions after a sensible period of inactivity, and log the user out everywhere when they change their password.
- Rate limit login, password reset and any endpoint that sends email or messages.
- Make password reset links single use and short lived, and never reveal whether an email address exists.
- Log administrative actions with who did what and when.
Authorization: the one most often missed
Check permissions on the server for every single request, not in the interface. The OWASP Top 10, the widely used list of web application security risks, puts broken access control at the top, and the usual form of it is simple: the app hides a button from a user but still answers the request if they send it directly.
- Every endpoint checks both who the user is and whether this specific record belongs to them.
- Record identifiers in URLs are treated as user input. Changing a number in the address bar must not reveal someone else's data.
- Roles are defined in one place, and default to the least access that works.
- Administrative functions are on separate routes with their own checks.
- File downloads go through a permission check, not a public folder with hard-to-guess names.
- Test it deliberately: log in as a low-privilege user and try to reach another account's records.
Input, output and injection
Treat every value that arrives from a browser, an API or a file as hostile until it is validated. The defenses are well understood:
| Risk | What it looks like | Defense |
|---|---|---|
| SQL injection | Database queries built by joining strings | Parameterized queries or a query builder, always |
| Cross-site scripting | User content rendered as markup | Escape on output, and a content security policy |
| Cross-site request forgery | A form on another site posting to yours | Anti-forgery tokens and same-site cookies |
| Unsafe file upload | Any file accepted and stored in a public path | Validate type and size, store outside the web root, serve through a handler |
| Server-side request forgery | The app fetches a URL a user supplied | Allowlist destinations, block internal addresses |
Protecting the data
Collect the minimum, encrypt it in transit and at rest, and know where it all is. Keep the whole application on HTTPS with modern settings, including the API, and redirect anything on the insecure version. Our explainer on HTTP and HTTPS covers why that is not optional for anything with a login.
- Secrets live in environment configuration or a secret manager, never in the code repository.
- Production data is not copied to laptops or test environments without being anonymized.
- Backups are automatic, stored separately, encrypted, and restored as a test at least once so you know they work.
- Personal data has a retention period and a way to delete an individual's records on request.
- Payment card details are handled by the payment provider, never stored by your application.
- Access to the production database is limited to named people, with credentials that can be revoked.
Dependencies and updates
Every library is code you did not write and are still responsible for. Set up automated dependency alerts, patch quickly when something is flagged, and remove packages you no longer use. Keep the runtime, the framework and the server operating system on supported versions, because an unsupported version eventually stops getting security fixes entirely.
Agree who does this after launch and how often, before launch. An application with no maintenance owner drifts into an insecure state within a year, whatever it was like on day one. Our guide on website maintenance covers what that ongoing work involves.
Logging, monitoring and response
You cannot respond to what you cannot see. Log authentication events, permission failures, administrative actions and server errors, and send them somewhere a person will actually look.
- Alerts for error spikes, repeated failed logins and unusual traffic.
- Uptime monitoring that pages a human, not an inbox nobody reads.
- No passwords, tokens or full card numbers in logs, ever.
- A written plan for the first hour of an incident: who is called, how you take the app offline, where the backups are, and who talks to customers.
Keep logs long enough to investigate something reported weeks later, and store them where an attacker who reaches the application cannot quietly edit them. Decide up front how long they are kept, because logs contain personal data too and should not sit forever by accident.
Pre-launch checklist
| Item | Done when |
|---|---|
| HTTPS everywhere | Insecure requests redirect and the API is covered too |
| Passwords hashed | A modern password hashing algorithm is in use, verified in code |
| Two-factor available | Enabled and required for administrators |
| Server-side permission checks | Tested by trying to reach another account's records |
| Parameterized queries | No query built by joining strings anywhere |
| Output escaped | User content renders as text, with a content security policy set |
| Secrets out of the repository | Rotated after any exposure, stored in configuration |
| Backups tested | A restore has actually been performed |
| Dependencies current | Alerts enabled and open advisories resolved |
| Logging and alerts | Someone receives them and knows what to do |
| Rate limiting | Login, reset and messaging endpoints are limited |
| Environments separated | Test data and credentials are not production ones |
| Incident plan | Written down, with names and phone numbers |
If the application handles health, financial or children's data, or operates in a regulated sector, get specific advice for those rules. This checklist is a baseline for any application, not a compliance program, and it is not legal advice.
Next step
If you are about to launch, or you inherited an application nobody has reviewed, we can work through this checklist against your codebase and hosting and give you a prioritized list of fixes. Tell us what the app does and what data it holds on the contact page, or see the web application development page. If you are still building, our guide on how to build a web app shows where each of these items fits in the process.