Exposure of Backup File to an Unauthorized Control Sphere

Description

Exposure of Backup File to an Unauthorized Control Sphere is a vulnerability where a backup file is stored in a directory or archive that is accessible to unauthorized users, potentially exposing sensitive application data. Backup files are often renamed with extensions like .bak, .old, .~bk, .backup, or include timestamps to distinguish them from production files. When these renamed files remain in the webroot or other accessible locations, they can be retrieved by attackers. Such backup creation may occur automatically through web server configuration, editor behavior, or manually by administrators performing maintenance.

Risk

Backup files often contain the same sensitive information as production files but bypass security controls designed for the original files. PHP source files renamed to .php.bak may be served as plaintext, exposing source code including database credentials, API keys, and business logic. Configuration backups may contain plaintext passwords. Database dump backups expose complete datasets. Attackers commonly scan for backup file patterns as part of reconnaissance. Even partial backups can reveal application architecture, parameter formats, and security mechanisms that aid further attacks.

Solution

Establish and enforce security policies prohibiting storage of backup files within web-accessible directories. Configure web servers to block access to common backup file extensions. Implement automated deployment processes that don't create backup files. Use proper version control instead of backup copies. Store necessary backups in secure, non-web-accessible locations with appropriate access controls. Configure editors and IDEs to store backup files outside the project directory. Regularly scan for and remove backup files from production systems. Include backup file checks in security testing and deployment verification.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Attackers retrieving backup files gain access to all contained information including source code, database credentials, API keys, configuration details, and architectural information about the application.

Example Code

Vulnerable Configuration

# Vulnerable: Backup files in webroot
$ ls -la /var/www/html/
-rw-r--r-- 1 www-data www-data 5120 Jan 15 10:00 config.php
-rw-r--r-- 1 www-data www-data 5120 Jan 14 09:00 config.php.bak
-rw-r--r-- 1 www-data www-data 5120 Jan 13 08:00 config.php.old
-rw-r--r-- 1 www-data www-data 5120 Jan 12 07:00 config.php~
-rw-r--r-- 1 www-data www-data 5120 Jan 11 06:00 config.php.2024-01-11
-rw-r--r-- 1 www-data www-data 5120 Jan 10 05:00 config.php.orig
-rw-r--r-- 1 www-data www-data 5120 Jan 09 04:00 .config.php.swp

# All these backup files are web-accessible!
# config.php is executed as PHP, but backups served as plaintext
<?php
// /var/www/html/config.php - normal execution as PHP
// /var/www/html/config.php.bak - served as PLAINTEXT!

$db_host = 'localhost';
$db_user = 'admin';
$db_pass = 'super_secret_password_123';  // Exposed in backup!
$db_name = 'production_db';

$api_key = 'sk_live_abcdef123456789';    // API key exposed!
$encryption_key = 'aes-256-encryption-key'; // Encryption key exposed!

// Attacker accesses: http://example.com/config.php.bak
// And sees all these credentials in plaintext!
?>
# Vulnerable: Editor backup files
# vim creates .swp files
# emacs creates ~ files
# Many editors create .bak files

# After editing /var/www/html/database.php with vim:
$ ls -la /var/www/html/
-rw-r--r-- 1 dev dev 1024 Jan 15 10:00 database.php
-rw-r--r-- 1 dev dev 4096 Jan 15 10:00 .database.php.swp  # Vim swap
-rw-r--r-- 1 dev dev 1024 Jan 15 10:00 database.php~      # Vim backup
# Vulnerable: Admin creates backup before changes
$ ssh admin@server
$ cd /var/www/html
$ cp application.conf application.conf.backup  # Bad practice!
$ cp index.php index.php.old                   # Bad practice!

# Vulnerable: tar backup in webroot
$ tar czf backup.tar.gz /var/www/html/
$ mv backup.tar.gz /var/www/html/             # Entire site exposed!

# Vulnerable: SQL dump in webroot
$ mysqldump mydb > /var/www/html/backup.sql   # Database exposed!
# Vulnerable: Apache serving backup files
<VirtualHost *:80>
    DocumentRoot /var/www/html

    # No restrictions on backup file extensions
    # All .bak, .old, .backup files are served

    # PHP files with backup extensions served as plaintext
</VirtualHost>
# Vulnerable: Nginx serving backup files
server {
    listen 80;
    root /var/www/html;

    # PHP execution only for .php files
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php-fpm.sock;
    }

    # Vulnerable: .php.bak, .php.old served as plaintext
    # No restrictions on backup files
}

Fixed Configuration

# Fixed: Remove backup files from webroot
#!/bin/bash

# Find and remove common backup file patterns
find /var/www/html -type f \( \
    -name "*.bak" -o \
    -name "*.backup" -o \
    -name "*.old" -o \
    -name "*~" -o \
    -name "*.orig" -o \
    -name "*.swp" -o \
    -name ".*.swp" -o \
    -name "*.swo" -o \
    -name "*.save" -o \
    -name "*.sql" -o \
    -name "*.tar" -o \
    -name "*.tar.gz" -o \
    -name "*.zip" \
\) -delete

echo "Backup files removed from webroot"
# Fixed: Apache blocking backup files
<VirtualHost *:80>
    DocumentRoot /var/www/html

    # Fixed: Block common backup extensions
    <FilesMatch "\.(bak|backup|old|orig|save|swp|swo|tmp)$">
        Require all denied
    </FilesMatch>

    # Fixed: Block files with backup patterns
    <FilesMatch "~$">
        Require all denied
    </FilesMatch>

    # Fixed: Block hidden swap files
    <FilesMatch "^\.[^.]+\.sw[op]$">
        Require all denied
    </FilesMatch>

    # Fixed: Block archive files
    <FilesMatch "\.(tar|tar\.gz|tgz|zip|rar|7z|sql|dump)$">
        Require all denied
    </FilesMatch>

    # Fixed: Block PHP files with backup extensions
    <FilesMatch "\.php\.(bak|backup|old|orig|~)$">
        Require all denied
    </FilesMatch>
</VirtualHost>
# Fixed: Nginx blocking backup files
server {
    listen 80;
    root /var/www/html;

    # Fixed: Block common backup extensions
    location ~* \.(bak|backup|old|orig|save|swp|swo|tmp)$ {
        deny all;
        return 404;
    }

    # Fixed: Block tilde backup files
    location ~ ~$ {
        deny all;
        return 404;
    }

    # Fixed: Block hidden swap files
    location ~ /\.[^/]+\.sw[op]$ {
        deny all;
        return 404;
    }

    # Fixed: Block archive files
    location ~* \.(tar|tar\.gz|tgz|zip|rar|7z|sql|dump)$ {
        deny all;
        return 404;
    }

    # Fixed: Block PHP backup files
    location ~* \.php\.(bak|backup|old|orig|~)$ {
        deny all;
        return 404;
    }

    # PHP execution
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php-fpm.sock;
    }
}
# Fixed: Proper backup procedures (outside webroot)
#!/bin/bash

# Fixed: Store backups in secure location
BACKUP_DIR="/var/backups/webapp"
mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"

# Fixed: Backup to secure location, not webroot
tar czf "$BACKUP_DIR/webapp-$(date +%Y%m%d).tar.gz" \
    --exclude='*.log' \
    /var/www/html/

# Fixed: Database backup to secure location
mysqldump mydb | gzip > "$BACKUP_DIR/db-$(date +%Y%m%d).sql.gz"
chmod 600 "$BACKUP_DIR"/*.gz

# Fixed: Rotate old backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
# Fixed: Configure editors to store backups elsewhere
# ~/.vimrc
# set backupdir=~/.vim/backup//
# set directory=~/.vim/swap//
# set undodir=~/.vim/undo//

# Fixed: Create these directories
mkdir -p ~/.vim/{backup,swap,undo}

# ~/.emacs
# (setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
# Fixed: CI/CD pipeline with backup file cleanup
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      # Fixed: Remove all backup files before deployment
      - name: Remove backup files
        run: |
          find . -type f \( \
            -name "*.bak" -o \
            -name "*.backup" -o \
            -name "*.old" -o \
            -name "*~" -o \
            -name "*.orig" -o \
            -name "*.swp" -o \
            -name "*.sql" -o \
            -name "*.tar.gz" \
          \) -delete

      # Fixed: Verify no sensitive files
      - name: Check for sensitive files
        run: |
          if find . -type f \( -name "*.sql" -o -name "*.bak" \) | grep -q .; then
            echo "ERROR: Backup files found!"
            exit 1
          fi

      - name: Deploy
        run: rsync -avz --delete ./ server:/var/www/html/

      # Fixed: Post-deployment verification
      - name: Verify no backups accessible
        run: |
          for ext in bak backup old sql tar.gz; do
            if curl -s -o /dev/null -w "%{http_code}" \
                "https://example.com/config.php.$ext" | grep -q "200"; then
              echo "ERROR: Backup file accessible!"
              exit 1
            fi
          done
# Fixed: Deployment script with backup file detection
import os
import sys
import fnmatch

BACKUP_PATTERNS = [
    '*.bak', '*.backup', '*.old', '*.orig', '*.save',
    '*.swp', '*.swo', '*~', '*.tmp',
    '*.sql', '*.dump', '*.tar', '*.tar.gz', '*.zip',
    '.*.swp', '.*.swo'
]

def find_backup_files(directory):
    """Find backup files that shouldn't be deployed."""
    backup_files = []

    for root, dirs, files in os.walk(directory):
        # Skip hidden directories
        dirs[:] = [d for d in dirs if not d.startswith('.')]

        for pattern in BACKUP_PATTERNS:
            for filename in fnmatch.filter(files, pattern):
                backup_files.append(os.path.join(root, filename))

    return backup_files

def pre_deployment_check(webroot):
    """Check for backup files before deployment."""
    backups = find_backup_files(webroot)

    if backups:
        print("ERROR: Backup files found in deployment:")
        for f in backups:
            print(f"  - {f}")
        print("\nRemove these files before deploying!")
        return False

    print("OK: No backup files found")
    return True

if __name__ == '__main__':
    webroot = sys.argv[1] if len(sys.argv) > 1 else '/var/www/html'
    if not pre_deployment_check(webroot):
        sys.exit(1)

CVE Examples

No specific CVEs are listed in the MITRE database for this CWE. However, the vulnerability pattern is extremely common:

  • Backup file exposure is a frequent finding in penetration tests
  • Part of OWASP Testing Guide for security misconfiguration
  • Common in bug bounty programs

References

  1. MITRE Corporation. "CWE-530: Exposure of Backup File to an Unauthorized Control Sphere." https://cwe.mitre.org/data/definitions/530.html
  2. OWASP. "Testing for Backup Files (WSTG-CONF-04)."
  3. OWASP Top Ten. "A05:2021 - Security Misconfiguration."