Application Security

Secure Code Review

4 min read

Secure code review is a core AppSec skill tested in most security interviews. This lesson covers the tools, techniques, and vulnerabilities you need to identify.

SAST vs DAST vs SCA

Tool Type What It Analyzes When It Runs Examples
SAST Source code Build time Semgrep, CodeQL, SonarQube
DAST Running application Runtime Burp Suite, OWASP ZAP, Nuclei
SCA Dependencies Build time Snyk, Dependabot, Trivy
IAST Running + instrumented Runtime Contrast Security

Interview Question

Q: "What are the limitations of SAST tools?"

Answer:

  • False positives: Flagging safe code as vulnerable
  • No runtime context: Can't detect configuration issues
  • Language-specific: Each language needs different rules
  • Business logic blind: Can't understand intended behavior

Code Review Methodology

The STRIDE-per-Element Approach

When reviewing code, systematically check for:

1. INPUT HANDLING
   └── Where does external data enter?
   └── Is it validated, sanitized, escaped?

2. AUTHENTICATION
   └── How are credentials handled?
   └── Are sessions managed securely?

3. AUTHORIZATION
   └── Are access controls enforced?
   └── Is there role/permission checking?

4. DATA PROTECTION
   └── Is sensitive data encrypted?
   └── Are secrets hardcoded?

5. ERROR HANDLING
   └── Do errors leak information?
   └── Are failures handled safely?

Spotting Vulnerabilities in Code

SQL Injection

# VULNERABLE
def get_user(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    return db.execute(query)

# SECURE
def get_user(username):
    query = "SELECT * FROM users WHERE username = %s"
    return db.execute(query, (username,))

Command Injection

# VULNERABLE
import os
def ping_host(host):
    os.system(f"ping -c 1 {host}")

# SECURE
import subprocess
def ping_host(host):
    # Validate input
    if not re.match(r'^[\w.-]+$', host):
        raise ValueError("Invalid hostname")
    subprocess.run(["ping", "-c", "1", host], check=True)

Path Traversal

# VULNERABLE
def read_file(filename):
    with open(f"/uploads/{filename}") as f:
        return f.read()

# SECURE
import os
def read_file(filename):
    base_dir = "/uploads"
    full_path = os.path.normpath(os.path.join(base_dir, filename))
    # Ensure path stays within base directory
    if not full_path.startswith(base_dir):
        raise ValueError("Invalid path")
    with open(full_path) as f:
        return f.read()

Insecure Deserialization

# VULNERABLE
import pickle
def load_session(data):
    return pickle.loads(base64.b64decode(data))

# SECURE
import json
def load_session(data):
    return json.loads(base64.b64decode(data))

Interview Exercise: Find the Bugs

// How many vulnerabilities can you spot?
app.get('/user/:id', async (req, res) => {
    const userId = req.params.id;
    const query = `SELECT * FROM users WHERE id = ${userId}`;

    try {
        const user = await db.query(query);
        res.send(`<h1>Welcome ${user.name}</h1>`);
    } catch (err) {
        res.send(`Error: ${err.message}`);
    }
});

Answer:

  1. SQL Injection: Direct interpolation of userId
  2. XSS: Unescaped user.name in HTML response
  3. Information Disclosure: Full error message exposed
  4. Missing AuthZ: No check if requester can access this user

Interview Tip: When given code to review, verbalize your thought process. Interviewers want to see HOW you find vulnerabilities, not just that you find them.

Next, we'll cover threat modeling methodologies. :::

Quiz

Module 3: Application Security

Take Quiz