Skip to main content
Educational Purpose Only - Exposing sensitive data violates privacy laws and puts users at risk. Always protect user information properly.

Overview

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.

Types of Sensitive Data Exposure

Vulnerable Code (vulnerable/database.py:32-40):
# VULNERABLE: Plaintext passwords in database
cursor.execute("""
    INSERT INTO users (username, password, email, role) 
    VALUES (?, ?, ?, ?)
""", ('admin', 'admin123', '[email protected]', 'admin'))
Database Content:
idusernamepasswordemail
1adminadmin123[email protected]
2usuariopassword123[email protected]
Impact:
  • Database backup = all passwords exposed
  • SQL injection = password dump
  • Insider threat = instant access
  • Password reuse across sites
Vulnerable Code (vulnerable/app.py:26):
# VULNERABLE: Password in SQL query
query = 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
Vulnerable Code (vulnerable/app.py:101):
# VULNERABLE: Returns ALL fields including password
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
user = cursor.fetchone()
return render_template('profile.html', user=user)
Template Receives:
user = {
    'id': 2,
    'username': 'usuario',
    'password': 'password123',  # Plaintext password exposed!
    'email': '[email protected]',
    'role': 'user',
    'created_at': '2024-03-10'
}
Risks:
  • Password in HTML (view source)
  • Password in browser memory
  • Password in template cache
  • Accidental display in UI
Vulnerable Code (vulnerable/app.py:34-36):
# Sensitive data in client-side session
session['user_id'] = user['id']
session['username'] = user['username']
session['role'] = user['role']  # Privilege in cookie!
Decoded Session Cookie:
import base64
import json

cookie = "eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0"
decoded = base64.b64decode(cookie)
print(json.loads(decoded))
# {'user_id': 1, 'username': 'admin', 'role': 'admin'}
With known secret key, attacker can:
  • Read all session data
  • Modify role to escalate privileges
  • Forge sessions for other users
Vulnerable Code (vulnerable/app.py:67-68):
except sqlite3.Error as e:
    flash(f'Error al registrar: {str(e)}', 'danger')
Example Error Messages:
"Error al registrar: UNIQUE constraint failed: users.username"
→ Reveals: Username 'admin' already exists (user enumeration)

"Error SQL: near 'WHERE': syntax error"
→ Reveals: Database type, SQL structure

"Error: /home/user/vulnerable/app.py line 26"
→ Reveals: File paths, application structure
Vulnerable Pattern:
# VULNERABLE: Sensitive data in URL
return redirect(f'/reset-password?token={reset_token}&email={user.email}')
URL:
https://site.com/reset-password?token=abc123&[email protected]
Exposure Risks:
  • Browser history
  • Server logs
  • Proxy logs
  • Referer header leaks
  • Shared links/screenshots

Attack Scenarios

1

Scenario 1: Database Backup Theft

Attack Vector: Attacker obtains database backupVulnerable Database:
SELECT * FROM users;

id | username | password     | email              | role
---|----------|--------------|--------------------|----- 
1  | admin    | admin123     | admin@test.com     | admin
2  | usuario  | password123  | [email protected]      | user
3  | john     | MyP@ssw0rd!  | [email protected]   | user
Attacker Actions:
  1. Extracts all plaintext passwords instantly
  2. Tests passwords on user emails (Gmail, corporate, etc.)
  3. ~65% success rate due to password reuse
  4. 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 vulnerability
payload = "' 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 profile
GET /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:
  1. Steal session via XSS: document.cookie
  2. Hijack user session
  3. Access profile page
  4. View exposed sensitive data
  5. Extract additional user information
Amplification: IDOR allows accessing other users’ data too

Secure Implementation

The secure version properly protects sensitive data:
from werkzeug.security import generate_password_hash

# SECURE: Hash passwords before storage
admin_password = generate_password_hash('admin123')
user_password = generate_password_hash('password123')

cursor.execute("""
    INSERT INTO users (username, password, email, role) 
    VALUES (?, ?, ?, ?)
""", ('admin', admin_password, '[email protected]', 'admin'))

Data Protection Best Practices

Identify and Classify Data:
ClassificationExamplesProtection Level
PublicProduct descriptions, blog postsNone required
InternalEmployee directory, org chartAccess control
ConfidentialEmail addresses, phone numbersEncryption at rest
RestrictedPasswords, SSNs, credit cardsEncryption + strict access
Implementation:
class DataClassification:
    PUBLIC = 0
    INTERNAL = 1
    CONFIDENTIAL = 2
    RESTRICTED = 3

# Mark fields
class 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)
Query Only What You Need:
# Bad: SELECT * returns everything
query = "SELECT * FROM users WHERE id = %s"

# Good: Explicit field list
query = "SELECT id, username, email FROM users WHERE id = %s"

# Better: Context-specific views
def 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()
Mask Sensitive Data in Responses:
def mask_email(email):
    """[email protected] -> u***@example.com"""
    username, domain = email.split('@')
    return f"{username[0]}{'*' * (len(username) - 1)}@{domain}"

def mask_credit_card(card_number):
    """1234567890123456 -> ************3456"""
    return f"{'*' * 12}{card_number[-4:]}"

def mask_phone(phone):
    """555-123-4567 -> XXX-XXX-4567"""
    return f"XXX-XXX-{phone[-4:]}"

# Use in templates
@app.template_filter('mask_email')
def email_mask_filter(email):
    return mask_email(email)
Template Usage:
<p>Email: {{ user.email|mask_email }}</p>
<!-- Displays: u***@example.com -->
Encrypt Sensitive Fields:
from cryptography.fernet import Fernet
import os

# Generate and store encryption key securely
ENCRYPTION_KEY = os.getenv('ENCRYPTION_KEY').encode()
cipher = Fernet(ENCRYPTION_KEY)

def encrypt_data(plaintext):
    """Encrypt sensitive data before storage"""
    return cipher.encrypt(plaintext.encode()).decode()

def decrypt_data(ciphertext):
    """Decrypt data when needed"""
    return cipher.decrypt(ciphertext.encode()).decode()

# Usage
class User(db.Model):
    ssn_encrypted = db.Column(db.String(255))
    
    @property
    def ssn(self):
        return decrypt_data(self.ssn_encrypted)
    
    @ssn.setter
    def ssn(self, value):
        self.ssn_encrypted = encrypt_data(value)
Never Log Sensitive Data:
import logging
import re

class SensitiveDataFilter(logging.Filter):
    """Filter to redact sensitive data from logs"""
    
    def filter(self, record):
        # Redact patterns
        patterns = [
            (r'password["\']?\s*[:=]\s*["\']?([^"\',\s]+)', 'password=***'),
            (r'token["\']?\s*[:=]\s*["\']?([^"\',\s]+)', 'token=***'),
            (r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '***@***.com'),
            (r'\b\d{3}-\d{2}-\d{4}\b', 'XXX-XX-XXXX'),  # SSN
        ]
        
        message = str(record.msg)
        for pattern, replacement in patterns:
            message = re.sub(pattern, replacement, message, flags=re.IGNORECASE)
        
        record.msg = message
        return True

# Configure logger
logger = logging.getLogger(__name__)
logger.addFilter(SensitiveDataFilter())

# Usage
logger.info(f"User login: {username}")  # OK
logger.debug(f"Query: {query}")  # Passwords automatically redacted
Enforce Encrypted Transmission:
from flask import request, redirect

@app.before_request
def enforce_https():
    """Redirect HTTP to HTTPS"""
    if not request.is_secure and not app.debug:
        url = request.url.replace('http://', 'https://', 1)
        return redirect(url, code=301)

# SSL/TLS configuration (production server)
# nginx.conf
"""
server {
    listen 443 ssl http2;
    server_name example.com;
    
    # SSL certificate
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Strong SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
"""
Implement Data Lifecycle Management:
from datetime import datetime, timedelta

class 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 cleanup
from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()
scheduler.add_job(DataRetentionPolicy.cleanup_old_data, 'cron', hour=2)  # 2 AM daily
scheduler.start()

Compliance Requirements

GDPR

Article 32: Security of processing
  • Encryption of personal data
  • Pseudonymization where possible
  • Regular security testing
Article 17: Right to erasure
  • Ability to delete user data
  • Data retention policies
Penalties: Up to €20M or 4% of global revenue

PCI-DSS

Requirement 3: Protect stored cardholder data
  • Minimum data retention
  • Secure deletion
  • Strong cryptography
Requirement 4: Encrypt transmission
  • TLS for all cardholder data
  • Strong cryptographic protocols
Penalties: 5,0005,000-100,000 per month

HIPAA

164.312(a)(2)(iv): Encryption and decryption
  • Encrypt ePHI at rest and in transit
  • Implement access controls
  • Audit logs
164.316(b)(2): Periodic evaluation
  • Regular security assessments
Penalties: Up to $1.5M per year per violation category

CCPA

§ 1798.150: Data breach liability
  • 100100-750 per consumer per incident
  • Reasonable security measures required
§ 1798.105: Right to deletion
  • Delete consumer data on request

Testing for Data Exposure

# 1. Inspect database
sqlite3 users.db
SELECT * FROM users;
# Check if passwords are hashed

# 2. Check API responses
curl https://target.com/api/user/1 | jq .
# Look for password, SSN, tokens in response

# 3. Inspect HTML source
curl 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

Incident Response

If sensitive data is exposed:
1

Immediate Actions

  1. Contain the breach: Disable affected systems
  2. Assess scope: How many records? What data?
  3. Preserve evidence: Backup logs, snapshots
  4. Notify team: Security, legal, management
2

Remediation

  1. Patch vulnerability: Fix the code/config issue
  2. Rotate credentials: Change all exposed passwords, keys
  3. Revoke sessions: Invalidate all user sessions
  4. Monitor: Watch for unauthorized access
3

Notification

  1. Users: Email affected users within 72 hours (GDPR)
  2. Regulators: Report to relevant authorities
  3. Public: Transparency (if required by law)
  4. Documentation: Detailed incident report
4

Post-Incident

  1. Root cause analysis: Why did this happen?
  2. Update policies: Improve security practices
  3. Training: Educate development team
  4. Monitoring: Implement detection for similar issues

References

Next Steps

Related security topics:

Build docs developers (and LLMs) love