Sensitive Data Exposure occurs when applications fail to adequately protect sensitive information such as passwords, credit cards, personal data, or authentication tokens. In this demo, the vulnerable version exposes plaintext passwords, returns unnecessary sensitive fields in queries, and lacks proper data protection mechanisms.
# VULNERABLE: Password in SQL queryquery = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"print(f"Query ejecutada: {query}") # Logged!
Console Output:
Query ejecutada: SELECT * FROM users WHERE username = 'admin' AND password = 'admin123'
Exposure Vectors:
Application logs
Database query logs
Monitoring systems
Error messages
Console output
3. Excessive Data in Responses
Vulnerable Code (vulnerable/app.py:101):
# VULNERABLE: Returns ALL fields including passwordcursor.execute(f"SELECT * FROM users WHERE id = {user_id}")user = cursor.fetchone()return render_template('profile.html', user=user)
Tests passwords on user emails (Gmail, corporate, etc.)
~65% success rate due to password reuse
Gains access to external accounts
Impact: Complete breach across multiple services
2
Scenario 2: SQL Injection Data Exfiltration
Attack: Combine SQL injection with data exposure
# Exploit the SQLi vulnerabilitypayload = "' UNION SELECT id, username, password, email, role, created_at FROM users --"# Results displayed on screen# All user records with plaintext passwords
Via Profile IDOR (vulnerable/app.py:101):
# Access any user profileGET /profile?id=1# Response includes password field<div>Password: admin123</div> # If accidentally rendered
3
Scenario 3: Log File Analysis
Vulnerable Logging (vulnerable/app.py:27):
print(f"Query ejecutada: {query}")# Logs: SELECT * FROM users WHERE username = 'admin' AND password = 'Secret123!'
Attacker Access to Logs:
Compromised server
Log aggregation service
Shared hosting environment
Misconfigured log permissions
Result: Passwords in plaintext in log files
4
Scenario 4: Session Hijacking + Data Access
Attack Flow:
Steal session via XSS: document.cookie
Hijack user session
Access profile page
View exposed sensitive data
Extract additional user information
Amplification: IDOR allows accessing other users’ data too
class DataClassification: PUBLIC = 0 INTERNAL = 1 CONFIDENTIAL = 2 RESTRICTED = 3# Mark fieldsclass User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), classification=DataClassification.INTERNAL) email = db.Column(db.String(100), classification=DataClassification.CONFIDENTIAL) password_hash = db.Column(db.String(255), classification=DataClassification.RESTRICTED)
2. Principle of Least Privilege
Query Only What You Need:
# Bad: SELECT * returns everythingquery = "SELECT * FROM users WHERE id = %s"# Good: Explicit field listquery = "SELECT id, username, email FROM users WHERE id = %s"# Better: Context-specific viewsdef get_user_for_profile(user_id): return db.execute( "SELECT id, username, email, created_at FROM users WHERE id = %s", (user_id,) ).fetchone()def get_user_for_authentication(username): return db.execute( "SELECT id, username, password_hash, role FROM users WHERE username = %s", (username,) ).fetchone()
from datetime import datetime, timedeltaclass DataRetentionPolicy: # Define retention periods RETENTION_PERIODS = { 'session_logs': timedelta(days=30), 'login_attempts': timedelta(days=90), 'inactive_accounts': timedelta(days=365), 'deleted_accounts': timedelta(days=30) # Grace period } @staticmethod def cleanup_old_data(): """Scheduled job to remove old data""" now = datetime.now() # Delete old session logs cutoff = now - DataRetentionPolicy.RETENTION_PERIODS['session_logs'] db.execute("DELETE FROM session_logs WHERE created_at < %s", (cutoff,)) # Delete inactive accounts cutoff = now - DataRetentionPolicy.RETENTION_PERIODS['inactive_accounts'] db.execute( "DELETE FROM users WHERE last_login < %s AND deleted_at IS NULL", (cutoff,) ) # Permanently delete accounts after grace period cutoff = now - DataRetentionPolicy.RETENTION_PERIODS['deleted_accounts'] db.execute("DELETE FROM users WHERE deleted_at < %s", (cutoff,)) @staticmethod def delete_user_data(user_id): """Securely delete all user data (GDPR right to erasure)""" # Soft delete first (grace period) db.execute( "UPDATE users SET deleted_at = %s, email = NULL, password_hash = NULL WHERE id = %s", (datetime.now(), user_id) ) # Delete related data db.execute("DELETE FROM user_sessions WHERE user_id = %s", (user_id,)) db.execute("DELETE FROM user_logs WHERE user_id = %s", (user_id,))# Schedule cleanupfrom apscheduler.schedulers.background import BackgroundSchedulerscheduler = BackgroundScheduler()scheduler.add_job(DataRetentionPolicy.cleanup_old_data, 'cron', hour=2) # 2 AM dailyscheduler.start()
# 1. Inspect databasesqlite3 users.dbSELECT * FROM users;# Check if passwords are hashed# 2. Check API responsescurl https://target.com/api/user/1 | jq .# Look for password, SSN, tokens in response# 3. Inspect HTML sourcecurl https://target.com/profile | grep -i password# Check if sensitive data in HTML comments or hidden fields# 4. Check browser storage# Open DevTools > Application > Storage# Look for sensitive data in localStorage, sessionStorage, cookies# 5. Monitor network traffic# Use browser DevTools > Network# Check if HTTPS enforced# Look for sensitive data in URLs or headers
import requestsimport jsondef scan_for_data_exposure(base_url, session_cookie): """Scan for sensitive data exposure""" findings = [] # Test user profile endpoint headers = {'Cookie': f'session={session_cookie}'} r = requests.get(f'{base_url}/profile', headers=headers) # Check for sensitive keywords in response sensitive_fields = ['password', 'ssn', 'credit_card', 'token', 'secret'] for field in sensitive_fields: if field in r.text.lower(): findings.append(f'Potential {field} exposure in profile page') # Test if password returned in API r = requests.get(f'{base_url}/api/user/me', headers=headers) try: data = r.json() if 'password' in data: findings.append('CRITICAL: Password field in API response') except: pass # Check for HTTPS enforcement r = requests.get(base_url.replace('https://', 'http://'), allow_redirects=False) if r.status_code != 301: findings.append('HTTP not redirected to HTTPS') return findings
import redef scan_logs_for_sensitive_data(log_file): """Scan application logs for accidentally logged sensitive data""" patterns = { 'passwords': r'password["\']?\s*[:=]\s*["\']?([^"\',\s]+)', 'emails': r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', 'ssn': r'\b\d{3}-\d{2}-\d{4}\b', 'credit_card': r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b', 'api_keys': r'[A-Za-z0-9]{32,}', } findings = {key: [] for key in patterns} with open(log_file, 'r') as f: for line_num, line in enumerate(f, 1): for data_type, pattern in patterns.items(): if re.search(pattern, line): findings[data_type].append({ 'line': line_num, 'content': line.strip()[:100] }) return findings