OWASP Top 10 attack vectors, risks, and tools explained

·

What’s OWASP briefly

OWASP stands for Open Web Application Security Project, a non-profit organization that’s focused on web application security standards, tools, and methodologies. OWASP top 10 in its turn stands for Top-10 major and wide-spread security risks of web applications (doesn’t matter backend or frontend ones).

OWASP is a registered trademark of the OWASP foundation

Injections

An injection is an execution of a malicious statement forged for a specific system by a user, where the vulnerability is only possible because the service allows any information to be put in the system (database, OS, etc) without limitations.

Injection example

The classic example of SQL injection is the “Bobby DROP TABLE” meme, where a boy named 

Robert’); DROP TABLE students;– destroys the school’s database merely by the power of its name.

INSERT INTO students (name) VALUES ('Robert');
-- turns into
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE students;--');

What does this SQL injection mean?

The data sent through some school forms weren’t checked or parametrized and were executed by SQL database in a raw, pristine way. Robert’s name, consisting of special characters, ended a previous command and executed a new one `DROP TABLE ‘students’ destroying the entire database and, perhaps, Robert’s bad education results, too.

You can see that the command imitates a normal SQL syntax, so it’s executed unnoticed. It’s not necessarily a real Robert’s name or performed as a direct SQL expression, but could be an attack carried through an HTTP request. Let’s take a look at the cURL example:

curl -X PUT -d '{"name": "Robert\'); DROP TABLE students;--"}' https://school.com/user/Robert
// Notice that the quotation marks are escaped in the request's body

Injection attack vector

An injection may involve any system that works with external data and has access to internal data:

  • SQL and NoSQL
  • XML and JSON parsers
  • Command-line/OS commands
  • LDAP
  • XPath
  • SMTP headers
  • Expression languages
  • ORM queries (sometimes)
  • Custom macros

Despite the attack may come from any user or service input, the main source of vulnerability is API requests. Users can modify HTTP requests and add special characters and commands resulting in data breaches, system failure, etc.

Injection tools

Must have injection tools:

  • sqlmap is the most popular open-source penetration tool that detects and exploits possible SQL injection. When the target is cracked, the tool provides features to take over the database
  • DSSS is a compact SQLi scanner that has all the required features
  • NoSQLMap is abusing NoSQL databases such as MongoDB or CouchDB, allows exploring vulnerabilities and eventually copy the data
  • bbqsql is an automated blind SQL injection framework

Tools that are no longer maintained but still might be used for education purposes:

  • zeus-scanner is an advanced URL explorer that powers up sqlmap and provides other reconnaissance features
  • whitewidow see ‘zeus-scanner’, but without ‘URL’ part

How to prevent Injection

  • Parametrize your API: you must know exactly what you expect from your users
  • Prefer ORMs to libraries with more freedom
  • Don’t use dynamic queries
  • If you have to, escape special characters
  • Whitelist commands that are intended to be executed by OS, but dynamically selected by users
  • Don’t blacklist commands, it never works as attacks become more and more sophisticated

For instance, a company has a service that allows an image to be scaled, cropped, filtered, etc

curl -X POST https://company.com/images/{imageId}/crop
curl -X POST https://company.com/images/{imageId}/scale

As you see, this API has at least 2 attack vectors: 

imageId and 

crop | scale

{imageId} may include harmful statements like

SELECT * FROM images WHERE image.id = {imageId}

If 

imageId contains a special character, an attacker can execute any command, as in Booby’s example. In the case of IDs, you need to check that the ID consists of only numbers, characters, or special symbols you are safe with. You cannot whitelist all possible IDs, but you can check if the ID follows the structure you expect.

crop | scale has to be simply whitelisted, any attempt to call

curl -X POST https://company.com/images/{imageId}/;)

must end on API router side as in the Node.js example:

app.post('/images/:imageId/(crop|scale)',  ( request, response ) => {
       if (validate(request.params.imageId)) {
          // ...
       }
    }
)

Broken Authentication and Session Management

Another common OWASP attack vector is everything related to incorrectly configured and/or implemented authentication. Despite it being called ‘broken’ it doesn’t really need to be one, the authentication is just not good enough. This kind of vulnerability implies that the system has an authentication process that involves the usage of login and password in any form.

At the moment when this article was published, most of the world’s passwords are still on the list

Broken Authentication example

An attacker has a list of users and a ‘most popular passwords’ dictionary. As a result, some users and passwords match, therefore, the attacker gets to secure part of the web application using stolen identities.

Broken Authentication attack vector

  • An attacker knows that the login form requires the user’s login & password, then clearly shows that the user exists in the database, but the entered password is incorrect
  • An attacker can use the login form without a two-factor authentication option
  • An attacker knows that systems’ passwords are weak because the sign-up form doesn’t check the password’s complexity
  • Admin credentials are the default ones for the systems involved (e.g., WordPress with a default password “password” and username “admin”), therefore, an attacker can use them

How to prevent authentication breaking

  • Always use 2FA or at least provide a simple way to use one
  • Change the default credentials of the libraries you use
  • Use logs for failed login attempts and use automated software to analyze suspicious ones
  • Never explain why a pair of login and password doesn’t work, ‘Incorrect login or password’ is good enough in 100% of cases
  • Use highly entropy session tokens & don’t use them in URL’s pathname or query
  • Limit life of session tokens, use authentication & refresh tokens
  • Follow NIST password policy for passwords you allow users to create
  • Check passwords using Have I been pwned or another popular password dictionary. “Have I been pwned” has just gone open source and has libraries for multiple languages
  • Artificially delay login responses by a random number of seconds

Exposure of Sensitive Data

The simplest way to expose data is to provide it to an attacker straight away. It might be an open API, unprotected port, or data that’s not encrypted or encrypted with weak algorithms. This vulnerability occurs when applications don’t adequately protect sensitive information such as financial data, healthcare records, personal information, or authentication credentials.

Sensitive Data Exposure Examples

Unencrypted Database Storage:

-- Vulnerable: Storing passwords in plain text
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100),  -- Stored as plain text!
    ssn VARCHAR(11)         -- Social Security Number in plain text
);

INSERT INTO users VALUES (1, 'john_doe', 'mypassword123', '123-45-6789');

Weak Encryption Implementation:

 
// Vulnerable: Using deprecated MD5 for password hashing
const crypto = require('crypto');
const password = 'user_password';
const hash = crypto.createHash('md5').update(password).digest('hex');

Data Transmitted Over HTTP:

# Vulnerable: Sending sensitive data over unencrypted connection
curl -X POST http://bankapi.com/transfer \
  -d '{"account": "12345", "amount": 10000, "to": "67890"}'

Sensitive Data Exposure Attack Vectors

  • Man-in-the-Middle attacks on unencrypted HTTP connections
  • Database breaches exposing unencrypted stored data
  • Backup files containing sensitive information left on web servers
  • Application logs containing passwords, API keys, or personal data
  • Browser storage (localStorage, sessionStorage) with sensitive data
  • URL parameters containing authentication tokens or personal information
  • Error messages revealing database schemas or internal system information

How to Prevent Sensitive Data Exposure

Encrypt Data at Rest:

 
 
python
# Secure: Using bcrypt for password hashing
import bcrypt

password = "user_password".encode('utf-8')
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password, salt)

# Verify password
if bcrypt.checkpw(password, hashed):
    print("Password matches!")

Use HTTPS Everywhere:

 
# Nginx configuration forcing HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    ssl_protocols TLSv1.2 TLSv1.3;
}

Proper Key Management:

  • Use environment variables or secure vaults for API keys
  • Rotate encryption keys regularly
  • Never hardcode secrets in source code
  • Implement proper access controls for sensitive data

XML External Entities (XXE)

XXE attacks occur when a weakly configured XML parser processes XML input containing a reference to an external entity. This vulnerability can lead to the disclosure of confidential data, denial of service, server-side request forgery, and other system impacts.

XXE Attack Example

Vulnerable XML Processing:

 
<!-- Malicious XML payload -->
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>

Vulnerable PHP Code:

// Vulnerable: XML parser with external entities enabled
$xml = file_get_contents('user_input.xml');
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);
echo $dom->textContent; // This could output /etc/passwd content

XXE Attack Vectors

  • File disclosure through SSRF attacks using protocols
  • Port scanning of the internal network
  • Denial of service through entity expansion attacks
  • Remote code execution in some configurations

XXE Prevention Methods

Disable External Entities:

// Secure XML parsing in Java
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(inputStream);

Use Safe Parsing Libraries:

# Secure XML parsing in Python
from defusedxml import ElementTree as ET

# This automatically disables dangerous features
tree = ET.parse('input.xml')
root = tree.getroot()

Broken Access Control

Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of data, or performing business functions outside the user’s limits.

Broken Access Control Examples

Insecure Direct Object Reference:

// Vulnerable: No authorization check
app.get('/api/accounts/:accountId', (req, res) => {
    const accountId = req.params.accountId;
    const account = database.getAccount(accountId);
    res.json(account); // Any user can access any account!
});

Missing Function Level Access Control:

// Vulnerable: Admin function accessible to all users
if ($_SESSION['logged_in']) {
    // Missing role check!
    if ($_GET['action'] == 'delete_user') {
        deleteUser($_GET['user_id']);
    }
}

Access Control Attack Vectors

  • Vertical privilege escalation – accessing admin functions as regular user
  • Horizontal privilege escalation – accessing other users’ data
  • Bypassing access control through URL manipulation
  • Force browsing to unauthorized pages
  • CORS misconfiguration allowing unauthorized cross-origin requests

Access Control Prevention

Implement Proper Authorization:

// Secure: Check both authentication and authorization
app.get('/api/accounts/:accountId', authenticateUser, (req, res) => {
    const accountId = req.params.accountId;
    const userId = req.user.id;
    
    // Check if user owns this account or is admin
    if (!userOwnsAccount(userId, accountId) && !req.user.isAdmin) {
        return res.status(403).json({ error: 'Access denied' });
    }
    
    const account = database.getAccount(accountId);
    res.json(account);
});
Role-Based Access Control (RBAC):
# Secure: Decorator-based access control
from functools import wraps

def require_role(required_role):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.has_role(required_role):
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/admin/users')
@require_role('admin')
def admin_users():
    return render_template('admin_users.html')

Security Misconfiguration

Security misconfiguration is commonly a result of insecure default configurations, incomplete configurations, open cloud storage, misconfigured HTTP headers, and verbose error messages containing sensitive information.

Security Misconfiguration Examples

Default Credentials:

# Vulnerable: Using default database credentials
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=password  # Default password!
DB_NAME=production

Exposed Debug Information:

# Vulnerable: Debug mode enabled in production
from flask import Flask

app = Flask(__name__)
app.config['DEBUG'] = True  # Exposes sensitive information!

@app.route('/')
def home():
    1/0  # This will show full stack trace to users

Missing Security Headers:

HTTP/1.1 200 OK
Content-Type: text/html
# Missing security headers:
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff  
# X-XSS-Protection: 1; mode=block
# Strict-Transport-Security: max-age=31536000

Security Misconfiguration Prevention

Secure Configuration Management:

# Secure Docker configuration
version: '3.8'
services:
  webapp:
    image: myapp:latest
    environment:
      - NODE_ENV=production
      - DEBUG=false
    secrets:
      - db_password
    user: "1001:1001"  # Non-root user
    
secrets:
  db_password:
    external: true
Implement Security Headers:
 
// Secure: Adding security headers middleware
const helmet = require('helmet');

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

Cross-Site Scripting (XSS)

XSS flaws occur when an application includes untrusted data in a web page without proper validation or escaping, allowing attackers to execute malicious scripts in victims’ browsers.

XSS Attack Types and Examples

Stored XSS:

// Vulnerable: Directly inserting user content
app.post('/comments', (req, res) => {
    const comment = req.body.comment;
    database.saveComment(comment); // Malicious script stored in DB
});

// Later displayed without escaping:
comments.forEach(comment => {
    html += `<div>${comment.text}</div>`; // XSS executed here!
});

Reflected XSS:

// Vulnerable: Reflecting user input without sanitization
$search = $_GET['search'];
echo "<p>You searched for: " . $search . "</p>";
// URL: site.com/search?search=<script>alert('XSS')</script>

DOM-based XSS:

// Vulnerable: Client-side DOM manipulation
const hash = window.location.hash;
document.getElementById('content').innerHTML = hash.substring(1);
// URL: site.com#<img src=x onerror=alert('XSS')>
 

XSS Prevention Methods

Output Encoding:

// Secure: Proper escaping of user content
const escapeHtml = (unsafe) => {
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "'");
};

// Usage
const safeComment = escapeHtml(userComment);
html += `<div>${safeComment}</div>`;

Content Security Policy (CSP):

Content-Security-Policy: default-src 'self'; 
                        script-src 'self' 'unsafe-inline'; 
                        object-src 'none';
                        base-uri 'self';

Input Validation:

# Secure: Validate and sanitize input
import bleach

allowed_tags = ['p', 'strong', 'em', 'br']
allowed_attributes = {}

def sanitize_html(content):
    return bleach.clean(content, tags=allowed_tags, attributes=allowed_attributes)

clean_content = sanitize_html(user_input)

Insecure Deserialization

Insecure deserialization often leads to remote code execution. Even if deserialization flaws don’t result in remote code execution, they can lead to replay attacks, injection attacks, and privilege escalation.

Insecure Deserialization Example

Vulnerable Python Pickle:

import pickle
import base64

# Vulnerable: Deserializing untrusted data
user_data = request.get('user_data')  # From cookie or POST data
decoded_data = base64.b64decode(user_data)
user_object = pickle.loads(decoded_data)  # Dangerous!

Malicious Payload:

# Attacker creates malicious payload
import pickle
import os

class MaliciousPayload:
    def __reduce__(self):
        return (os.system, ('rm -rf /',))

# Serialize malicious object
malicious_data = pickle.dumps(MaliciousPayload())
encoded_payload = base64.b64encode(malicious_data)

Insecure Deserialization Prevention

Use Safe Serialization Formats:

# Secure: Use JSON instead of pickle
import json

# Safe serialization
user_data = {
    'username': 'john_doe',
    'preferences': {'theme': 'dark'}
}
safe_data = json.dumps(user_data)

# Safe deserialization  
parsed_data = json.loads(safe_data)

Implement Integrity Checks:

# Secure: Sign serialized data
import hmac
import hashlib
import json

SECRET_KEY = 'your-secret-key'

def sign_data(data):
    json_data = json.dumps(data)
    signature = hmac.new(
        SECRET_KEY.encode(),
        json_data.encode(),
        hashlib.sha256
    ).hexdigest()
    return f"{json_data}.{signature}"

def verify_data(signed_data):
    try:
        data, signature = signed_data.rsplit('.', 1)
        expected_sig = hmac.new(
            SECRET_KEY.encode(),
            data.encode(),
            hashlib.sha256
        ).hexdigest()
        
        if hmac.compare_digest(signature, expected_sig):
            return json.loads(data)
        return None
    except ValueError:
        return None

Using Components with Known Vulnerabilities

Many applications use libraries, frameworks, and modules with known vulnerabilities. Attackers can exploit these vulnerabilities to compromise the application.

Vulnerable Components Example

Outdated Dependencies: 

{
  "name": "my-app",
  "dependencies": {
    "express": "4.16.0",     // Known vulnerabilities
    "lodash": "4.17.4",      // Known vulnerabilities  
    "mongoose": "4.13.0",    // Known vulnerabilities
    "jquery": "2.1.4"        // Multiple XSS vulnerabilities
  }
}

Using Vulnerable Component:

// Vulnerable: Using lodash template with user input
const _ = require('lodash');

app.post('/template', (req, res) => {
    const template = req.body.template;
    const compiled = _.template(template); // RCE vulnerability in old versions
    const result = compiled({ name: 'World' });
    res.send(result);
});

Component Vulnerability Prevention

Dependency Scanning:

# Use npm audit to check for vulnerabilities
npm audit

# Fix vulnerabilities automatically
npm audit fix

# Use tools like Snyk
npx snyk test
npx snyk wizard

Keep Dependencies Updated:

# Check for outdated packages
npm outdated

# Update to latest versions
npm update

# Use package-lock.json to ensure consistent versions
npm ci  # Instead of npm install in production

Implement Dependency Management:

# GitHub Dependabot configuration
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    reviewers:
      - "security-team"
    assignees:
      - "dev-team"

Insufficient Logging and Monitoring

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

Insufficient Logging Examples

Missing Security Events:

// Vulnerable: No logging of security events
app.post('/login', (req, res) => {
    const { username, password } = req.body;
    
    if (validateCredentials(username, password)) {
        req.session.user = username;
        res.json({ success: true });
    } else {
        res.status(401).json({ error: 'Invalid credentials' });
        // No logging of failed login attempt!
    }
});

Inadequate Log Information:

# Vulnerable: Generic logging without context
import logging

def transfer_money(from_account, to_account, amount):
    try:
        result = process_transfer(from_account, to_account, amount)
        logging.info("Transfer completed")  # Not enough information!
    except Exception as e:
        logging.error("Transfer failed")    # No details about the failure!

Proper Logging and Monitoring Implementation

Security Event Logging:

// Secure: Comprehensive security logging
const winston = require('winston');

const securityLogger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({ filename: 'security.log' }),
        new winston.transports.Console()
    ]
});

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const clientIP = req.ip;
    const userAgent = req.get('User-Agent');
    
    if (validateCredentials(username, password)) {
        securityLogger.info('Login success', {
            event: 'login_success',
            username: username,
            ip: clientIP,
            userAgent: userAgent,
            timestamp: new Date().toISOString()
        });
        
        req.session.user = username;
        res.json({ success: true });
    } else {
        securityLogger.warn('Login failure', {
            event: 'login_failure',
            username: username,
            ip: clientIP,
            userAgent: userAgent,
            timestamp: new Date().toISOString()
        });
        
        res.status(401).json({ error: 'Invalid credentials' });
    }
});

Monitoring and Alerting:

# Secure: Monitoring with alerts
import logging
from datetime import datetime, timedelta
from collections import defaultdict

class SecurityMonitor:
    def __init__(self):
        self.failed_logins = defaultdict(list)
    
    def log_failed_login(self, username, ip_address):
        timestamp = datetime.now()
        
        # Log the event
        logging.warning(f"Failed login attempt", extra={
            'event_type': 'failed_login',
            'username': username,
            'ip_address': ip_address,
            'timestamp': timestamp.isoformat()
        })
        
        # Track for rate limiting
        self.failed_logins[ip_address].append(timestamp)
        
        # Clean old entries
        cutoff = timestamp - timedelta(minutes=15)
        self.failed_logins[ip_address] = [
            t for t in self.failed_logins[ip_address] if t > cutoff
        ]
        
        # Alert on suspicious activity
        if len(self.failed_logins[ip_address]) > 5:
            self.send_security_alert(f"Multiple failed logins from {ip_address}")
    
    def send_security_alert(self, message):
        # Integration with alerting system (email, Slack, PagerDuty, etc.)
        logging.critical(f"SECURITY ALERT: {message}")

Essential Security Monitoring Checklist:

  • Authentication events (login, logout, failed attempts)
  • Authorization failures
  • Input validation failures
  • Application errors and exceptions
  • Admin privilege usage
  • Data access and modification
  • Network anomalies
  • File integrity changes