Menu

Topic 4 of 8

OWASP Basics

Learn OWASP Basics for free with explanations, exercises, and a quick test (for Backend Engineer).

Published: January 20, 2026 | Updated: January 20, 2026

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.

Practice Exercises

1 exercises to complete

Instructions

Identify the OWASP category for each issue and propose a concrete 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());
  • Deliverable A: map each snippet to an OWASP Top 10 category.
  • Deliverable B: one specific fix per snippet.
Expected Output
A list mapping each code snippet to an OWASP category and a clear remediation (e.g., prepared statements, ownership checks, URL allowlist).

OWASP Basics — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about OWASP Basics?

AI Assistant

Ask questions about this tool