Files
2025-11-29 17:51:02 +08:00

10 KiB

Vulnerability Remediation Guide

Security remediation patterns organized by vulnerability category.

Table of Contents

SQL Injection

Vulnerability Pattern

# VULNERABLE
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)

Secure Remediation

# SECURE: Use parameterized queries
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_id,))

# Or use ORM
user = User.objects.get(id=user_id)

Framework-Specific Solutions

Django:

# Use Django ORM (safe by default)
User.objects.filter(email=user_email)

# For raw SQL, use parameterized queries
User.objects.raw('SELECT * FROM myapp_user WHERE email = %s', [user_email])

Node.js (Sequelize):

// Use parameterized queries
User.findAll({
  where: { email: userEmail }
});

// Or use replacements
sequelize.query(
  'SELECT * FROM users WHERE email = :email',
  { replacements: { email: userEmail } }
);

Java (JDBC):

// Use PreparedStatement
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(1, userId);
ResultSet rs = stmt.executeQuery();

Cross-Site Scripting (XSS)

Vulnerability Pattern

// VULNERABLE
element.innerHTML = userInput;
document.write(userInput);

Secure Remediation

// SECURE: Use textContent for text
element.textContent = userInput;

// Or properly escape HTML
element.innerHTML = escapeHtml(userInput);

function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

Framework-Specific Solutions

React:

// React auto-escapes by default
<div>{userInput}</div>

// For HTML content, sanitize first
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(userInput)}} />

Flask/Jinja2:

# Templates auto-escape by default
{{ user_input }}

# For HTML content, sanitize
from markupsafe import Markup
import bleach
{{ Markup(bleach.clean(user_input)) }}

Django:

{# Auto-escaped by default #}
{{ user_input }}

{# Mark as safe only after sanitization #}
{{ user_input|safe }}

Command Injection

Vulnerability Pattern

# VULNERABLE
os.system(f"ping {user_host}")
subprocess.call(f"ls {user_directory}", shell=True)

Secure Remediation

# SECURE: Use subprocess with list arguments
import subprocess
subprocess.run(['ping', '-c', '1', user_host],
               capture_output=True, check=True)

# Validate input against allowlist
import shlex
if not re.match(r'^[a-zA-Z0-9.-]+$', user_host):
    raise ValueError("Invalid hostname")
subprocess.run(['ping', '-c', '1', user_host])

Node.js:

// VULNERABLE
exec(`ls ${userDir}`);

// SECURE
const { execFile } = require('child_process');
execFile('ls', [userDir], (error, stdout) => {
  // Handle output
});

Path Traversal

Vulnerability Pattern

# VULNERABLE
file_path = os.path.join('/uploads', user_filename)
with open(file_path) as f:
    return f.read()

Secure Remediation

# SECURE: Validate and normalize path
import os
from pathlib import Path

def safe_join(directory, user_path):
    # Normalize and resolve path
    base_dir = Path(directory).resolve()
    file_path = (base_dir / user_path).resolve()

    # Ensure it's within base directory
    if not str(file_path).startswith(str(base_dir)):
        raise ValueError("Path traversal detected")

    return file_path

try:
    safe_path = safe_join('/uploads', user_filename)
    with open(safe_path) as f:
        return f.read()
except ValueError:
    return "Invalid filename"

Insecure Deserialization

Vulnerability Pattern

# VULNERABLE
import pickle
data = pickle.loads(user_data)

Secure Remediation

# SECURE: Use safe formats like JSON
import json
data = json.loads(user_data)

# If you must deserialize, validate and restrict
import yaml
data = yaml.safe_load(user_data)  # Use safe_load, not load

Node.js:

// VULNERABLE
const data = eval(userInput);
const obj = Function(userInput)();

// SECURE
const data = JSON.parse(userInput);

// For complex objects, use schema validation
const Joi = require('joi');
const schema = Joi.object({
  name: Joi.string().required(),
  email: Joi.string().email().required()
});
const { value, error } = schema.validate(JSON.parse(userInput));

Weak Cryptography

Vulnerability Pattern

# VULNERABLE
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

Secure Remediation

# SECURE: Use bcrypt or argon2
import bcrypt

# Hashing
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())

# Verification
if bcrypt.checkpw(password.encode(), stored_hash):
    print("Password correct")

# Or use argon2
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash(password)
ph.verify(hash, password)

Encryption:

# VULNERABLE
from Crypto.Cipher import DES
cipher = DES.new(key, DES.MODE_ECB)

# SECURE: Use AES-GCM
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)

Authentication & Session Management

Vulnerability Pattern

// VULNERABLE
app.use(session({
  secret: 'weak-secret',
  cookie: { secure: false }
}));

Secure Remediation

// SECURE
const session = require('express-session');
app.use(session({
  secret: process.env.SESSION_SECRET, // Strong random secret
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,      // HTTPS only
    httpOnly: true,    // No JavaScript access
    sameSite: 'strict', // CSRF protection
    maxAge: 3600000    // 1 hour
  }
}));

Password Requirements:

# Implement strong password policy
import re

def validate_password(password):
    if len(password) < 12:
        return False
    if not re.search(r'[A-Z]', password):
        return False
    if not re.search(r'[a-z]', password):
        return False
    if not re.search(r'[0-9]', password):
        return False
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False
    return True

CSRF

Vulnerability Pattern

# VULNERABLE: No CSRF protection
@app.route('/transfer', methods=['POST'])
def transfer():
    amount = request.form['amount']
    to_account = request.form['to']
    # Process transfer

Secure Remediation

# SECURE: Use CSRF tokens
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

@app.route('/transfer', methods=['POST'])
@csrf.exempt  # Only if using custom CSRF
def transfer():
    # CSRF token automatically validated
    amount = request.form['amount']
    to_account = request.form['to']

Express.js:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.post('/transfer', csrfProtection, (req, res) => {
  // CSRF token validated
  const { amount, to } = req.body;
});

SSRF

Vulnerability Pattern

# VULNERABLE
import requests
url = request.args.get('url')
response = requests.get(url)

Secure Remediation

# SECURE: Validate URLs and use allowlist
import requests
from urllib.parse import urlparse

ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']

def safe_fetch(url):
    parsed = urlparse(url)

    # Check protocol
    if parsed.scheme not in ['http', 'https']:
        raise ValueError("Invalid protocol")

    # Check domain against allowlist
    if parsed.netloc not in ALLOWED_DOMAINS:
        raise ValueError("Domain not allowed")

    # Block internal IPs
    import ipaddress
    try:
        ip = ipaddress.ip_address(parsed.hostname)
        if ip.is_private:
            raise ValueError("Private IP not allowed")
    except ValueError:
        pass  # Not an IP, continue

    return requests.get(url, timeout=5)

XXE

Vulnerability Pattern

# VULNERABLE
from lxml import etree
tree = etree.parse(user_xml)

Secure Remediation

# SECURE: Disable external entities
from lxml import etree

parser = etree.XMLParser(
    resolve_entities=False,
    no_network=True,
    dtd_validation=False
)
tree = etree.parse(user_xml, parser)

# Or use defusedxml
from defusedxml import ElementTree
tree = ElementTree.parse(user_xml)

Node.js:

// Use secure XML parser
const libxmljs = require('libxmljs');
const xml = libxmljs.parseXml(userXml, {
  noent: false,  // Disable entity expansion
  dtdload: false,
  dtdvalid: false
});

General Security Principles

  1. Input Validation: Validate all user input against expected format
  2. Output Encoding: Encode output based on context (HTML, URL, SQL, etc.)
  3. Least Privilege: Grant minimum necessary permissions
  4. Defense in Depth: Use multiple layers of security controls
  5. Fail Securely: Ensure failures don't expose sensitive data
  6. Secure Defaults: Use secure configuration by default
  7. Keep Dependencies Updated: Regularly update libraries and frameworks

Testing Remediation

After applying fixes:

  1. Verify with Semgrep: Re-scan to ensure vulnerability is resolved

    semgrep --config <ruleset> fixed_file.py
    
  2. Manual Testing: Attempt to exploit the vulnerability

  3. Code Review: Have peer review the fix

  4. Integration Tests: Add tests to prevent regression

References