OWASP Top 10: Web Application Security Risks

We will examine in detail the most critical web application security risks identified by OWASP (Open Web Application Security Project). These risks represent the most common and devastating security vulnerabilities in modern web applications.

1. Injection

SQL, NoSQL, OS, and LDAP injection attacks occur when untrusted data is interpreted as commands or queries. These types of attacks can lead to data loss, data corruption, or unauthorized access.

Danger! Injection attacks have been ranked #1 in the OWASP Top 10 for many years and are the most common security vulnerability.

SQL Injection Example

Below you can see insecure and secure code examples:


// Insecure code example - Vulnerable to SQL Injection
username = request.get('username')
password = request.get('password')
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(query)

// Attacker can send the following value:
// username: admin' OR '1'='1' --
// In this case, the query becomes:
// SELECT * FROM users WHERE username = 'admin' OR '1'='1' --' AND password = '...'

// Secure code example - Using parameterized queries
query = "SELECT * FROM users WHERE username = ? AND password = ?"
cursor.execute(query, (username, password))

NoSQL Injection

NoSQL databases like MongoDB can also be vulnerable to injection attacks:


\\ Insecure MongoDB query
const user = db.users.findOne({
    username: req.body.username,
    password: req.body.password
});

\\ Attacker can send the following JSON:
\\ { "username": {"$ne": null}, "password": {"$ne": null} }

\\ Secure usage
const user = db.users.findOne({
    username: String(req.body.username),
    password: String(req.body.password)
});

2. Broken Authentication

When authentication and session management application functions are implemented incorrectly, attackers can impersonate other users or compromise session information.

Warning! Weak password policies, insecure session management, and missing multi-factor authentication fall into this category.

Common Mistakes and Solutions

Problem Risk Solution
Weak password policy Brute force attacks Minimum 12 characters, special characters required
Predictable Session ID Session hijacking Cryptographically secure random IDs
No session timeout Unauthorized access Auto logout after 15-30 minutes of inactivity

Secure Authentication Implementation


import bcrypt
import secrets
from datetime import datetime, timedelta

class SecureAuth:
    def hash_password(self, password):
        // Hash password securely
        salt = bcrypt.gensalt()
        return bcrypt.hashpw(password.encode('utf-8'), salt)
                        
    def verify_password(self, password, hashed):
        // Verify password
        return bcrypt.checkpw(password.encode('utf-8'), hashed)
                        
    def generate_session_token(self):
        // Generate secure session token
        return secrets.token_urlsafe(32)
                        
    def create_session(self, user_id):
        token = self.generate_session_token()
        expiry = datetime.utcnow() + timedelta(hours=2)
                            
        // Save session to database
        session = {
            'token': token,
            'user_id': user_id,
            'created_at': datetime.utcnow(),
            'expires_at': expiry,
            'ip_address': request.remote_addr,
            'user_agent': request.headers.get('User-Agent')
        }
                            
        return session

3. Sensitive Data Exposure

Security vulnerability that occurs when sensitive data (credit card information, personal data, passwords) is not adequately protected.

"Use encryption to protect your data, but also store your encryption keys securely. A chain is only as strong as its weakest link."

Data Protection Principles

  • Data Minimization: Only collect the data you need
  • Encryption: Encrypt data both in transit and at rest
  • Key Management: Store encryption keys securely
  • Data Classification: Know which data is sensitive
Tip: HTTPS usage, database encryption, and secure backup strategies are critical in this regard.

Encryption Implementation Example


from cryptography.fernet import Fernet
import os
import base64

class DataEncryption:
    def __init__(self):
        // Generate or load encryption key
        self.key = self.get_or_create_key()
        self.cipher = Fernet(self.key)
    
    def get_or_create_key(self):
        key_file = 'encryption.key'
        if os.path.exists(key_file):
            with open(key_file, 'rb') as file:
                return file.read()
        else:
            key = Fernet.generate_key()
            with open(key_file, 'wb') as file:
                file.write(key)
            return key
    
    def encrypt_data(self, data):
        if isinstance(data, str):
            data = data.encode()
        encrypted = self.cipher.encrypt(data)
        return base64.urlsafe_b64encode(encrypted).decode()
    
    def decrypt_data(self, encrypted_data):
        encrypted_bytes = base64.urlsafe_b64decode(encrypted_data.encode())
        decrypted = self.cipher.decrypt(encrypted_bytes)
        return decrypted.decode()

// Usage example
encryptor = DataEncryption()
sensitive_data = "Credit Card: 1234-5678-9012-3456"
encrypted = encryptor.encrypt_data(sensitive_data)
print(f"Encrypted: {encrypted}")

decrypted = encryptor.decrypt_data(encrypted)
print(f"Decrypted: {decrypted}")

4. XML External Entities (XXE)

Security vulnerability that occurs when misconfigured XML processors process external entity references.


<"!-- Dangerous XML example --">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>
    <user>&xxe;</user>
</data>

XXE Protection Methods


import defusedxml.ElementTree as ET

// Safe XML parsing
def safe_parse_xml(xml_string):
    try:
        // defusedxml library provides protection against XXE attacks
        tree = ET.fromstring(xml_string)
        return tree
    except ET.ParseError as e:
        logger.error(f"XML parsing error: {e}")
        return None

// Alternative: Use JSON instead of XML
import json

def parse_json_safely(json_string):
    try:
        data = json.loads(json_string)
        // JSON is naturally secure against XXE
        return data
    except json.JSONDecodeError as e:
        logger.error(f"JSON parsing error: {e}")
        return None

5. Broken Access Control

Access control vulnerabilities occur when users can act outside of their intended permissions. This can result in unauthorized information disclosure, modification, or destruction.

Critical! Broken Access Control moved from #5 to #1 in OWASP Top 10 2021, showing its increasing prevalence.

Common Access Control Failures

  • Vertical Privilege Escalation: User gains admin privileges
  • Horizontal Privilege Escalation: User accesses other users' data
  • IDOR (Insecure Direct Object References): Direct access via URL manipulation
  • Missing Function Level Access Control: API endpoints without proper checks

Vulnerable Code Example


// Vulnerable - No access control check
@app.route('/api/user/<int:user_id>/profile')
def get_user_profile(user_id):
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

// Attacker can access any user's profile:
// GET /api/user/123/profile
// GET /api/user/456/profile

// Secure - Proper access control
@app.route('/api/user/<int:user_id>/profile')
@login_required
def get_user_profile(user_id):
    // Check if current user can access this profile
    if current_user.id != user_id and not current_user.is_admin:
        abort(403)  // Forbidden
    
    user = User.query.get_or_404(user_id)
    return jsonify(user.to_dict())

6. Security Misconfiguration

Security misconfiguration occurs when security settings are not properly defined, implemented, or maintained. This includes insecure default configurations, incomplete or ad-hoc configurations, open cloud storage, misconfigured HTTP headers.

Common Misconfigurations

Component Misconfiguration Impact
Web Server Default credentials, unnecessary services enabled Full server compromise
Database Default passwords, open to internet Data breach
Cloud Storage Public read/write permissions Data exposure
HTTP Headers Missing security headers XSS, Clickjacking attacks

Security Headers Implementation


from flask import Flask, make_response

app = Flask(__name__)

@app.after_request
def set_security_headers(response):
    // Prevent XSS attacks
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    
    // HTTPS enforcement
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    
    // Content Security Policy
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    
    // Referrer Policy
    response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
    
    return response

// Environment-specific configuration
class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key-change-in-production'
    DEBUG = False
    TESTING = False

class DevelopmentConfig(Config):
    DEBUG = True

class ProductionConfig(Config):
    // Never use debug mode in production!
    DEBUG = False
    // Use strong secret key from environment
    SECRET_KEY = os.environ.get('SECRET_KEY')
    if not SECRET_KEY:
        raise ValueError("No SECRET_KEY set for production!")

7. Cross-Site Scripting (XSS)

XSS flaws occur when an application includes untrusted data in a new web page without proper validation or escaping. XSS allows attackers to execute scripts in the victim's browser which can hijack user sessions, deface web sites, or redirect users.

Impact: XSS can lead to session hijacking, account takeover, defacement, and malware distribution.

Types of XSS

  • Reflected XSS: Malicious script echoed back from web server
  • Stored XSS: Malicious script stored in database/file
  • DOM-based XSS: Vulnerability exists in client-side code

XSS Examples and Prevention


\\ Vulnerable code - Reflected XSS
app.get('/search', (req, res) => {
    const query = req.query.q;
    res.send(`<h1>Search results for: ${query}</h1>`);
    \\ Attacker can use: /search?q=<script>alert('XSS')</script>
});

\\ Secure code - Proper escaping
const escapeHtml = (unsafe) => {
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
};

app.get('/search', (req, res) => {
    const query = escapeHtml(req.query.q);
    res.send(`<h1>Search results for: ${query}</h1>`);
});

\\ Using template engine with auto-escaping (Recommended)
app.set('view engine', 'ejs');
app.get('/search', (req, res) => {
    res.render('search', { query: req.query.q }); \\ EJS auto-escapes by default \\
});

Client-Side XSS Prevention


\\ Vulnerable DOM manipulation
function displayUserComment(comment) {
    document.getElementById('comments').innerHTML += '<div>' + comment + '</div>';
}

\\ Secure DOM manipulation
function displayUserCommentSafe(comment) {
    const div = document.createElement('div');
    div.textContent = comment; \\textContent automatically escapes\\
    document.getElementById('comments').appendChild(div);
}

\\ Input validation and sanitization
function sanitizeInput(input) {
    return input
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#x27;');
}

\\ Modern framework safe patterns
function createSafeElement(tag, content) {
    const element = document.createElement(tag);
    element.textContent = content; \\Safe from XSS\\
    return element;
}

8. Insecure Deserialization

Insecure deserialization often leads to remote code execution. Even if deserialization flaws do not result in remote code execution, they can be used to perform attacks including replay attacks, injection attacks, and privilege escalation.

High Risk! Successful exploitation of deserialization flaws can result in complete system compromise.

Vulnerable Deserialization Example


import pickle
import base64

// Vulnerable code - Never deserialize untrusted data!
def load_user_session(session_data):
    try:
        // Dangerous! Can execute arbitrary code
        session = pickle.loads(base64.b64decode(session_data))
        return session
    except:
        return None

// Attacker can create malicious pickle payload:
class MaliciousPayload:
    def __reduce__(self):
        import os
        return (os.system, ('rm -rf /',))  // Dangerous payload

// Safe alternatives
import json
import hmac
import hashlib

class SecureSessionManager:
    def __init__(self, secret_key):
        self.secret_key = secret_key
    
    def create_session(self, user_data):
        // Use JSON for safe serialization
        session_json = json.dumps(user_data)
        
        // Create HMAC signature
        signature = hmac.new(
            self.secret_key.encode(),
            session_json.encode(),
            hashlib.sha256
        ).hexdigest()
        
        return base64.b64encode(f"{session_json}:{signature}".encode())
    
    def load_session(self, session_data):
        try:
            decoded = base64.b64decode(session_data).decode()
            session_json, signature = decoded.rsplit(':', 1)
            
            // Verify signature
            expected_sig = hmac.new(
                self.secret_key.encode(),
                session_json.encode(),
                hashlib.sha256
            ).hexdigest()
            
            if not hmac.compare_digest(signature, expected_sig):
                return None
            
            return json.loads(session_json)
        except:
            return None

9. Using Components with Known Vulnerabilities

Components such as libraries, frameworks, and other software modules run with the same privileges as the application. If a vulnerable component is exploited, such an attack can facilitate serious data loss or server takeover.

Component Security Best Practices

  • Inventory Management: Maintain a complete inventory of all components
  • Regular Updates: Keep all components up to date
  • Vulnerability Scanning: Regularly scan for known vulnerabilities
  • Security Monitoring: Monitor security advisories for your components
  • Remove Unused Components: Remove unnecessary dependencies

Automated Vulnerability Scanning


// Python - Check for known vulnerabilities
pip install safety
safety check

// Alternative: Use pip-audit
pip install pip-audit
pip-audit

// Node.js - Check for vulnerabilities
npm audit
npm audit fix

// Generate detailed report
npm audit --json > audit-report.json

// Java - Use OWASP Dependency Check
mvn org.owasp:dependency-check-maven:check

// Docker - Scan container images
docker run --rm -v $(pwd):/app clair-scanner:latest

Dependency Management Example


// GitHub Actions - Automated dependency scanning
name: Security Scan
on: [push, pull_request]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v1
      with:
        sarif_file: 'trivy-results.sarif'

// requirements.txt with version pinning
Flask==2.3.2  /* Pin to specific secure version*/
Werkzeug==2.3.4
Jinja2==3.1.2
// Avoid using outdated versions like Flask==1.0.0

10. Insufficient Logging & Monitoring

Insufficient logging and monitoring, coupled with missing or ineffective integration with incident response, allows attackers to further attack systems, maintain persistence, pivot to more systems, and tamper, extract, or destroy data.

Detection Time: The average time to detect a breach is over 200 days. Proper logging and monitoring can significantly reduce this time.

What to Log

  • Authentication Events: Login attempts, password changes
  • Authorization Failures: Access denied events
  • High-Value Transactions: Financial transactions, data exports
  • Security Events: Failed validation, suspicious patterns
  • System Events: Application start/stop, configuration changes

Secure Logging Implementation


import logging
import json
from datetime import datetime
from flask import request, g

// Configure structured logging
logging.basicConfig(
    level=logging.INFO,
    format='%(message)s'
)

class SecurityLogger:
    def __init__(self):
        self.logger = logging.getLogger('security')
        handler = logging.FileHandler('security.log')
        handler.setFormatter(logging.Formatter('%(message)s'))
        self.logger.addHandler(handler)
    
    def log_security_event(self, event_type, user_id=None, ip_address=None, 
                          details=None, severity='INFO'):
        log_entry = {
            'timestamp': datetime.utcnow().isoformat(),
            'event_type': event_type,
            'user_id': user_id,
            'ip_address': ip_address or request.remote_addr,
            'user_agent': request.headers.get('User-Agent'),
            'severity': severity,
            'details': details
        }
        
                self.logger.info(json.dumps(log_entry))

// Usage examples
security_logger = SecurityLogger()

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    
    user = authenticate(username, password)
    if user:
        security_logger.log_security_event(
            'LOGIN_SUCCESS',
            user_id=user.id,
            details={'username': username}
        )
        return jsonify({'status': 'success'})
    else:
        security_logger.log_security_event(
            'LOGIN_FAILURE',
            details={'username': username, 'reason': 'invalid_credentials'},
            severity='WARNING'
        )
        return jsonify({'error': 'Invalid credentials'}), 401

@app.route('/admin/users/<int:user_id>')
@admin_required
def get_user(user_id):
    if not current_user.is_admin:
        security_logger.log_security_event(
            'UNAUTHORIZED_ACCESS_ATTEMPT',
            user_id=current_user.id,
            details={'attempted_resource': f'/admin/users/{user_id}'},
            severity='HIGH'
        )
        abort(403)
    
    // Log high-privilege access
    security_logger.log_security_event(
        'ADMIN_ACCESS',
        user_id=current_user.id,
        details={'accessed_user': user_id}
    )
    
    return jsonify(User.query.get(user_id).to_dict())

Log Analysis with ELK Stack


// docker-compose.yml for ELK Stack
version: '3.7'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.14.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"

  logstash:
    image: docker.elastic.co/logstash/logstash:7.14.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:7.14.0
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

// logstash.conf
input {
  file {
    path => "/var/log/security.log"
    start_position => "beginning"
    codec => "json"
  }
}

filter {
  if [severity] == "HIGH" or [severity] == "CRITICAL" {
    mutate {
      add_tag => ["alert"]
    }
  }
  
  // Parse IP geolocation
  geoip {
    source => "ip_address"
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "security-logs-%{+YYYY.MM.dd}"
  }
}

Real-time Monitoring Alerts


import smtplib
from email.mime.text import MIMEText
from collections import defaultdict
from datetime import datetime, timedelta

class SecurityMonitor:
    def __init__(self):
        self.failed_attempts = defaultdict(list)
        self.alert_threshold = 5  *5 failed attempts
        self.time_window = timedelta(minutes=10)
    
    def check_brute_force(self, ip_address, event_type):
        if event_type == 'LOGIN_FAILURE':
            now = datetime.utcnow()
            
            // Clean old attempts
            self.failed_attempts[ip_address] = [
                attempt for attempt in self.failed_attempts[ip_address]
                if now - attempt < self.time_window
            ]
            
            // Add current attempt
            self.failed_attempts[ip_address].append(now)
            
            // Check if threshold exceeded
            if len(self.failed_attempts[ip_address]) >= self.alert_threshold:
                self.send_alert(
                    f"Brute force attack detected from IP: {ip_address}",
                    f"Multiple failed login attempts detected"
                )
                return True
        return False
    
    def send_alert(self, subject, message):
        // Send email alert
        msg = MIMEText(message)
        msg['Subject'] = f"[SECURITY ALERT] {subject}"
        msg['From'] = '[email protected]'
        msg['To'] = '[email protected]'
        
        // Also log the alert
        security_logger.log_security_event(
            'SECURITY_ALERT_SENT',
            details={'subject': subject, 'message': message},
            severity='CRITICAL'
        )

// Integration with Flask app
monitor = SecurityMonitor()

@app.after_request
def security_monitoring(response):
    if hasattr(g, 'security_event'):
        event = g.security_event
        monitor.check_brute_force(
            request.remote_addr, 
            event.get('event_type')
        )
    return response
Congratulations! You've now learned all 10 items of the OWASP Top 10. These security principles form the foundation of secure web application development. Remember: security is not a feature to be added later, but a mindset to be maintained throughout development.

Summary and Next Steps

The OWASP Top 10 represents the most critical web application security risks. As a security professional, you should:

  • Stay Updated: OWASP Top 10 is updated every 3-4 years
  • Implement Defense in Depth: Use multiple layers of security
  • Security Testing: Regular penetration testing and code reviews
  • Developer Training: Educate development teams on secure coding
  • Incident Response: Have a plan ready for when attacks happen
"Security is a process, not a product. It's about good practices, awareness, and continuous improvement." - Bruce Schneier

About the Author

I'm developing myself as a Penetration Tester and Red Team member. While gaining new experiences in web application security, network security, and cloud security topics, I actively solve CTFs on TryHackMe and HackTheBox platforms. This comprehensive OWASP Top 10 guide is part of my effort to share knowledge and help the cybersecurity community build more secure applications.

Comments & Discussion

Sarah Johnson
3 days ago

Excellent comprehensive guide! The code examples are particularly helpful. I've bookmarked this for reference during our security code reviews. The logging section (#10) is often overlooked but so important.

Mike Chen
1 week ago

Great work on covering all 10 vulnerabilities with practical examples. The Broken Access Control section really helped me understand IDOR attacks better. Would love to see a follow-up on OWASP API Security Top 10!