Sensitive File Exposure Prevention
Prevent exposure of .git directories, .env files, backups, IDE configs, and source maps by blocking access at the web server and hardening your deployment pipeline.
Sensitive files left accessible on production web servers are one of the most common and most dangerous security misconfigurations. An exposed .git directory lets an attacker reconstruct your entire source code. A leaked .env file hands over database credentials, API keys, and secrets. Backup files, IDE project configs, and source maps each reveal information that turns a casual scan into a targeted attack.
What Gets Exposed
The files that most frequently leak fall into five categories:
- Version control:
.git/,.svn/,.hg/directories. The.git/HEADfile alone confirms the repository exists, and tools likegit-dumpercan reconstruct the full repo from a reachable.git/directory. - Environment files:
.env,.env.local,.env.production. These contain secrets in plaintext -- database URIs, API tokens, signing keys. - Backup and database files:
.sql,.bak,.dump,.old,.tar.gz. Developers or automated processes sometimes leave these in the web root after migrations or debugging sessions. - IDE and editor files:
.idea/,.vscode/,.project,.DS_Store. These leak project structure, run configurations, and sometimes credentials stored in workspace settings. - Source maps:
.mapfiles generated by JavaScript and CSS bundlers. They map minified production code back to readable source, exposing business logic, comments, and internal APIs. For remediation steps covering all these disclosure vectors, see our information disclosure remediation guide.
Why Content Validation Matters
A naive scanner checks whether GET /.env returns HTTP 200 and flags the result. But many servers return 200 for all paths via a single-page application fallback or a custom error page. This produces false positives.
Content-validated detection solves this. Instead of relying on status codes alone, CyberShield inspects the response body for signatures that confirm the file is genuine. For .env files, it looks for patterns like KEY=value lines. For .git/HEAD, it checks for ref: refs/heads/. For SQL dumps, it checks for SQL statements. This approach dramatically reduces false positives and gives you reliable findings you can act on.
Blocking Access in Nginx
Add these rules to your server block. They return 403 for any request matching sensitive file patterns:
# Block version control directories
location ~ /\.(git|svn|hg) {
deny all;
return 403;
}
# Block environment and config files
location ~ /\.(env|env\.local|env\.production|env\.staging) {
deny all;
return 403;
}
# Block backup and database files
location ~* \.(sql|bak|dump|old|tar\.gz|zip|sqlite|db)$ {
deny all;
return 403;
}
# Block IDE and editor files
location ~ /\.(idea|vscode|project|DS_Store) {
deny all;
return 403;
}
# Block source maps
location ~* \.map$ {
deny all;
return 403;
}
Use location ~ for case-sensitive regex and location ~* for case-insensitive. The return 403 is explicit -- without it, deny all alone still works but adding the return ensures the response body is minimal.
Blocking Access in Apache
Add these rules to your .htaccess or VirtualHost configuration. Requires mod_rewrite to be enabled:
# Block version control directories
RedirectMatch 403 /\.(git|svn|hg)
# Block environment files
<FilesMatch "^\.env">
Require all denied
</FilesMatch>
# Block backup and database files
<FilesMatch "\.(sql|bak|dump|old|sqlite|db)$">
Require all denied
</FilesMatch>
# Block IDE directories
RedirectMatch 403 /\.(idea|vscode)
# Block source maps
<FilesMatch "\.map$">
Require all denied
</FilesMatch>
If you are on Apache 2.2 or earlier, replace Require all denied with Deny from all inside <FilesMatch> blocks.
.gitignore Best Practices
Blocking access at the web server is a safety net. The primary defense is ensuring sensitive files never reach the server in the first place.
Your .gitignore should include at minimum:
# Environment files
.env
.env.*
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
Thumbs.db
# Backup and database files
*.sql
*.bak
*.dump
*.sqlite
# Source maps (if not needed in production)
*.map
# Dependency directories
node_modules/
__pycache__/
For files that were already committed before being added to .gitignore, run git rm --cached <file> to remove them from tracking. Adding a path to .gitignore does not retroactively remove it from history. If secrets were committed, rotate them immediately -- the old values remain in git history forever unless you rewrite history.
Deployment Pipeline Checks
Add a pre-deployment check that scans your build output directory for files that should not be deployed. A simple script catches most problems:
#!/bin/bash
# pre-deploy-check.sh — run before deploying to production
DEPLOY_DIR="${1:-.}"
PATTERNS=(".env" ".git" ".svn" ".idea" ".vscode" "*.sql" "*.bak" "*.dump")
FOUND=0
for pattern in "${PATTERNS[@]}"; do
matches=$(find "$DEPLOY_DIR" -name "$pattern" -not -path "*/node_modules/*" 2>/dev/null)
if [ -n "$matches" ]; then
echo "BLOCKED: Found sensitive file pattern '$pattern':"
echo "$matches"
FOUND=1
fi
done
if [ "$FOUND" -eq 1 ]; then
echo "Deployment blocked. Remove sensitive files before deploying."
exit 1
fi
echo "No sensitive files found. Safe to deploy."
Integrate this into your CI/CD pipeline as a required step before deployment. In GitHub Actions, add it as a job step that runs after the build and before the deploy.
Source Map Configuration
Most bundlers generate source maps by default. Disable them for production builds:
Webpack (webpack.config.js):
module.exports = {
mode: 'production',
devtool: false, // disables source maps
};
Vite (vite.config.js):
export default defineConfig({
build: {
sourcemap: false,
},
});
Next.js (next.config.js):
module.exports = {
productionBrowserSourceMaps: false, // default is already false
};
If you need source maps for error tracking services like Sentry, upload them directly to the service during your build step and exclude the .map files from deployment.
Verification
After applying these controls, verify by requesting known sensitive paths directly:
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.env
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.git/HEAD
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/backup.sql
Each should return 403. Run a CyberShield scan to get a full assessment, including content-validated checks that confirm these paths are genuinely blocked and not just returning a soft 403 page with a 200 status code. File exposure is one of the key findings that passive web vulnerability assessment surfaces through content validation rather than status code checking alone.
Continue Reading
WAF Detection and Fingerprinting: How CyberShield Identifies Web Application Firewalls
Understanding what web application firewall protects a target is essential context for any security assessment. Learn how CyberShield passively fingerprints 15+ WAF vendors through header analysis, error patterns, and behavioral signatures.
Web Vulnerability Assessment: What Passive Analysis Reveals Without Firing a Single Exploit
Passive web analysis uncovers OWASP-relevant vulnerabilities -- information leaks, form weaknesses, exposed files, and redirect flaws -- without touching a single exploit.
HTTP Security Headers: The Complete Hardening Guide
Most web servers ship with minimal security headers. Learn which headers protect against XSS, clickjacking, MIME sniffing, and other browser-side attacks — and how to configure them correctly.