Why this matters
Backends hold secrets, money, and personal data. A single vulnerability can leak databases, drain wallets, or take your service offline. OWASP gives you a short list of the most common, high-impact risks so you can build safer APIs and services faster.
- Real tasks you will face: designing auth flows, validating input for APIs, securing file uploads, encrypting data, handling secrets, and logging security events.
- Goal: ship features without introducing high-risk vulnerabilities that cause incidents, audits, or fines.
Quick note: The quick test is available to everyone. If you log in, your progress will be saved.
Who this is for
- Backend engineers building APIs or services.
- Developers doing code reviews or incident response.
- Engineers preparing for security reviews or compliance audits.
Prerequisites
- Basic web/API knowledge (HTTP, cookies/headers, JSON).
- Comfort with at least one server-side language.
- Basic database knowledge (SQL or NoSQL).
Concept explained simply
OWASP is a community that publishes the most common and severe web risks (the "Top 10"). Think of it as a checklist of traps to avoid in every feature you ship.
Mental model
- Attack Surface: entry points (endpoints, forms, headers, files, queues, cron jobs).
- Controls: input validation, auth, access control, encryption, safe defaults.
- Verification: tests, code review, logging, monitoring, and least privilege in production.
OWASP Top 10 (2021) in plain language
A01 Broken Access Control
Users can act as someone else or access data they should not. Fix with server-side checks: verify identity, ownership, and role on every request.
A02 Cryptographic Failures
Data not protected in transit or at rest. Use TLS, strong algorithms, and never roll your own crypto. Rotate keys and avoid storing plain secrets.
A03 Injection
Untrusted input changes queries or commands (SQL, NoSQL, OS, LDAP). Use parameterized queries, allowlists, and safe APIs instead of string building.
A04 Insecure Design
Architecture choices make security impossible (e.g., no rate limits, no segregation of duties). Threat-model and design controls before coding.
A05 Security Misconfiguration
Default passwords, verbose errors, open admin panels. Harden configs, least privilege, and consistent environment baselines.
A06 Vulnerable and Outdated Components
Old libraries with known CVEs. Track dependencies, update regularly, and remove unused packages.
A07 Identification and Authentication Failures
Weak login, session, or MFA handling. Use secure session cookies, proper password hashing, and lockout/step-up where needed.
A08 Software and Data Integrity Failures
Builds or updates are tampered with. Use signed artifacts, checksums, and verified sources for plugins and containers.
A09 Security Logging and Monitoring Failures
Attacks go unnoticed. Log auth and sensitive operations, avoid secrets in logs, and alert on anomalies.
A10 Server-Side Request Forgery (SSRF)
Server fetches attacker-controlled URLs. Restrict outbound requests, block access to internal metadata and private networks, and allowlist destinations.
Worked examples
1) Prevent SQL Injection (A03)
Bad:
// Node (unsafe)
const q = `SELECT * FROM users WHERE email = '${req.body.email}'`;
const rows = await db.query(q);
Good:
// Node with parameterized query
const q = 'SELECT * FROM users WHERE email = $1';
const rows = await db.query(q, [req.body.email]);
- Rule: never build queries with string concatenation; use bound parameters.
2) Enforce Ownership (A01)
Bad:
// GET /invoices/:id returns invoice by id regardless of owner
const inv = await db.getInvoice(req.params.id);
res.json(inv);
Good:
// Ensure requester owns the resource
const inv = await db.getInvoice(req.params.id);
if (!inv || inv.user_id !== req.user.id) return res.status(403).end();
res.json(inv);
- Rule: verify resource ownership on the server for every access.
3) Secure File Upload (A05, A08)
Checklist:
- Allowlist mime types and extensions (e.g., .jpg, .png).
- Store outside web root or in object storage with private ACLs.
- Generate random filenames; never execute or parse as code.
- Scan size limits and reject archives with nested content.
// Pseudocode
if (!ALLOWLIST.includes(file.mime)) return 415;
if (file.size > MAX_SIZE) return 413;
const safeName = randomUUID() + extname(file.name);
storePrivately(file.stream, safeName);
Baseline secure-by-default checklist
- Use parameterized queries (SQL/NoSQL) and safe command APIs.
- Validate and sanitize all external input; prefer allowlists.
- Require auth by default; deny by default for endpoints.
- Check ownership and roles on every data access.
- HTTPS only; secure cookies; HSTS; no mixed content.
- Secrets in a secure store; rotate keys; least privilege for service accounts.
- Dependency updates on schedule; remove unused components.
- Structured security logs with request IDs; alert on auth anomalies.
- Rate limits and timeouts for external calls; restrict egress to trusted hosts.
Exercises
Complete the exercise below, then check your work. The quick test is available to everyone; sign in to save your results.
Exercise 1: Fix the vulnerable endpoints (paper exercise)
Given the following simplified code, identify OWASP category for each issue and propose a fix.
// 1) Login
const user = await db.get(`SELECT * FROM users WHERE email = '${body.email}' AND pass = '${body.pass}'`);
if (!user) return 401;
session.userId = user.id;
// 2) Get invoice
const invoice = await db.get(`SELECT * FROM invoices WHERE id = ${params.id}`);
return json(invoice);
// 3) Proxy fetch
const r = await fetch(body.url);
return text(await r.text());
Deliverables:
- Which OWASP category applies to each snippet?
- One concrete fix for each.
- When you finish, compare with the solution inside the exercise block below.
Common mistakes and self-check
- Mistake: Trusting client-side checks. Self-check: Can you call the API with curl and still bypass?
- Mistake: "Admin-only" hidden buttons. Self-check: Does the server verify role before action?
- Mistake: Blacklisting strings for injection. Self-check: Are you using prepared statements for all queries?
- Mistake: Logging secrets. Self-check: Search logs for tokens/passwords; they should not appear.
- Mistake: Overly broad network egress. Self-check: Can your service call internal metadata IPs? Block it.
Practical projects
- Harden a demo REST API: add auth, access control, parameterized queries, input validation, secure headers, and structured security logs. Verify with negative tests.
- Implement an upload service: allowlist mimetypes, scan sizes, store privately, generate signed short-lived URLs for access.
Learning path
- Start with OWASP basics (this page): categories, mental model, and safe defaults.
- Deep dive by risk: access control, auth/session, input validation, crypto at rest/in transit.
- Operationalize: logging/monitoring, dependency management, configuration hardening.
Mini challenge
You add a new feature: an endpoint that imports data from a user-provided URL and stores rows into your DB. In 5 minutes, list: entry points, top 3 OWASP risks, and one control per risk. Keep it concrete.
Starter ideas
- Risks: SSRF, injection via parsed content, auth bypass if import is privileged.
- Controls: allowlist domains, parse with strict schema, run as a low-privilege worker, rate limit, and log every import with outcome.
Next steps
- Apply the baseline checklist to one of your services this week.
- Run the quick test below. If you log in, your progress will be saved.