Thinking like an attacker
A published API receives requests from anyone. Assume that every input is hostile until you validate it. The OWASP project maintains the list of the most common risks; these are the basics you must know.
Injection
It happens when user data is interpreted as code/commands. The classic case is SQL injection:
// BAD: the user controls part of the query.
db.query("SELECT * FROM users WHERE name = '" + name + "'");
// If name = "' OR '1'='1", the condition is always true.
// GOOD: parameterized queries. The engine treats the value as data, not code.
db.query("SELECT * FROM users WHERE name = $1", [name]);
The general defense: never build commands by concatenating user text; use parameters, prepared statements or an ORM.
XSS (Cross-Site Scripting)
If you store a comment like <script>stealCookies()</script> and then
render it as-is in the HTML, that script runs in other users' browsers. The
defense is to escape/sanitize the output (turn < into <, etc.) and, on
the client, avoid innerHTML with untrusted data.
Sanitize and validate input
Validate types, lengths and formats on the server (never trust browser validation alone). Reject what doesn't fit and normalize what you accept.
CORS
The browser, due to the same-origin policy, blocks by default a website at
https://myapp.com from calling your API at https://api.other.com via fetch.
CORS (Cross-Origin Resource Sharing) is the mechanism by which your server
explicitly authorizes which origins may call it, through headers like
Access-Control-Allow-Origin. It is not a protection against the attacker: it is
a permission your API grants to websites from another origin.
Rate limiting
Limiting how many requests a client is allowed per unit of time. It slows down brute force against login and abuse/DoS. Whoever exceeds it gets a 429 Too Many Requests.
Secrets in environment variables
Keys (JWT secret, DB passwords, API keys) are not written in the code nor committed to the repository. They go in environment variables and are read at runtime:
const SECRET = process.env.JWT_SECRET;
HTTPS always
Without HTTPS, tokens and passwords travel in plain text and anyone on the network can read them. In production, all traffic is encrypted with TLS.
Summary of defenses
| Risk | Defense |
|---|---|
| Injection | Parameterized queries, ORM |
| XSS | Escape/sanitize the output |
| Credential theft in transit | HTTPS/TLS |
| Brute force | Rate limiting + slow hashing |
| Leaked secrets | Environment variables |
| Cross-origin calls | CORS configured on purpose |