C
Security/API Security/Lesson 04

Input Validation + API Security — Rate Limiting · CORS · Environment Variables

45 min·theory

Input Validation + API Security — Rate Limiting · CORS · Environment Variables

🎯 After reading this lesson

After finishing this lesson, you will be able to confidently do the following 3 things.

  • ✅ .env management + GitHub Secret Scanning
  • ✅ API Rate Limit (Token Bucket) implementation
  • ✅ CORS whitelist + helmet security headers

Keep the learning objectives as a checklist — close the lesson once you can answer all of them.

.env File Management — *The #1 Cause of GitHub Leak Incidents*

Never hardcode secrets directly in your code

javascript
// ❌ Strictly forbidden
const dbPassword = 'mysecret123';
const awsKey = 'AKIA...';

Once pushed to Git, it becomes a permanent record. If someone finds it, it will be exploited immediately.

Separate secrets using a .env file

bash
# .env
DATABASE_URL=postgresql://user:pass@localhost/db
JWT_SECRET=super_secret_random_string
AWS_ACCESS_KEY=AKIA...
AWS_SECRET_KEY=...
OPENAI_API_KEY=sk-...
javascript
// Reference in code
const db = process.env.DATABASE_URL;
const secret = process.env.JWT_SECRET;

Always add to .gitignore

code
# .gitignore
.env
.env.local
.env.production
*.pem
*.key

Add this immediately when starting a new project. Once committed, fixing it requires a major operation of rewriting the entire history with tools like git filter-repo.

.env.example is OK

bash
# .env.example — only key names, no actual values
DATABASE_URL=
JWT_SECRET=
AWS_ACCESS_KEY=

Serves as a guide to tell other developers "these keys are required". Since it contains no actual values, committing it is fine.

GitHub Leak Incidents — Real-World Cases

  • AWS key exposed → automated bots find it within 24 hours → Bitcoin mining → charges in the thousands of dollars
  • Stripe key exposed → payment system hijacked
  • OpenAI key exposed → unlimited token consumption

GitHub Secret Scanning detects these automatically, but the money is already gone.

If your key has been exposed

1. Revoke the key immediately (rotate) — issue a new key from AWS/OpenAI console, delete the old one
2. Clean Git historygit filter-repo or BFG Repo-Cleaner
3. Monitor billing — check your invoice
4. Notify the team

"One exposure = permanent exposure" — issuing a new key is non-negotiable.

Separate by environment

code
.env.local           # Local development
.env.development     # Development server
.env.production      # Production server

Production values should go in dedicated secrets stores like GitHub Actions Secrets, AWS Parameter Store, or Vault. Never leave them in a .env file on the server (risk of exposure through backups, logs, or mistakes).

🤖 Try asking AI like this

  • "Extract all hardcoded API keys from this code into process.env and generate a .env.example"
  • "Review whether this project's .gitignore is secure"

CORS · Rate Limiting · HTTPS — The 3 Lines of Defense

CORS — Why Does the Browser Block Requests?

CORS (Cross-Origin Resource Sharing) is the browser's safety mechanism. It blocks API calls from different domains by default.

code
Frontend: https://app.example.com
API:      https://api.other.com
→ Browser blocks the request (CORS error)

Without it — malicious sites could call your bank's API using your login cookies.

Allowing requests on the server

Express (Node.js)

javascript
import cors from 'cors';

app.use(cors({
    origin: ['https://app.example.com'],   // Explicitly specify allowed origins
    credentials: true,                       // Allow requests with cookies
    methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

Spring Boot

java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://app.example.com")
            .allowedMethods("*")
            .allowCredentials(true);
    }
}

Anti-pattern — Allowing All Origins

javascript
app.use(cors({ origin: '*' }));   // ❌ Worst security practice

Unless it is a public API, always specify origins using a whitelist.

Rate Limiting — Preventing Abuse

Why is it necessary?

  • An attacker calls 10,000 times per second → server goes down
  • Password brute-force attacks
  • AI API cost explosion

Express + express-rate-limit

javascript
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,    // 15 minutes
    max: 100,                     // 100 requests per IP
    message: 'Too many requests'
});

app.use('/api/', limiter);

Redis-based (Distributed Environment)

javascript
import { rateLimit } from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

const limiter = rateLimit({
    store: new RedisStore({ /* ... */ }),
    windowMs: 60_000,
    max: 10
});

When running multiple server instances, share state via Redis. In-memory counting on a single server is unsuitable for distributed environments.

Apply stricter limits to login endpoints

javascript
const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 5,           // 5 attempts per 15 minutes
    skipSuccessfulRequests: true   // Successful requests are not counted
});

app.post('/auth/login', loginLimiter, loginHandler);

HTTPS — Required in 2026

Why is it required?

  • HTTP transmits in plain text — can be intercepted over Wi-Fi
  • Chrome / Safari show 'Not Secure' → trust collapses
  • Modern APIs like Service Workers and camera access require HTTPS

Let's Encrypt Free Certificate

bash
# Nginx + Certbot
sudo certbot --nginx -d example.com -d www.example.com

Valid for 90 days + auto-renew. Every site can have HTTPS for free.

Vercel · Netlify

HTTPS is applied automatically. Zero configuration needed.

5 Security Headers — Free Points

javascript
// One line with Express helmet middleware
import helmet from 'helmet';
app.use(helmet());

helmet automatically sets:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: SAMEORIGIN (prevents clickjacking)
  • Strict-Transport-Security (HSTS — enforces HTTPS)
  • X-XSS-Protection (for legacy browsers)
  • Referrer-Policy: strict-origin-when-cross-origin

🤖 Try asking AI like this

  • "Add helmet + CORS whitelist + global rate limit to this Express API"
  • "Add a limit of 5 requests per 5 minutes to the login endpoint"
  • "Find 5 security vulnerabilities in this code"
Input Validation + API Security — Rate Limiting · CORS · Environment Variables - Security