How to Pass a Security Audit: The Practical Playbook
Every security audit follows the same pattern: scan, find issues, fix them, prove you fixed them. The difference between a stressful audit and a smooth one is whether you've been doing the basics all along.
This playbook covers what auditors actually look for, how to find and fix the most common findings before they become problems, and how to prepare for formal compliance audits.
#What security auditors actually check
Security auditors (and automated scanners) look for the same categories of issues, regardless of whether it's a casual review or a formal SOC 2 audit:
- Attack surface — what's exposed to the internet
- Known vulnerabilities — outdated software with CVEs
- Configuration errors — headers, permissions, defaults
- Authentication and authorization — who can access what
- Data protection — encryption, storage, transmission
Each category has specific checks. Let's go through them.
#1. Inventory your attack surface
Before you can secure it, you need to know what's exposed.
#External-facing assets
- All domains and subdomains are known and documented
- Every domain resolves to expected IP addresses
- No orphaned subdomains pointing to forgotten servers
- SSL certificates cover all active subdomains
Check subdomain drift:
# Query Certificate Transparency logs for your domain
curl -s "https://crt.sh/?q=%.your-domain.com&output=json" | jq '.[].name_value' | sort -u
This queries crt.sh, a public certificate transparency log. Any subdomain that appears here has a public SSL certificate and is discoverable by attackers.
#Exposed files and directories
The most common low-effort attack is scanning for exposed sensitive files:
.git/config— reveals your repository URL and potentially credentials.env— contains API keys, database passwords, secretswp-config.php— WordPress database credentialsphpinfo.php— reveals server configuration.DS_Store— reveals directory structure on macOS
# Quick check for exposed files
for file in .env .git/config .git/HEAD wp-config.php phpinfo.php .DS_Store; do
status=$(curl -s -o /dev/null -w "%{http_code}" "https://your-site.com/$file")
echo "$file: $status"
done
If any return 200, you have an exposure. Fix it immediately.
Learn more: OWASP Testing for Exposed Files
#2. Patch known vulnerabilities
#Dependencies
Outdated dependencies with known CVEs are the most common audit finding. Use automated tools to scan:
# Python
pip-audit
# Node.js
npm audit
# General (SCA - Software Composition Analysis)
# Vergate scans your tech stack and cross-references against OpenCVE
Remediation priority:
- Critical CVEs — patch within 24 hours
- High CVEs — patch within 7 days
- Medium CVEs — patch within 30 days
- Low CVEs — patch within 90 days or accept the risk
#Server software
- Operating system is up to date
- Web server (nginx/Apache/Caddy) is current
- Runtime (Node.js/Python/Go) is on a supported version
- Database server is patched
Learn more: NIST National Vulnerability Database, CVE Details
#3. Fix configuration errors
#Security headers
The cheapest security upgrade. Set these 5 headers on every response:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'; form-action 'self'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
See our complete security headers guide for detailed configuration.
#SSL/TLS
- Certificate is valid and not expired
- TLS 1.2 or higher is enforced (no SSLv3, TLS 1.0, TLS 1.1)
- Strong cipher suites only (no RC4, DES, 3DES)
- HSTS header is set with a long max-age
# Check SSL configuration
nmap --script ssl-enum-ciphers -p 443 your-site.com
Learn more: Mozilla SSL Configuration Generator
#Error handling
- Custom error pages (no stack traces or server info leaked)
- Debug mode is disabled in production
-
Serverheader is removed or genericized
# Nginx — hide server version
server_tokens off;
#4. Audit authentication and authorization
#Authentication
- Password policy enforced (minimum 8 characters, check against breached password lists)
- Rate limiting on login endpoints (prevent brute force)
- Account lockout or progressive delay after failed attempts
- Multi-factor authentication available (and encouraged)
- Session tokens are rotated after login
- Sessions expire after reasonable inactivity (24 hours for web apps)
#Authorization
- Users can only access their own data
- API endpoints enforce authentication and authorization
- Admin routes are protected and restricted
- No IDOR (Insecure Direct Object Reference) vulnerabilities
# Test for IDOR — can user A access user B's data?
curl -H "Authorization: Bearer <user_a_token>" https://api.your-site.com/users/<user_b_id>
#API security
- Rate limiting on all endpoints (not just login)
- Input validation on every endpoint
- CORS is configured to allow only trusted origins
- API keys are scoped and rotatable
- Sensitive data is never in URL query parameters
Learn more: OWASP API Security Top 10
#5. Protect data
#In transit
- All traffic is HTTPS (no HTTP endpoints)
- HTTP requests are redirected to HTTPS (301)
- HSTS prevents downgrade attacks
- No mixed content (HTTP resources loaded on HTTPS pages)
# Check for mixed content
curl -s https://your-site.com | grep -i 'http://' | head -10
#At rest
- Database passwords are encrypted (not plaintext in config files)
- API keys are stored in environment variables (not source code)
- Sensitive data is encrypted in the database
- Backups are encrypted
-
.envfiles are in.gitignoreand never committed
# Check if secrets are in your git history
git log --all --oneline -- '.env' '*.pem' '*password*' '*secret*'
If this returns anything, the secrets are in your git history forever. Rotate them immediately and use git-filter-repo to clean the history.
Learn more: OWASP Data Protection Cheat Sheet
#Preparing for a formal audit
If you're facing a SOC 2, ISO 27001, or PCI DSS audit, the technical checks above are only part of the picture. Formal audits also require:
#Documentation
- Information security policy
- Access control policy
- Incident response plan
- Data retention and disposal policy
- Vendor management procedures
- Change management process
#Evidence
- Automated scan results (monthly minimum)
- Vulnerability remediation records
- Access review logs
- Security training completion records
- Incident response drill records
#Process
- Regular security reviews (quarterly)
- Penetration testing (annually minimum)
- Dependency scanning in CI/CD pipeline
- Security headers verification in deployment
- Backup and recovery testing
#Automating your security posture
The best audit is one where you already know the answers. Automate the checks:
- Weekly automated scan — run a passive security scanner on all production domains
- CI/CD security gate —
npm audit/pip-auditin every build pipeline - Header verification — deploy-time check that security headers are present
- Dependency alerts — GitHub Dependabot or Renovate for automated PR creation
- SSL monitoring — alert when certificates are within 30 days of expiry
- Uptime monitoring — detect outages and degradation in real time
Vergate combines automated security scanning, performance analysis, uptime monitoring, and threat detection into a single platform. Every scan produces a scored report with specific fix suggestions and AI-ready remediation prompts. Run a free scan to see where you stand.
#Further reading
- OWASP Top 10 — the definitive list of web application security risks
- NIST Cybersecurity Framework — risk management framework used by SOC 2 and ISO 27001
- CIS Benchmarks — configuration best practices for every technology
- Verizon DBIR — annual data breach investigation report
- Vergate Security Scanner — automated security auditing with AI fix prompts
Frequently asked questions
How often should I run a security audit?
Run automated security scans weekly (or on every deploy). Conduct a manual or penetration-test-style audit quarterly. Full compliance audits (SOC 2, ISO 27001) are annual. Continuous monitoring between audits catches regressions.
What is the difference between a vulnerability scan and a penetration test?
A vulnerability scan is automated and checks for known issues (missing headers, exposed files, outdated software). A penetration test is manual — a human attacker tries to exploit vulnerabilities to access data or systems. Scans are fast and cheap; pentests are thorough and expensive.
What are the most common security audit findings?
Missing security headers, exposed sensitive files (.env, .git), outdated dependencies with known CVEs, insecure SSL/TLS configuration, excessive permissions, and lack of rate limiting on authentication endpoints.
Do I need a security audit for a small website?
Yes. 43% of cyberattacks target small businesses [according to Verizon DBIR](https://www.verizon.com/business/resources/reports/dbir/). Automated scanners like Vergate make audits accessible for any size — you don't need a pentest firm for basic hygiene.