When someone brings up security in a web project, the conversation usually jumps straight to OWASP, SQL injection and XSS. That matters, but there’s a layer underneath it that’s more basic and cheaper to get right: how the application is exposed and who can talk to whom.
There are four practices. None of them is sophisticated, and together they cover a big part of the real risk.
1. Least-privilege firewall rules
Each resource gets only the minimum access it needs, and everything else is denied by default.
- The load balancer accepts
443(and80only to redirect). - The application accepts traffic only from the load balancer.
- The database accepts connections only from the application, on its own port.
- SSH, if it exists at all, is restricted to an IP or a bastion. Never
0.0.0.0/0.
The point isn’t to prevent anything from ever being compromised. It’s to make sure that, if something is, the damage stays contained to that piece.
2. Secrets out of the code, inside a vault
Database passwords, API keys and tokens don’t go in the repository, or in a committed .env “just for now”. They go in a vault (HashiCorp Vault, AWS Secrets Manager, SSM Parameter Store), and the application reads them at runtime.
The win isn’t just hiding the password:
- centralized access control: who can read what;
- audit trail: who read it and when;
- rotation without redeploy: replacing a leaked credential becomes an operation, not an incident.
3. HTTPS to expose the application
All public traffic goes over TLS. Without it, data and session cookies travel in plain text, and anyone in the middle can read or tamper with them.
In practice, the certificate lives on the load balancer or proxy (ACM, Let’s Encrypt), with an HTTP → HTTPS redirect and HSTS enabled, so the browser doesn’t even try HTTP again.
4. Subnet tiers: LB, app and database
The network is split into layers, each with one job:
| Layer | Subnet | Who can reach it |
|---|---|---|
| Load balancer | Public | The internet |
| Application | Private | Only the LB (outbound via NAT, if needed) |
| Database | Private | Only the application, no route to the internet |
Combined with least-privilege, this becomes defense in depth. To reach the database, an attacker would have to get through the LB and compromise the application first. The database simply doesn’t exist to the internet.
What this doesn’t cover
This is application infrastructure security. It doesn’t replace input validation, solid authentication and authorization, up-to-date dependencies and decent logging. But without this base, everything else sits on sand: the best-validated code in the world won’t help if the database port is open to the internet.
Checklist
- Firewall rules deny everything by default and allow only what’s needed
- No database or SSH port open to
0.0.0.0/0 - Secrets in a vault, nothing in the repository
- Credentials can be rotated without a redeploy
- HTTPS on all public traffic, with redirect and HSTS
- LB in a public subnet; app and database in private subnets
- Database with no route to the internet