step 24 / 32 · 6 min
The vulnerabilities you will actually meet
Security in a first job is rarely cryptography. It is a small number of recurring mistakes, and knowing them by name covers most of what goes wrong.
Injection
Building a query or command by concatenating input.
db.execute(f"SELECT * FROM users WHERE email = '{email}'")
# email = "' OR '1'='1" -> returns everyoneThe fix is parameterisation, which sends the query and the values separately so the value can never be read as syntax:
db.execute("SELECT * FROM users WHERE email = ?", (email,))Escaping by hand is the wrong fix: it depends on getting every case right, forever. The same principle applies to shell commands and to any interpreter.
Cross site scripting
Rendering user input as HTML, so their script runs in another user's browser with that user's session.
Modern templates escape by default, which is why the vulnerability now usually appears where someone deliberately bypassed it: dangerouslySetInnerHTML, v-html, | safe. Those names are warnings.
Broken access control
The most common serious flaw, and the least dramatic.
GET /api/orders/1234 # my order
GET /api/orders/1235 # somebody else's, and the server returns itThe interface never offered that link, so nobody tested it. Every request must check that this user may see this record, on the server, every time.
Authentication mistakes
| Mistake | Instead |
|---|---|
| Storing passwords, or hashing with SHA-256 | bcrypt, scrypt or argon2 |
| No rate limit on login | limit by account and by IP |
| Session id in the URL | in a cookie, HttpOnly and Secure |
| Telling them which field was wrong | "email or password is incorrect" |
That last row is deliberate: distinguishing them confirms to an attacker which email addresses are registered.
Secrets
Not in the repository, not in the image, not in logs. In environment variables or a secret manager, with scanning in the pipeline, because this is a mistake competent people make regularly.
A secret that was committed is compromised. Rotate it; rewriting history does not recall what was already read.
Dependencies
Most of your application is other people's code. A known vulnerability in a library is as exploitable as one in yours, and far easier to find at scale.
Run a scanner in CI, update regularly rather than in a panic, and pin versions so a build is reproducible.
The habits
Validate on the server, always. Least privilege everywhere. Fail closed, so an error denies rather than permits. Log the security relevant events, and never the secrets.