DEV Community

Cover image for Attack Surface: The Subdomains You Forgot You Had
APIVerve
APIVerve

Posted on • Originally published at blog.apiverve.com

Attack Surface: The Subdomains You Forgot You Had

Every security audit starts the same way: "Show me all your public-facing assets."

And every organization gives the same answer: a list that's incomplete.

They know about www. They know about mail. They know about api. But what about dev.yourcompany.com, the staging environment that marketing set up for a campaign last year? Or old.yourcompany.com, pointed at a server that hasn't been patched since 2021? Or jira.yourcompany.com, running an ancient version of Atlassian software that nobody realized was still exposed?

These forgotten subdomains are your real attack surface. And if you don't know they exist, you can't secure them.

The Shadow IT Problem

Nobody creates subdomains with malicious intent. They're created to solve problems.

The marketing team needs a landing page for a campaign. The dev team needs a staging environment. The support team needs a help desk. Someone needs to test something quickly, just for a week.

So they spin up a server, point a subdomain at it, and move on with their work.

The problem isn't creation. It's retirement.

That campaign landing page? Campaign ended, page forgotten, server still running. The staging environment? Product shipped, staging abandoned, credentials unchanged. The quick test that was "just for a week"? It's been three years.

Meanwhile, the software on these forgotten systems hasn't been updated. The security configurations haven't been reviewed. The credentials haven't been rotated.

And the subdomains are still there, publicly accessible, waiting for someone to find them.

What Attackers See

When attackers target an organization, they don't start with the main website. The main website is usually well-protected — WAF, DDoS protection, regular security reviews.

Instead, they start with reconnaissance. And subdomain enumeration is step one.

The attacker enumerates every subdomain of your domain. They find your staging environment with default credentials. They find your old Jira instance with a known vulnerability. They find an admin panel that someone forgot to put behind a VPN.

Each subdomain is a potential entry point. The more subdomains, the larger the attack surface. The older and more forgotten the subdomain, the more likely it's vulnerable.

The irony: your main website might be perfectly secure, but the attacker gets in through a test environment that nobody knew still existed.

Discovering What You Have

The first step in securing your attack surface is knowing what it includes. Here's how to enumerate your own subdomains:

async function discoverSubdomains(domain) {
  const response = await fetch(
    `https://api.apiverve.com/v1/subdomainfinder?domain=${encodeURIComponent(domain)}`,
    { headers: { 'x-api-key': 'YOUR_API_KEY' } }
  );
  const { data } = await response.json();

  console.log(`Found ${data.count} subdomains for ${domain}:`);

  // Categorize by apparent function
  const categorized = {
    api: [],
    mail: [],
    dev: [],
    admin: [],
    other: []
  };

  for (const sub of data.subdomains) {
    const name = sub.subdomain.toLowerCase();

    if (name.includes('api')) {
      categorized.api.push(sub);
    } else if (name.includes('mail') || name.includes('smtp')) {
      categorized.mail.push(sub);
    } else if (/dev|staging|test|qa|uat/.test(name)) {
      categorized.dev.push(sub);
    } else if (/admin|panel|dashboard|manage/.test(name)) {
      categorized.admin.push(sub);
    } else {
      categorized.other.push(sub);
    }
  }

  return categorized;
}
Enter fullscreen mode Exit fullscreen mode

Dev and admin subdomains deserve immediate attention. These are high-value targets for attackers and often have weaker security than production systems.

The Inventory Gap

Run subdomain discovery on your domain. Then compare the results to your official asset inventory.

The delta is your blind spot.

import requests

def audit_subdomain_inventory(domain, known_subdomains):
    """
    Compare discovered subdomains against known inventory.
    Returns unknown assets and potential shadow IT.
    """
    response = requests.get(
        'https://api.apiverve.com/v1/subdomainfinder',
        params={'domain': domain},
        headers={'x-api-key': 'YOUR_API_KEY'}
    )
    data = response.json()['data']

    discovered = {s['subdomain'] for s in data['subdomains']}
    known = set(known_subdomains)

    # Subdomains we found that aren't in inventory
    unknown = discovered - known

    # Subdomains in inventory that we didn't find (might be internal-only)
    inventory_only = known - discovered

    report = {
        'total_discovered': len(discovered),
        'total_in_inventory': len(known),
        'unknown_assets': list(unknown),
        'shadow_it_risk': len(unknown),
        'inventory_only': list(inventory_only)
    }

    if unknown:
        print(f"WARNING: Found {len(unknown)} subdomains not in inventory:")
        for sub in unknown:
            print(f"  - {sub}")

    return report
Enter fullscreen mode Exit fullscreen mode

Every subdomain in the "unknown" list is a question: What is this? Who created it? Is it still needed? Is it secure?

The High-Risk Patterns

Some subdomain patterns deserve immediate investigation:

Development environments: dev., staging., test., qa., uat., sandbox.

These often have:

  • Default or weak credentials
  • Debug modes enabled
  • Verbose error messages
  • Outdated software versions
  • Production data copies (including PII)

Admin panels: admin., panel., dashboard., manage., control., backend.

These provide:

  • Privileged access to systems
  • User management capabilities
  • Configuration controls
  • Data export features

Legacy systems: old., legacy., v1., archive., backup.

These suffer from:

  • Unpatched vulnerabilities
  • Abandoned maintenance
  • Forgotten credentials
  • Outdated security practices

Service-specific: jira., confluence., gitlab., jenkins., grafana., kibana.

These run:

  • Third-party software with known CVEs
  • Default configurations
  • Exposed sensitive data

The SSL Certificate Problem

Subdomains with expired or missing SSL certificates are red flags. Either the system is abandoned, or security isn't a priority.

After discovering subdomains, check their SSL status:

async function auditSubdomainSSL(subdomains) {
  const issues = [];

  for (const sub of subdomains) {
    const response = await fetch(
      `https://api.apiverve.com/v1/sslchecker?domain=${sub.subdomain}`,
      { headers: { 'x-api-key': 'YOUR_API_KEY' } }
    );
    const { data } = await response.json();

    if (!data.valid) {
      issues.push({
        subdomain: sub.subdomain,
        problem: 'Invalid or missing SSL certificate',
        ip: sub.ip
      });
    } else if (data.daysUntilExpiry < 30) {
      issues.push({
        subdomain: sub.subdomain,
        problem: `SSL expires in ${data.daysUntilExpiry} days`,
        ip: sub.ip
      });
    }
  }

  return issues;
}
Enter fullscreen mode Exit fullscreen mode

An expired certificate might just be annoying for users. But it also indicates a system that isn't being maintained — which probably means it isn't being patched either.

Building Continuous Visibility

One-time subdomain audits are useful. Continuous monitoring is better.

Your attack surface changes constantly. Developers spin up new services. Marketing creates landing pages. Someone tests something "just for a minute."

Set up regular subdomain enumeration — weekly at minimum, daily if you can:

import requests
import json
from datetime import datetime

def continuous_subdomain_monitoring(domain, api_key, baseline_file):
    """
    Compare current subdomains against baseline.
    Alert on new discoveries.
    """
    # Load previous baseline
    try:
        with open(baseline_file, 'r') as f:
            baseline = json.load(f)
    except FileNotFoundError:
        baseline = {'subdomains': [], 'last_check': None}

    # Get current subdomains
    response = requests.get(
        'https://api.apiverve.com/v1/subdomainfinder',
        params={'domain': domain},
        headers={'x-api-key': api_key}
    )
    data = response.json()['data']

    current = {s['subdomain'] for s in data['subdomains']}
    previous = set(baseline['subdomains'])

    # Find changes
    new_subdomains = current - previous
    removed_subdomains = previous - current

    if new_subdomains:
        alert_new_subdomains(domain, new_subdomains)

    # Update baseline
    baseline = {
        'subdomains': list(current),
        'last_check': datetime.now().isoformat()
    }

    with open(baseline_file, 'w') as f:
        json.dump(baseline, f)

    return {
        'new': list(new_subdomains),
        'removed': list(removed_subdomains),
        'total': len(current)
    }

def alert_new_subdomains(domain, subdomains):
    """Send alert for new subdomain discoveries."""
    print(f"ALERT: New subdomains discovered for {domain}:")
    for sub in subdomains:
        print(f"  - {sub}")
    # Send to Slack, email, PagerDuty, etc.
Enter fullscreen mode Exit fullscreen mode

When a new subdomain appears, someone should investigate. Who created it? For what purpose? Is it secured appropriately?

The Response Playbook

When you discover an unknown subdomain, here's the investigation process:

Step 1: Identify ownership. Check DNS records for the authoritative nameserver. Check the SSL certificate for organization details. Check server headers for identifying information.

Step 2: Assess exposure. What services are running? Are there login pages? APIs? Sensitive data? Is it indexed by search engines?

Step 3: Evaluate security posture. Is the software up to date? Are there known vulnerabilities? Are authentication mechanisms strong? Is data transmitted securely?

Step 4: Decide disposition.

  • Keep and secure: If it's needed, bring it up to security standards.
  • Restrict access: If it's needed but shouldn't be public, put it behind VPN.
  • Decommission: If it's not needed, take it down completely.

Step 5: Update inventory. Add to your official asset inventory with owner, purpose, and security status.

The Organizational Challenge

Subdomain sprawl is a governance problem as much as a technical one.

The technical fix is easy: enumerate subdomains, audit them, secure them.

The organizational fix is harder: create processes that prevent sprawl in the first place.

Require approval for new subdomains. Someone should know when DNS records are created.

Assign owners. Every subdomain should have an owner responsible for its security and lifecycle.

Set expiration dates. Temporary systems should have automatic sunset dates.

Regular audits. Monthly or quarterly reviews of all public-facing assets.

Clear decommissioning process. Make it easy to retire subdomains properly.

Without governance, you're playing whack-a-mole. Secure the subdomains you find today; new ones appear tomorrow.

The Business Case

Security teams sometimes struggle to get resources for attack surface management. Here's how to make the case:

Risk reduction. Every unknown subdomain is potential liability. Data breaches through forgotten systems are common and expensive.

Compliance. PCI-DSS, SOC 2, and other frameworks require maintaining asset inventories. You can't be compliant if you don't know what you have.

Incident response. When (not if) something goes wrong, knowing your full attack surface accelerates investigation.

Acquisition due diligence. Companies acquiring others want to know the full technical footprint. Unknown assets delay or derail deals.

Brand protection. Typosquat and impersonation subdomains harm your brand. Discovering them early limits damage.

What About Certificate Transparency?

You might think: if I use SSL certificates, those are logged in Certificate Transparency logs, so I know my subdomains.

Partially true. CT logs reveal subdomains with SSL certificates. But they miss:

  • Subdomains with HTTP only (no HTTPS)
  • Internal subdomains that never got certificates
  • Subdomains with self-signed certificates
  • Recently created subdomains not yet indexed

Our Subdomain Finder API combines multiple sources — CT logs, DNS enumeration, public datasets — to provide more complete discovery than any single source.

The Attacker's Perspective

Here's a simple truth: if you can enumerate your subdomains, so can attackers.

The tools are freely available. The techniques are well-documented. Any reasonably competent attacker will run subdomain enumeration as part of their reconnaissance.

The question isn't whether your subdomains will be discovered. They will be.

The question is whether you discovered them first and secured them, or whether you find out they're exposed when the breach notification arrives.

The Bottom Line

Your main website is probably secure. You put resources into it. You review it. You patch it.

But your attack surface extends far beyond your main website. Every subdomain is an entry point. Every forgotten test environment is a potential compromise.

Subdomain discovery isn't a one-time security exercise. It's an ongoing process of maintaining visibility into your own infrastructure.

Know what you expose. Secure what you keep. Decommission what you don't need.

And do it before the attackers do.


Ready to map your attack surface? The Subdomain Finder API discovers all your public subdomains using multiple enumeration techniques. The SSL Certificate Checker verifies those subdomains are properly secured. Find out what you're exposing before someone else does.


Originally published at APIVerve Blog

Top comments (0)