Skip to content

API Security — Input Validation, CORS, and SQL Injection Prevention

DodaTech Updated 2026-06-23 8 min read

In this tutorial, you'll learn about API Security. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

API security encompasses the practices and tools used to protect REST APIs from attacks including SQL Injection, cross-site scripting, cross-origin abuse, input validation exploits, and unauthorized access through proper request validation and security headers.

What You'll Learn

You will learn to protect APIs against SQL Injection, configure CORS correctly, implement input validation, prevent XSS Attacks, use security headers, and follow OWASP API Security Top 10 recommendations.

Why API Security Matters

APIs are the most attacked surface of modern applications. The OWASP API Security Top 10 lists the most critical risks, from broken authentication to mass assignment. A single API vulnerability can expose millions of user records. In 2025, API attacks accounted for 40 percent of all data breaches. Investing in API security upfront prevents costly incidents.

Real-World Use

DodaTech takes API security seriously across all products. Doda Browser implements strict CORS policies and input validation, DodaZIP uses parameterized queries for all database access, and Durga Antivirus Pro includes API security scanning in its CI/CD pipeline to catch vulnerabilities before deployment.

API Security Learning Path

flowchart LR
  A[REST API Design] --> B[Input Validation]
  B --> C[SQL Injection Prevention]
  C --> D[CORS & Headers]
  D --> E[XSS & CSRF Protection]
  E --> F[Rate Limiting & Auth]
  F --> G[Security Monitoring]
  B:::current

  classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px

Prerequisites

Understand RESTful Api Design Best Practices, Authentication Patterns JWT OAuth2 API Keys, and HTTP Protocol Basics. Basic security concepts like encryption and hashing are helpful.

Input Validation

Never trust user input. Validate every field on the server side, even if you validate on the client side.

Whitelist Validation

Only accept known-good values:

const Joi = require("joi");

const createUserSchema = Joi.object({
  name: Joi.string()
    .min(2)
    .max(100)
    .pattern(/^[a-zA-Z\s'-]+$/)
    .required()
    .messages({
      "string.pattern.base": "Name contains invalid characters"
    }),
  email: Joi.string()
    .email({ tlds: { allow: false } })
    .required(),
  age: Joi.number()
    .integer()
    .min(18)
    .max(120)
    .required(),
  role: Joi.string()
    .valid("admin", "member", "viewer")
    .default("member"),
  website: Joi.string()
    .uri()
    .optional()
});

Python Input Validation with Pydantic

from pydantic import BaseModel, EmailStr, Field, validator
import re

class CreateUserSchema(BaseModel):
    name: str = Field(..., min_length=2, max_length=100)
    email: EmailStr
    age: int = Field(..., ge=18, le=120)
    role: str = Field(default="member", pattern="^(admin|member|viewer)$")

    @validator("name")
    def validate_name(cls, v):
        if not re.match(r"^[a-zA-Z\s'-]+$", v):
            raise ValueError("Name contains invalid characters")
        return v.strip()

SQL Injection Prevention

SQL Injection occurs when user input is concatenated directly into SQL queries.

Vulnerable Code (NEVER do this)

// DANGEROUS: String concatenation
const id = req.params.id;
const result = await db.query(`SELECT * FROM users WHERE id = '${id}'`);
// Attacker: id = "'; DROP TABLE users; --"

Safe Code: Parameterized Queries

// SAFE: Parameterized query with pg library
const result = await db.query(
  "SELECT * FROM users WHERE id = $1",
  [req.params.id]
);
# SAFE: Parameterized query with psycopg2
cursor.execute(
    "SELECT * FROM users WHERE id = %s",
    (user_id,)
)

Using an ORM (Safer)

// Sequelize ORM
const user = await User.findByPk(req.params.id);

// Prisma ORM
const user = await prisma.user.findUnique({
  where: { id: req.params.id }
});

Common SQL Injection Vectors

Attack Malicious Input Effect
Tautology ' OR '1'='1 Returns all rows
UNION ' UNION SELECT * FROM passwords-- Returns data from other tables
Comment injection admin'-- Comments out the REST of query
Stacked queries '; DROP TABLE users; -- Executes multiple queries

CORS (Cross-Origin Resource Sharing)

CORS controls which domains can access your API from a browser.

How CORS Works

sequenceDiagram
    Browser->>API: OPTIONS /api/users (Preflight)
    API->>Browser: 200 OK (Access-Control-Allow-Origin: *)
    Browser->>API: GET /api/users
    API->>Browser: 200 OK (Data)

CORS Configuration in Express

const CORS = require("CORS");

// Restrictive: allow only specific origins
app.use(CORS({
  origin: [
    "HTTPS://dodatech.com",
    "HTTPS://app.dodatech.com",
    "HTTPS://dashboard.dodatech.com]
  ],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  exposedHeaders: ["X-RateLimit-Remaining"],
  credentials: true,           // Allow cookies
  maxAge: 86400               // Cache preflight for 24 hours
}));

// NEVER use this in production:
// app.use(CORS({ origin: "*" }));

CORS Configuration in FastAPI

from FastAPI.middleware.CORS import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "HTTPS://dodatech.com",
        "HTTPS://app.dodatech.com]
    ],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Content-Type", "Authorization"],
    max_age=86400
)

CORS Headers Explained

Header Purpose
Access-Control-Allow-Origin Which origins are allowed
Access-Control-Allow-Methods Which HTTP methods are allowed
Access-Control-Allow-Headers Which headers can be sent
Access-Control-Allow-Credentials Whether cookies are allowed
Access-Control-Max-Age How long to cache preflight
Access-Control-Expose-Headers Which headers the client can read

Security Headers

const helmet = require("helmet");

app.use(helmet());  // Sets multiple security headers

// Equivalent manual configuration:
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "HTTPS://CDN.dodatech.com"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:"],
      connectSrc: ["'self'", "HTTPS://API.dodatech.com"]
    }
  },
  hsts: {
    maxAge: 31536000,      // 1 year
    includeSubDomains: true,
    preload: true
  }
}));
Header Purpose
Content-Security-Policy Prevents XSS by controlling allowed content sources
Strict-Transport-Security Enforces HTTPS connections
X-Content-Type-Options Prevents MIME type sniffing
X-Frame-Options Prevents clickjacking
X-XSS-Protection Enables browser XSS filter

Mass Assignment Protection

Mass assignment occurs when an attacker sends extra fields that get written to the database.

Vulnerable Code

// DANGEROUS: Direct assignment from request body
app.put("/API/users/:id", async (req, res) => {
  const user = await User.findByPk(req.params.id);
  await user.update(req.body);  // Attacker can set { role: "admin" }
});

Safe Code: Whitelist Fields

// SAFE: Only update allowed fields
const allowedFields = ["name", "email"];
const updateData = {};

for (const field of allowedFields) {
  if (req.body[field] !== undefined) {
    updateData[field] = req.body[field];
  }
}

await user.update(updateData);

XSS Prevention

Cross-Site Scripting attacks inject malicious scripts into API responses.

// Escape HTML in responses
const escapeHtml = (text) => {
  const map = {
    "&": "&",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#x27;"
  };
  return text.replace(/[&<>"']/g, (m) => map[m]);
};

app.get("/API/users/:id", async (req, res) => {
  const user = await User.findByPk(req.params.id);
  // Always escape user-generated content in responses
  user.bio = escapeHtml(user.bio);
  res.JSON(user);
});

Security Checklist

  • All input validated server-side with whitelist approach
  • Parameterized queries for all database operations
  • CORS configured with specific allowed origins (not wildcard)
  • Security headers set via helmet or similar middleware
  • Rate Limiting on all endpoints
  • Authentication required for sensitive endpoints
  • Mass assignment prevented with field whitelists
  • Output escaped to prevent XSS
  • HTTPS enforced (HSTS header)
  • File upload validation (type, size, content)
  • API keys stored as hashes, not plaintext
  • Error messages do not leak internal details

Common Errors

  1. Using wildcard CORS in production — Setting Access-Control-Allow-Origin: * allows any website to make requests to your API. Always restrict to specific origins in production.

  2. Concatenating SQL queries — Building SQL by string concatenation with user input. Always use parameterized queries. Even ORMs can be vulnerable if you use raw queries.

  3. No input length limits — Accepting arbitrarily long input strings. Attackers can send multi-megabyte payloads to exhaust server memory. Set maxLength on all string fields.

  4. Returning internal error details — Sending Stack traces and database error messages to clients. Use a global error handler that returns sanitized messages.

  5. Trusting request headers for security decisions — Using X-Forwarded-For without validation for Rate Limiting or access control. These headers can be spoofed. Validate them against trusted Proxy lists.

  6. Not validating content type — Accepting any Content-Type including XML that may enable XXE Attacks. Restrict Content-Type to application/json and validate the parser.

  7. Missing Rate Limiting on auth endpoints — Allowing unlimited login attempts enables brute force attacks. Always rate limit auth endpoints to 5-10 attempts per minute per IP.

Practice Questions

  1. What is SQL Injection and how do you prevent it?
  2. How does CORS protect against cross-origin attacks?
  3. What is mass assignment and how do you prevent it?
  4. What security headers should every API include?
  5. Why is whitelist validation better than blacklist validation?

Challenge

Audit and secure an existing REST API. Review the codebase for: all SQL queries (ensure they use parameterized queries), CORS configuration (restrict to specific origins), input validation (add Joi or Pydantic schemas for all endpoints), security headers (add helmet middleware), Rate Limiting (implement on all endpoints), error handling (sanitize error messages), and authentication (verify JWT validation is correct). Write a security report documenting each vulnerability found and the fix applied.

FAQ

Is it safe to use ORMs or do I still need parameterized queries? ORMs like Sequelize, Prisma, and SQLAlchemy use parameterized queries internally for most operations. However, raw queries and some edge cases (like `$raw` in MongoDB) can still be vulnerable. Always use parameterized queries when writing raw SQL.

How do I handle CORS for mobile apps? CORS is a browser-only security mechanism. Mobile apps and server-to-server communication do not enforce CORS. You still need authentication and Rate Limiting for mobile clients.

What is the difference between authentication and authorization in API security? Authentication verifies who you are (JWT token, API key). Authorization determines what you can do (role-based access control). Both are required for API security. Authentication comes first, then authorization.

Should I sanitize input or escape output? Do both. Validate and sanitize input to reject malicious data early. Escape output to prevent XSS when user-generated content is displayed. Defense in depth means implementing both layers.

How do I protect against DDoS attacks on my API? Use Rate Limiting at the application layer, a Web Application Firewall (WAF) at the network layer, and a CDN with DDoS protection at the edge. AWS Shield, Cloudflare, and Akamai offer DDoS mitigation services.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro