Skip to main content

Security Practices

HTTPS Everywhere

  • HTTPS (Hypertext Transfer Protocol Secure) = HTTP + TLS (Transport Layer Security).
  • Ensures that all communication between the client (browser, mobile app, or service) and the API server is:
    • Encrypted → prevents eavesdropping (man-in-the-middle attacks).
    • Authenticated → guarantees the server is who it claims to be.
    • Integrity-protected → prevents data tampering in transit.

“HTTPS Everywhere” means: Always enforce HTTPS for all REST API requests — no exceptions.

Why HTTPS is Critical for REST APIs

  1. Protects Sensitive Data
  • API calls often include tokens, API keys, passwords, user info.
  • Without HTTPS, this data can be stolen in plain text.
  1. Prevents Man-in-the-Middle (MITM) Attacks
  • Attackers can intercept and modify requests on unsecured HTTP.
  • With HTTPS, TLS ensures encryption + server authenticity.
  1. Secures Authentication & Authorization
  • Mechanisms like Basic Auth, OAuth 2.0, JWT are safe only if transported via HTTPS.
  1. Builds Trust
  • Most modern clients reject HTTP APIs or warn users.
  • Browsers block mixed content (HTTPS site calling HTTP API).

How to Enforce HTTPS in REST APIs

  1. Redirect all HTTP requests → HTTPS
  • If a client mistakenly calls http://api.example.com, the server responds with 301 Moved Permanently → redirects to https://api.example.com.
  1. Use HSTS (HTTP Strict Transport Security)
  • Tells browsers: “Always use HTTPS, even if the user types HTTP.”

Example header:

Strict-Transport-Security: max-age=31536000; includeSubDomains
  1. Disable HTTP completely
  • Block port 80 (HTTP) and only accept 443 (HTTPS).
  1. Use TLS Certificates (SSL)
  • Get a trusted TLS cert (e.g., from Let’s Encrypt).
  • Keep certificates updated & rotated.
  1. Validate Certificates
  • Clients should reject self-signed or invalid certs unless explicitly trusted.

REST API over HTTPS

Insecure HTTP Request

GET http://api.example.com/user/profile
Authorization: Bearer abc123token

Risk: The token is sent in plain text. Anyone sniffing the network (Wi-Fi hotspot, ISP, proxy) can steal it.

Secure HTTPS Request

GET https://api.example.com/user/profile
Authorization: Bearer abc123token

The request and token are encrypted with TLS.

Even if intercepted, attackers cannot read or modify it.

Cross-Origin Resource Sharing

  • CORS is a browser security mechanism that controls how web applications (running in one origin/domain) can request resources from another origin.
  • It prevents malicious websites from making unauthorized requests to your REST API on behalf of a user.

Origin = combination of:

scheme + domain + port
  • https://app.example.com → origin
  • https://api.example.com → different origin

By default, browsers block cross-origin requests for security. CORS defines rules for when to allow them.

Why CORS is Important in REST APIs

  • REST APIs are often consumed by frontend apps hosted on different domains.
  • Example:
    • Frontend app → https://myapp.com
    • Backend API → https://api.myapp.com
  • Without CORS → browser blocks API calls.
  • With properly configured CORS → only trusted domains can call the API.

How CORS Works

  1. Simple Request (e.g., GET with standard headers):
  • Browser sends request directly with an Origin header.
  • Server responds with Access-Control-Allow-Origin header if allowed.
GET /user/profile HTTP/1.1
Host: api.example.com
Origin: https://myapp.com

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Content-Type: application/json

{ "username": "masum", "email": "masum@example.com" }
  1. Preflight Request (for non-simple requests, e.g., POST with custom headers):
  • Browser sends an OPTIONS request first.
  • Server replies with allowed methods, headers, origins.
  • If valid → browser sends the actual request.

Example (Preflight request):

OPTIONS /user/profile HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

Response:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

Then the browser proceeds with the real POST request.

CORS Headers Explained

  • Access-Control-Allow-Origin → which domains are allowed (e.g., https://myapp.com or *).

  • Access-Control-Allow-Methods → allowed HTTP methods (GET, POST, PUT, DELETE).

  • Access-Control-Allow-Headers → allowed request headers (Authorization, Content-Type).

  • Access-Control-Allow-Credentials → whether cookies/auth headers are allowed (true/false).

  • Access-Control-Max-Age → how long preflight results are cached (in seconds).

CORS in REST API

Suppose your API is at https://api.example.com and frontend at https://app.example.com.

Correct CORS Response

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
  • Only requests from https://app.example.com are allowed.
  • API allows specific methods & headers.
  • Cookies or JWT tokens can be sent with withCredentials = true.

Best Practices for CORS in REST APIs

  1. Never use * for Access-Control-Allow-Origin if your API requires authentication.
  2. Restrict allowed methods
  • Only expose what’s necessary (GET, POST).
  1. Restrict allowed headers
  • Avoid letting clients send arbitrary headers.
  1. Enable credentials only when needed
  • Access-Control-Allow-Credentials: true → only for trusted apps.
  1. Use short max-age for sensitive APIs
  • Prevents browsers from caching bad CORS rules too long.

Rate Limiting

Rate limiting is the process of restricting the number of requests a client can make to an API within a specific timeframe.

  • Prevents DoS/DDoS attacks (attackers flooding API with requests).
  • Prevents brute force attacks (guessing passwords or tokens).
  • Ensures fair usage so one client doesn’t overload the system.

Example:

  • Limit each client to 100 requests per minute.
  • If a client exceeds → return 429 Too Many Requests.

Example of Rate Limiting

API allows max 5 requests per minute per user.

Request (6th request within 1 minute)

GET /user/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{
"error": "Rate limit exceeded. Try again in 30 seconds."
}

Retry-After: 30 → tells client how long to wait before retrying.

Common Rate Limiting Strategies

  1. Fixed Window
  • Example: 100 requests per minute.
  • Simple but can cause bursts at window edges.
  1. Sliding Window / Rolling Window
  • Tracks requests over the last N seconds.
  • Smoother enforcement.
  1. Token Bucket (most popular)
  • Each client has a "bucket" of tokens (e.g., 10 tokens).
  • Each request consumes a token.
  • Tokens refill at a fixed rate.
  1. Leaky Bucket
  • Similar to token bucket but enforces steady outflow (good for throttling).

Best Practices of Rate Limiting

  • Different limits for different endpoints
    • Example: /login (lower limit) vs /posts (higher).
  • Different limits per user / API key / IP
  • Send helpful headers
    • X-RateLimit-Limit → max allowed
    • X-RateLimit-Remaining → remaining requests
    • X-RateLimit-Reset → reset time
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 5
X-RateLimit-Reset: 1694200320
  • Use caching layer (Redis, Memcached) for fast counting.
  • Combine with monitoring to detect abuse.

Throttling

Throttling is slowing down or delaying requests when clients exceed certain thresholds.

  • Instead of outright blocking, the API may:
    • Queue requests.
    • Allow fewer requests per second.
  • Ensures system stays responsive under heavy load.

Example:

  • API allows 10 requests per second.
  • If a client sends 20, extra requests are delayed (processed later).

Example of Throttling

Scenario: API allows max 10 requests per second.

If a client sends 15 requests:

  • First 10 are processed immediately.
  • Next 5 are delayed and spread out over time.

Client won’t see errors, but responses arrive slower.

Rate Limiting vs Throttling

FeatureRate Limiting 🚦Throttling 🕒
PurposeRestrict maximum requestsSlow down requests
BehaviorBlock when limit exceededDelay or spread requests
Example100 requests/minute max10 requests/sec, others queued
Error Code429 Too Many RequestsUsually still 200 (delayed)

Avoiding common vulnerabilities

SQL Injection

  • SQL Injection happens when untrusted input is concatenated into a database query.
  • Attackers can manipulate queries to read, modify, or delete data.

Vulnerable Example

# Python pseudo-code
username = request.GET["username"]
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
# Python pseudo-code
username = request.GET["username"]
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
  • If attacker sends: username=admin' OR '1'='1
  • Generated query: SELECT * FROM users WHERE username = 'admin' OR '1'='1'; Returns all users instead of just one.

Prevention

  1. Use Parameterized Queries / Prepared Statements: cursor.execute("SELECT * FROM users WHERE username = %s", (username,))

  2. Use ORM frameworks (like Django ORM, Hibernate, SQLAlchemy).

  3. Validate and sanitize input (ensure type: string, int, etc.).

Cross-Site Scripting

  • XSS occurs when malicious JavaScript is injected into your API’s response.
  • If your REST API is consumed by a frontend (like React, Angular), stored XSS can allow attackers to steal cookies, tokens, or session info.

Vulnerable Example

API returns unescaped user input:

{
"comment": "<script>alert('Hacked!')</script>"
}

Browser frontend might render this as executable script → attacker code runs.

Prevention

  1. Escape or sanitize output before sending to clients.
  • Use libraries like DOMPurify (JS), OWASP ESAPI (Java).
  1. Return only JSON, never raw HTML from REST APIs.
{ "comment": "&lt;script&gt;alert('Hacked!')&lt;/script&gt;" }
  1. Set correct headers
  • Content-Type: application/json
  • X-Content-Type-Options: nosniff
  1. Implement CSP (Content Security Policy) on frontend.

Cross-Site Request Forgery

  • CSRF tricks a logged-in user’s browser into making an unintended request to your API.
  • Example: If user is logged into a banking app, an attacker could trick them into transferring money using a malicious webpage.

Vulnerable Example

  • User is logged in to bank.com
  • Attacker hosts a page with:
<form action="https://bank.com/api/transfer" method="POST">
<input type="hidden" name="amount" value="1000" />
<input type="hidden" name="toAccount" value="attacker123" />
</form>
<script>
document.forms[0].submit();
</script>

User unknowingly sends money while visiting attacker’s site.

Prevention

  1. Use anti-CSRF tokens
  • Include random token in forms/requests.
{ "csrf_token": "abc123xyz" }
  1. Use SameSite cookies
  • Set-Cookie: session=abc123; SameSite=Strict; Secure
  • Prevents cookies from being sent in cross-site requests.
  1. Require re-authentication for critical actions (e.g., money transfer).

  2. Use JWT in headers instead of cookies for stateless APIs (but still ensure proper CORS rules).

Defense-in-Depth Checklist

VulnerabilityProtection
XSSOutput escaping, CSP, Helmet
CSRFTokens, SameSite cookies
SQL InjectionParameterized queries
AllInput validation
AllLeast privilege DB users

Real-world Secure Stack (Recommended)

app.use(helmet());
app.use(express.json());
app.use(cors(corsOptions));
app.use(rateLimiter);

API Gateway usage

  • An API Gateway is a single entry point for all client requests to your REST APIs.
  • Instead of clients calling services directly, they go through the gateway.
  • The gateway then routes, filters, or modifies the requests before sending them to backend microservices.

Why Use an API Gateway for Security?

  • Centralized authentication & authorization
  • Rate limiting & throttling
  • Logging & monitoring (audit trails)
  • CORS & HTTPS enforcement
  • Input validation & threat protection (e.g., blocking SQLi, XSS patterns)
  • Acts as a firewall for APIs (protects backend services from direct exposure)

How an API Gateway Works (Flow)

  1. Client makes request to https://api.example.com
  2. API Gateway intercepts request → applies policies (auth, rate limit, CORS, etc.)
  3. If valid → forwards to backend service (e.g., user-service, payment-service)
  4. Collects response and returns it to client

Key Security Features of API Gateway

  1. Authentication & Authorization
  • Gateway can check API keys, JWT tokens, OAuth 2.0 tokens before forwarding requests.
  • Example: If a request lacks a valid token → rejected at gateway (never reaches backend).
  1. Rate Limiting & Throttling
  • Prevents abuse and DDoS attacks.
  • Example: Limit 100 requests/minute per user.
  1. Data Protection (HTTPS Everywhere)
  • Gateway enforces TLS/SSL.
  • Backend services can remain private (internal network only).
  1. Input Validation / Threat Protection
  • Blocks requests with malicious payloads (SQL injection, XSS, etc.).
  • Example: API Gateway WAF (Web Application Firewall) integration.
  1. CORS Handling
  • Centralized CORS configuration instead of every microservice handling it.
  1. Logging & Monitoring
  • Logs all requests/responses.
  • Helps detect anomalies (e.g., brute force attempts).

Example Scenario with API Gateway

  • Frontend app → https://app.example.com
  • API Gateway → https://api.example.com
  • Backend services (internal):
    • user-service.local
    • payment-service.local

Request Flow

GET /users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Gateway Actions

  1. Check authentication → validate JWT.

  2. Check rate limit → ensure client hasn’t exceeded quota.

  3. Check CORS policy → ensure request origin is allowed.

  4. Forward request → user-service.local/api/profile.

Response from Gateway

HTTP/1.1 200 OK
Content-Type: application/json

{ "id": 101, "username": "masum", "email": "masum@example.com" }

If client exceeded limit:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{ "error": "Rate limit exceeded. Try again later." }