Back to blog
SecurityJanuary 1, 2024ยท 15 min read

API Endpoint Discovery: Find Hidden APIs and Security Vulnerabilities

A practical Fusebox guide to api endpoint discovery.

API Endpoint Discovery: Find Hidden APIs and Security Vulnerabilities

Published: January 2024
Reading time: 9 minutes

APIs power modern web applications, but they're also prime targets for attackers. Hidden endpoints, misconfigured authentication, and exposed sensitive data are everywhere. Here's how to discover APIs, analyze their security, and protect against common vulnerabilities.

Why API Discovery Matters

The Hidden Attack Surface

  • Shadow APIs - Undocumented endpoints developers forgot
  • Debug endpoints - Left enabled in production
  • Version sprawl - Old API versions still accessible
  • Internal APIs - Exposed without authentication
  • Third-party integrations - Leaking data to external services

Real API Breaches

Venmo (2019): Public API exposed 200M transactions
Facebook (2019): APIs leaked 540M user records
USPS (2018): API exposed 60M users' data
T-Mobile (2023): API breach affected 37M customers
Optus (2022): Unauthenticated API exposed 10M users

Quick API Discovery

1. Browser-Based API Detection

// Discover API calls from current page
(function discoverAPIs() {
  console.log('๐Ÿ” API Endpoint Discovery\n');
  
  const apiCalls = new Map();
  const sensitiveData = [];
  
  // Override fetch to intercept API calls
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const [url, options = {}] = args;
    const method = options.method || 'GET';
    
    // Log API call
    const urlObj = new URL(url, window.location.origin);
    const endpoint = `${method} ${urlObj.pathname}`;
    
    if (!apiCalls.has(endpoint)) {
      apiCalls.set(endpoint, {
        url: urlObj.href,
        method: method,
        count: 0,
        headers: options.headers || {},
        hasAuth: false,
        responses: []
      });
    }
    
    const apiCall = apiCalls.get(endpoint);
    apiCall.count++;
    
    // Check for authentication
    if (options.headers) {
      const authHeaders = ['authorization', 'x-api-key', 'x-auth-token'];
      apiCall.hasAuth = authHeaders.some(h => 
        Object.keys(options.headers).some(key => key.toLowerCase() === h)
      );
    }
    
    // Call original fetch and intercept response
    return originalFetch.apply(this, args).then(async response => {
      const clone = response.clone();
      
      try {
        const data = await clone.json();
        apiCall.responses.push({
          status: response.status,
          size: JSON.stringify(data).length
        });
        
        // Check for sensitive data
        const sensitivePatterns = [
          /password/i,
          /secret/i,
          /token/i,
          /api[_-]?key/i,
          /private/i,
          /ssn/i,
          /credit[_-]?card/i
        ];
        
        const jsonString = JSON.stringify(data);
        sensitivePatterns.forEach(pattern => {
          if (pattern.test(jsonString)) {
            sensitiveData.push({
              endpoint: endpoint,
              pattern: pattern.toString(),
              url: url
            });
          }
        });
      } catch (e) {
        // Not JSON response
      }
      
      return response;
    });
  };
  
  // Override XMLHttpRequest
  const originalXHR = window.XMLHttpRequest;
  window.XMLHttpRequest = function() {
    const xhr = new originalXHR();
    const originalOpen = xhr.open;
    const originalSend = xhr.send;
    
    xhr.open = function(method, url, ...args) {
      xhr._method = method;
      xhr._url = url;
      return originalOpen.apply(this, [method, url, ...args]);
    };
    
    xhr.send = function(data) {
      const urlObj = new URL(xhr._url, window.location.origin);
      const endpoint = `${xhr._method} ${urlObj.pathname}`;
      
      if (!apiCalls.has(endpoint)) {
        apiCalls.set(endpoint, {
          url: xhr._url,
          method: xhr._method,
          count: 1,
          hasAuth: false,
          responses: []
        });
      } else {
        apiCalls.get(endpoint).count++;
      }
      
      return originalSend.apply(this, [data]);
    };
    
    return xhr;
  };
  
  // Wait and report findings
  setTimeout(() => {
    console.log(`\n๐Ÿ“Š API Endpoints Discovered: ${apiCalls.size}\n`);
    
    // Group by base path
    const groupedAPIs = {};
    apiCalls.forEach((details, endpoint) => {
      const basePath = endpoint.split(' ')[1].split('/').slice(0, 3).join('/');
      if (!groupedAPIs[basePath]) {
        groupedAPIs[basePath] = [];
      }
      groupedAPIs[basePath].push({ endpoint, ...details });
    });
    
    // Display findings
    Object.entries(groupedAPIs).forEach(([basePath, endpoints]) => {
      console.log(`\n๐Ÿ“ ${basePath || '/'}`);
      endpoints.forEach(api => {
        const auth = api.hasAuth ? '๐Ÿ”' : '๐Ÿ”“';
        console.log(`  ${auth} ${api.endpoint} (${api.count} calls)`);
      });
    });
    
    // Report sensitive data
    if (sensitiveData.length > 0) {
      console.log('\nโš ๏ธ SENSITIVE DATA DETECTED:');
      sensitiveData.forEach(item => {
        console.log(`  โŒ ${item.endpoint} contains ${item.pattern}`);
      });
    }
    
    // Security analysis
    const openEndpoints = Array.from(apiCalls.values()).filter(api => !api.hasAuth);
    if (openEndpoints.length > 0) {
      console.log(`\n๐Ÿ”“ Unauthenticated endpoints: ${openEndpoints.length}`);
    }
  }, 5000);
  
  console.log('โณ Monitoring API calls for 5 seconds...');
})();

2. Advanced API Discovery

// Deep API discovery with multiple techniques
async function deepAPIDiscovery() {
  console.log('\n๐Ÿ”ฌ Deep API Discovery\n');
  
  const discovered = {
    fromHTML: [],
    fromJS: [],
    fromFetch: [],
    fromComments: [],
    fromStorage: []
  };
  
  // 1. Scan HTML for API references
  console.log('1๏ธโƒฃ Scanning HTML...');
  const htmlContent = document.documentElement.outerHTML;
  const apiPatterns = [
    /[\"'](\/api\/[^\"']+)[\"']/g,
    /[\"'](\/v\d+\/[^\"']+)[\"']/g,
    /[\"'](\/rest\/[^\"']+)[\"']/g,
    /[\"'](\/graphql[^\"']*)[\"']/g,
    /endpoint[\"'\s]*[:=][\"'\s]*[\"']([^\"']+)[\"']/g
  ];
  
  apiPatterns.forEach(pattern => {
    const matches = htmlContent.matchAll(pattern);
    for (const match of matches) {
      const endpoint = match[1];
      if (!discovered.fromHTML.includes(endpoint)) {
        discovered.fromHTML.push(endpoint);
      }
    }
  });
  
  // 2. Scan JavaScript files
  console.log('2๏ธโƒฃ Scanning JavaScript files...');
  const scripts = document.querySelectorAll('script[src]');
  
  for (const script of scripts) {
    try {
      const response = await fetch(script.src);
      const jsContent = await response.text();
      
      // Look for API endpoints
      const jsPatterns = [
        /fetch\([\"']([^\"']+)[\"']/g,
        /axios\.[get|post|put|delete]+\([\"']([^\"']+)[\"']/g,
        /\$\.ajax\({[^}]*url:[\"'\s]*([^\"']+)[\"']/g,
        /XMLHttpRequest.*open\([\"'][^\"']+[\"'],[\"'\s]*([^\"']+)[\"']/g
      ];
      
      jsPatterns.forEach(pattern => {
        const matches = jsContent.matchAll(pattern);
        for (const match of matches) {
          const endpoint = match[1];
          if (!discovered.fromJS.includes(endpoint)) {
            discovered.fromJS.push(endpoint);
          }
        }
      });
    } catch (e) {
      // Skip if can't fetch
    }
  }
  
  // 3. Check browser storage
  console.log('3๏ธโƒฃ Checking browser storage...');
  
  // LocalStorage
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    const value = localStorage.getItem(key);
    
    if (value && (value.includes('/api/') || value.includes('endpoint'))) {
      discovered.fromStorage.push({ key, value: value.substring(0, 100) });
    }
  }
  
  // SessionStorage
  for (let i = 0; i < sessionStorage.length; i++) {
    const key = sessionStorage.key(i);
    const value = sessionStorage.getItem(key);
    
    if (value && (value.includes('/api/') || value.includes('endpoint'))) {
      discovered.fromStorage.push({ key, value: value.substring(0, 100) });
    }
  }
  
  // 4. Scan comments
  console.log('4๏ธโƒฃ Scanning HTML comments...');
  const commentPattern = /<!--[\s\S]*?-->/g;
  const comments = htmlContent.match(commentPattern) || [];
  
  comments.forEach(comment => {
    if (comment.includes('api') || comment.includes('endpoint')) {
      discovered.fromComments.push(comment.substring(0, 100));
    }
  });
  
  // Report findings
  console.log('\n๐Ÿ“Š Discovery Results:');
  console.log(`From HTML: ${discovered.fromHTML.length} endpoints`);
  discovered.fromHTML.slice(0, 5).forEach(ep => console.log(`  - ${ep}`));
  
  console.log(`\nFrom JavaScript: ${discovered.fromJS.length} endpoints`);
  discovered.fromJS.slice(0, 5).forEach(ep => console.log(`  - ${ep}`));
  
  console.log(`\nFrom Storage: ${discovered.fromStorage.length} items`);
  discovered.fromStorage.slice(0, 3).forEach(item => 
    console.log(`  - ${item.key}: ${item.value}`)
  );
  
  if (discovered.fromComments.length > 0) {
    console.log(`\nFrom Comments: ${discovered.fromComments.length} references`);
  }
  
  return discovered;
}

deepAPIDiscovery();

3. API Security Scanner

// Scan discovered APIs for vulnerabilities
async function scanAPISecurity(endpoint) {
  console.log(`\n๐Ÿ”’ Security Scan: ${endpoint}\n`);
  
  const vulnerabilities = [];
  const apiUrl = new URL(endpoint, window.location.origin);
  
  // Test 1: Authentication bypass
  console.log('1๏ธโƒฃ Testing authentication...');
  try {
    const response = await fetch(apiUrl);
    if (response.status === 200) {
      console.log('  โš ๏ธ No authentication required');
      vulnerabilities.push('Missing authentication');
    } else if (response.status === 401) {
      console.log('  โœ… Authentication required');
    }
  } catch (e) {
    console.log('  โŒ Network error');
  }
  
  // Test 2: HTTP methods
  console.log('\n2๏ธโƒฃ Testing HTTP methods...');
  const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
  const allowedMethods = [];
  
  for (const method of methods) {
    try {
      const response = await fetch(apiUrl, { method });
      if (response.status !== 405) {
        allowedMethods.push(method);
      }
    } catch (e) {
      // Method might be blocked by CORS
    }
  }
  
  console.log(`  Allowed methods: ${allowedMethods.join(', ')}`);
  if (allowedMethods.includes('DELETE') || allowedMethods.includes('PUT')) {
    vulnerabilities.push('Dangerous HTTP methods enabled');
  }
  
  // Test 3: CORS configuration
  console.log('\n3๏ธโƒฃ Testing CORS...');
  try {
    const response = await fetch(apiUrl, {
      headers: { 'Origin': 'https://evil.com' }
    });
    
    const corsHeader = response.headers.get('access-control-allow-origin');
    if (corsHeader === '*') {
      console.log('  โŒ CORS allows any origin (*)');
      vulnerabilities.push('Overly permissive CORS');
    } else if (corsHeader) {
      console.log(`  โš ๏ธ CORS allows: ${corsHeader}`);
    } else {
      console.log('  โœ… CORS properly restricted');
    }
  } catch (e) {
    console.log('  โœ… CORS blocked request');
  }
  
  // Test 4: Rate limiting
  console.log('\n4๏ธโƒฃ Testing rate limiting...');
  const startTime = Date.now();
  let requests = 0;
  
  for (let i = 0; i < 10; i++) {
    try {
      await fetch(apiUrl);
      requests++;
    } catch (e) {
      break;
    }
  }
  
  const duration = Date.now() - startTime;
  const rps = (requests / duration * 1000).toFixed(1);
  console.log(`  Completed ${requests} requests in ${duration}ms (${rps} req/s)`);
  
  if (requests === 10) {
    console.log('  โš ๏ธ No rate limiting detected');
    vulnerabilities.push('Missing rate limiting');
  }
  
  // Test 5: Information disclosure
  console.log('\n5๏ธโƒฃ Checking response headers...');
  try {
    const response = await fetch(apiUrl);
    const headers = response.headers;
    
    // Check for sensitive headers
    const sensitiveHeaders = {
      'server': headers.get('server'),
      'x-powered-by': headers.get('x-powered-by'),
      'x-aspnet-version': headers.get('x-aspnet-version'),
      'x-debug': headers.get('x-debug')
    };
    
    Object.entries(sensitiveHeaders).forEach(([header, value]) => {
      if (value) {
        console.log(`  โš ๏ธ ${header}: ${value}`);
        vulnerabilities.push(`Information disclosure: ${header}`);
      }
    });
  } catch (e) {
    // Skip
  }
  
  // Summary
  console.log('\n๐Ÿ“‹ Security Summary:');
  if (vulnerabilities.length === 0) {
    console.log('  โœ… No major vulnerabilities found');
  } else {
    console.log(`  โŒ ${vulnerabilities.length} vulnerabilities found:`);
    vulnerabilities.forEach(vuln => console.log(`    - ${vuln}`));
  }
  
  return vulnerabilities;
}

// Example scan
scanAPISecurity('/api/v1/users');

API Authentication Analysis

1. Authentication Method Detection

// Detect and analyze API authentication methods
function analyzeAuthentication() {
  console.log('\n๐Ÿ” API Authentication Analysis\n');
  
  const authMethods = {
    bearer: false,
    apiKey: false,
    basic: false,
    oauth: false,
    session: false,
    custom: false
  };
  
  // Intercept requests to detect auth
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const [url, options = {}] = args;
    
    if (options.headers) {
      const headers = options.headers;
      
      // Check Authorization header
      if (headers.Authorization || headers.authorization) {
        const auth = headers.Authorization || headers.authorization;
        if (auth.startsWith('Bearer ')) {
          authMethods.bearer = true;
          console.log('๐Ÿ”‘ Bearer token detected');
        } else if (auth.startsWith('Basic ')) {
          authMethods.basic = true;
          console.log('๐Ÿ”‘ Basic auth detected');
        }
      }
      
      // Check API key headers
      const apiKeyHeaders = ['x-api-key', 'api-key', 'apikey', 'x-auth-token'];
      apiKeyHeaders.forEach(header => {
        if (headers[header] || headers[header.toLowerCase()]) {
          authMethods.apiKey = true;
          console.log(`๐Ÿ”‘ API key detected in ${header}`);
        }
      });
    }
    
    // Check for OAuth
    if (url.includes('oauth') || url.includes('authorize')) {
      authMethods.oauth = true;
      console.log('๐Ÿ”‘ OAuth flow detected');
    }
    
    // Check cookies
    if (document.cookie.includes('session') || document.cookie.includes('auth')) {
      authMethods.session = true;
      console.log('๐Ÿ”‘ Session-based auth detected');
    }
    
    return originalFetch.apply(this, args);
  };
  
  // Analyze security of detected methods
  setTimeout(() => {
    console.log('\n๐Ÿ“Š Authentication Methods Used:');
    
    Object.entries(authMethods).forEach(([method, used]) => {
      if (used) {
        console.log(`\n${method.toUpperCase()}:`);
        
        switch(method) {
          case 'bearer':
            console.log('  โœ… Modern and secure when implemented correctly');
            console.log('  โš ๏ธ Ensure tokens expire and can be revoked');
            console.log('  โš ๏ธ Use secure storage (not localStorage)');
            break;
            
          case 'apiKey':
            console.log('  ๐ŸŸก Simple but less secure than OAuth');
            console.log('  โš ๏ธ Keys should be rotatable');
            console.log('  โš ๏ธ Never expose in client-side code');
            break;
            
          case 'basic':
            console.log('  โŒ Deprecated for browser apps');
            console.log('  โš ๏ธ Credentials sent in every request');
            console.log('  โš ๏ธ Must use HTTPS');
            break;
            
          case 'oauth':
            console.log('  โœ… Industry standard for third-party access');
            console.log('  โœ… Supports scoped permissions');
            console.log('  โš ๏ธ Implement PKCE for SPAs');
            break;
            
          case 'session':
            console.log('  โœ… Traditional and well-understood');
            console.log('  โš ๏ธ Vulnerable to CSRF without tokens');
            console.log('  โš ๏ธ Set secure cookie flags');
            break;
        }
      }
    });
    
    // Best practices
    console.log('\n๐Ÿ’ก Authentication Best Practices:');
    console.log('1. Use OAuth 2.0 with PKCE for SPAs');
    console.log('2. Store tokens in httpOnly cookies when possible');
    console.log('3. Implement token rotation and expiry');
    console.log('4. Use secure headers (SameSite, Secure flags)');
    console.log('5. Implement proper CORS policies');
  }, 3000);
}

analyzeAuthentication();

2. API Permission Testing

// Test API permissions and access controls
async function testAPIPermissions(baseUrl) {
  console.log(`\n๐Ÿ”“ API Permission Testing: ${baseUrl}\n`);
  
  const tests = [];
  
  // Common permission bypass techniques
  const bypassTechniques = [
    {
      name: 'HTTP Method Override',
      test: async () => {
        const response = await fetch(`${baseUrl}/users/1`, {
          method: 'POST',
          headers: {
            'X-HTTP-Method-Override': 'DELETE'
          }
        });
        return response.status !== 405;
      }
    },
    {
      name: 'Case Variation',
      test: async () => {
        const endpoints = [
          `${baseUrl}/USERS`,
          `${baseUrl}/Users`,
          `${baseUrl}/uSeRs`
        ];
        
        for (const endpoint of endpoints) {
          const response = await fetch(endpoint);
          if (response.status === 200) return true;
        }
        return false;
      }
    },
    {
      name: 'Path Traversal',
      test: async () => {
        const payloads = [
          '../admin',
          '..%2fadmin',
          '..;/admin',
          '..//..//admin'
        ];
        
        for (const payload of payloads) {
          const response = await fetch(`${baseUrl}/api/${payload}`);
          if (response.status === 200) return true;
        }
        return false;
      }
    },
    {
      name: 'Parameter Pollution',
      test: async () => {
        const response = await fetch(`${baseUrl}/user?id=1&id=2`);
        return response.status === 200;
      }
    },
    {
      name: 'Version Bypass',
      test: async () => {
        const versions = ['v0', 'v1', 'v2', 'v3', 'beta', 'alpha'];
        
        for (const version of versions) {
          const versionUrl = baseUrl.replace(/v\d+/, version);
          const response = await fetch(versionUrl);
          if (response.status === 200) return true;
        }
        return false;
      }
    }
  ];
  
  // Run tests
  for (const technique of bypassTechniques) {
    console.log(`Testing: ${technique.name}...`);
    try {
      const vulnerable = await technique.test();
      tests.push({
        name: technique.name,
        vulnerable: vulnerable,
        severity: vulnerable ? 'HIGH' : 'PASS'
      });
      
      console.log(`  ${vulnerable ? 'โŒ VULNERABLE' : 'โœ… Secure'}`);
    } catch (e) {
      console.log('  โš ๏ธ Test blocked (good sign)');
      tests.push({
        name: technique.name,
        vulnerable: false,
        severity: 'PASS'
      });
    }
  }
  
  // IDOR testing
  console.log('\nTesting for IDOR vulnerabilities...');
  const idorEndpoints = [
    '/users/{id}',
    '/accounts/{id}',
    '/orders/{id}',
    '/documents/{id}'
  ];
  
  for (const endpoint of idorEndpoints) {
    const url = `${baseUrl}${endpoint.replace('{id}', '1')}`;
    try {
      const response1 = await fetch(url);
      const response2 = await fetch(url.replace('1', '2'));
      
      if (response1.status === 200 && response2.status === 200) {
        console.log(`  โš ๏ธ Possible IDOR: ${endpoint}`);
        tests.push({
          name: `IDOR: ${endpoint}`,
          vulnerable: true,
          severity: 'CRITICAL'
        });
      }
    } catch (e) {
      // Skip
    }
  }
  
  // Summary
  console.log('\n๐Ÿ“‹ Permission Test Summary:');
  const vulnerableTests = tests.filter(t => t.vulnerable);
  
  if (vulnerableTests.length === 0) {
    console.log('โœ… No permission bypasses found');
  } else {
    console.log(`โŒ ${vulnerableTests.length} vulnerabilities found:`);
    vulnerableTests.forEach(test => {
      console.log(`  - ${test.name} (${test.severity})`);
    });
  }
  
  return tests;
}

// Example test
testAPIPermissions('/api/v1');

API Documentation Discovery

1. Find API Documentation

// Discover API documentation endpoints
async function findAPIDocumentation() {
  console.log('\n๐Ÿ“š API Documentation Discovery\n');
  
  const docEndpoints = [
    '/swagger',
    '/swagger-ui.html',
    '/swagger/index.html',
    '/api-docs',
    '/api/docs',
    '/apidocs',
    '/documentation',
    '/docs',
    '/api/swagger.json',
    '/swagger.json',
    '/openapi.json',
    '/api/openapi.json',
    '/_doc',
    '/graphql',
    '/playground',
    '/graphiql',
    '/explorer',
    '/api-explorer',
    '/console',
    '/api/console'
  ];
  
  const found = [];
  
  console.log('Checking common documentation endpoints...\n');
  
  for (const endpoint of docEndpoints) {
    try {
      const url = new URL(endpoint, window.location.origin);
      const response = await fetch(url, { method: 'HEAD' });
      
      if (response.status === 200 || response.status === 401) {
        found.push({
          endpoint: endpoint,
          status: response.status,
          type: guessDocType(endpoint)
        });
        console.log(`โœ… Found: ${endpoint} (${response.status})`);
      }
    } catch (e) {
      // Endpoint doesn't exist or blocked
    }
  }
  
  // Check for API versioning patterns
  console.log('\nChecking for versioned APIs...');
  const versionPatterns = ['/v1', '/v2', '/v3', '/api/v1', '/api/v2'];
  
  for (const pattern of versionPatterns) {
    try {
      const response = await fetch(new URL(pattern, window.location.origin));
      if (response.status < 500) {
        console.log(`๐Ÿ“Œ API version found: ${pattern}`);
      }
    } catch (e) {
      // Skip
    }
  }
  
  // Parse existing page for clues
  console.log('\nScanning page for API references...');
  const pageContent = document.documentElement.innerHTML;
  
  // Look for API keys or references
  const apiReferences = {
    swagger: /swagger|openapi/i.test(pageContent),
    graphql: /graphql|apollo|relay/i.test(pageContent),
    rest: /rest[ful]*\s*api/i.test(pageContent),
    websocket: /websocket|socket\.io|ws:/i.test(pageContent)
  };
  
  Object.entries(apiReferences).forEach(([type, found]) => {
    if (found) {
      console.log(`๐Ÿ” ${type.toUpperCase()} API references found in page`);
    }
  });
  
  // Summary
  if (found.length > 0) {
    console.log('\n๐Ÿ“‹ Documentation Endpoints Found:');
    found.forEach(doc => {
      console.log(`  ${doc.endpoint} - ${doc.type} (Status: ${doc.status})`);
    });
    
    console.log('\nโš ๏ธ Security Implications:');
    console.log('  - Exposed documentation reveals all endpoints');
    console.log('  - May include example requests with real data');
    console.log('  - Could expose internal API structure');
    console.log('  - Often includes authentication details');
  }
  
  return found;
}

function guessDocType(endpoint) {
  if (endpoint.includes('swagger')) return 'Swagger/OpenAPI';
  if (endpoint.includes('graphql') || endpoint.includes('graphiql')) return 'GraphQL';
  if (endpoint.includes('playground')) return 'API Playground';
  return 'API Documentation';
}

findAPIDocumentation();

2. GraphQL-Specific Discovery

// Discover and analyze GraphQL endpoints
async function discoverGraphQL() {
  console.log('\n๐Ÿ”ท GraphQL Endpoint Discovery\n');
  
  // Common GraphQL endpoints
  const graphqlEndpoints = [
    '/graphql',
    '/api/graphql',
    '/graphql/api',
    '/v1/graphql',
    '/graph',
    '/api/graph',
    '/query'
  ];
  
  let graphqlFound = null;
  
  // Test each endpoint
  for (const endpoint of graphqlEndpoints) {
    try {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          query: '{ __schema { queryType { name } } }'
        })
      });
      
      if (response.status === 200) {
        const data = await response.json();
        if (data.data && data.data.__schema) {
          graphqlFound = endpoint;
          console.log(`โœ… GraphQL endpoint found: ${endpoint}`);
          break;
        }
      }
    } catch (e) {
      // Continue checking
    }
  }
  
  if (!graphqlFound) {
    console.log('โŒ No GraphQL endpoint found');
    return;
  }
  
  // Introspection query
  console.log('\n๐Ÿ” Running introspection query...');
  
  const introspectionQuery = `
    query IntrospectionQuery {
      __schema {
        queryType { name }
        mutationType { name }
        types {
          ...FullType
        }
      }
    }
    
    fragment FullType on __Type {
      kind
      name
      description
      fields(includeDeprecated: true) {
        name
        description
        args {
          ...InputValue
        }
        type {
          ...TypeRef
        }
        isDeprecated
        deprecationReason
      }
    }
    
    fragment InputValue on __InputValue {
      name
      description
      type { ...TypeRef }
      defaultValue
    }
    
    fragment TypeRef on __Type {
      kind
      name
      ofType {
        kind
        name
      }
    }
  `;
  
  try {
    const response = await fetch(graphqlFound, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ query: introspectionQuery })
    });
    
    const result = await response.json();
    
    if (result.data) {
      console.log('โœ… Introspection enabled (SECURITY RISK)');
      
      // Analyze schema
      const schema = result.data.__schema;
      const types = schema.types.filter(t => !t.name.startsWith('__'));
      
      console.log(`\n๐Ÿ“Š Schema Analysis:`);
      console.log(`  Custom Types: ${types.length}`);
      console.log(`  Queries: ${schema.queryType ? 'Yes' : 'No'}`);
      console.log(`  Mutations: ${schema.mutationType ? 'Yes' : 'No'}`);
      
      // Look for sensitive fields
      console.log('\nโš ๏ธ Potentially Sensitive Fields:');
      const sensitivePatterns = /password|secret|token|key|private|ssn|credit/i;
      
      types.forEach(type => {
        if (type.fields) {
          type.fields.forEach(field => {
            if (sensitivePatterns.test(field.name)) {
              console.log(`  - ${type.name}.${field.name}`);
            }
          });
        }
      });
      
      // Security recommendations
      console.log('\n๐Ÿ”’ Security Recommendations:');
      console.log('  1. Disable introspection in production');
      console.log('  2. Implement query depth limiting');
      console.log('  3. Add rate limiting per query complexity');
      console.log('  4. Use field-level authorization');
      console.log('  5. Implement query whitelisting');
    } else {
      console.log('โœ… Introspection disabled (good)');
    }
  } catch (e) {
    console.log('โŒ Failed to run introspection:', e.message);
  }
}

discoverGraphQL();

Complete API Security Audit

Comprehensive API Assessment

// Run complete API security audit
async function completeAPIAudit() {
  console.log('๐Ÿ” COMPLETE API SECURITY AUDIT');
  console.log('โ•'.repeat(50));
  console.log(`Domain: ${window.location.hostname}`);
  console.log(`Date: ${new Date().toLocaleDateString()}\n`);
  
  const audit = {
    endpoints: [],
    vulnerabilities: [],
    score: 100,
    recommendations: []
  };
  
  // Phase 1: Discovery
  console.log('๐Ÿ“ก PHASE 1: API DISCOVERY');
  console.log('โ”€'.repeat(40));
  
  // Collect all network requests
  const requests = new Set();
  
  // Monitor for 5 seconds
  const originalFetch = window.fetch;
  window.fetch = function(...args) {
    const [url] = args;
    const urlObj = new URL(url, window.location.origin);
    if (urlObj.origin === window.location.origin) {
      requests.add(urlObj.pathname);
    }
    return originalFetch.apply(this, args);
  };
  
  console.log('Monitoring API calls for 5 seconds...');
  
  await new Promise(resolve => setTimeout(resolve, 5000));
  
  // Restore fetch
  window.fetch = originalFetch;
  
  audit.endpoints = Array.from(requests);
  console.log(`\nDiscovered ${audit.endpoints.length} endpoints`);
  
  // Phase 2: Authentication Analysis
  console.log('\n๐Ÿ” PHASE 2: AUTHENTICATION ANALYSIS');
  console.log('โ”€'.repeat(40));
  
  // Check for common auth issues
  const authChecks = {
    'No HTTPS': window.location.protocol !== 'https:',
    'Credentials in URL': audit.endpoints.some(ep => ep.includes('password') || ep.includes('token')),
    'API key in URL': audit.endpoints.some(ep => ep.includes('api_key') || ep.includes('apikey')),
    'No auth headers': true // Would need to intercept actual requests
  };
  
  Object.entries(authChecks).forEach(([issue, found]) => {
    if (found) {
      console.log(`โŒ ${issue}`);
      audit.vulnerabilities.push(issue);
      audit.score -= 10;
    } else {
      console.log(`โœ… ${issue}: Secure`);
    }
  });
  
  // Phase 3: Security Headers
  console.log('\n๐Ÿ›ก๏ธ PHASE 3: SECURITY HEADERS');
  console.log('โ”€'.repeat(40));
  
  // Test a sample endpoint
  if (audit.endpoints.length > 0) {
    try {
      const testEndpoint = audit.endpoints.find(ep => ep.includes('/api/')) || audit.endpoints[0];
      const response = await fetch(testEndpoint);
      
      const securityHeaders = {
        'X-Content-Type-Options': response.headers.get('x-content-type-options'),
        'X-Frame-Options': response.headers.get('x-frame-options'),
        'Content-Security-Policy': response.headers.get('content-security-policy'),
        'Strict-Transport-Security': response.headers.get('strict-transport-security')
      };
      
      Object.entries(securityHeaders).forEach(([header, value]) => {
        if (!value) {
          console.log(`โŒ Missing: ${header}`);
          audit.vulnerabilities.push(`Missing ${header}`);
          audit.score -= 5;
        } else {
          console.log(`โœ… ${header}: ${value}`);
        }
      });
    } catch (e) {
      console.log('โš ๏ธ Could not test headers');
    }
  }
  
  // Phase 4: Common Vulnerabilities
  console.log('\n๐Ÿ” PHASE 4: VULNERABILITY SCAN');
  console.log('โ”€'.repeat(40));
  
  const vulnerabilityTests = {
    'Exposed GraphQL': await checkForGraphQL(),
    'Swagger UI exposed': await checkForSwagger(),
    'Directory listing': await checkDirectoryListing(),
    'Debug endpoints': audit.endpoints.some(ep => ep.includes('debug') || ep.includes('test')),
    'Admin endpoints': audit.endpoints.some(ep => ep.includes('admin'))
  };
  
  Object.entries(vulnerabilityTests).forEach(([vuln, found]) => {
    if (found) {
      console.log(`โŒ ${vuln}`);
      audit.vulnerabilities.push(vuln);
      audit.score -= 15;
    } else {
      console.log(`โœ… ${vuln}: Not found`);
    }
  });
  
  // Final Score
  audit.score = Math.max(0, audit.score);
  
  console.log('\n๐Ÿ“Š AUDIT SUMMARY');
  console.log('โ•'.repeat(50));
  console.log(`Security Score: ${audit.score}/100`);
  console.log(`Endpoints Found: ${audit.endpoints.length}`);
  console.log(`Vulnerabilities: ${audit.vulnerabilities.length}`);
  
  // Risk Assessment
  let risk = 'LOW';
  if (audit.score < 50) risk = 'CRITICAL';
  else if (audit.score < 70) risk = 'HIGH';
  else if (audit.score < 85) risk = 'MEDIUM';
  
  console.log(`Risk Level: ${risk}`);
  
  // Recommendations
  console.log('\n๐Ÿ’ก RECOMMENDATIONS:');
  
  if (window.location.protocol !== 'https:') {
    console.log('1. Enable HTTPS immediately');
  }
  if (audit.vulnerabilities.includes('Exposed GraphQL')) {
    console.log('2. Disable GraphQL introspection');
  }
  if (audit.vulnerabilities.includes('Swagger UI exposed')) {
    console.log('3. Restrict API documentation access');
  }
  console.log('4. Implement rate limiting');
  console.log('5. Add security headers to all responses');
  console.log('6. Use OAuth 2.0 for authentication');
  console.log('7. Implement API versioning');
  console.log('8. Monitor for suspicious activity');
  
  return audit;
}

// Helper functions
async function checkForGraphQL() {
  try {
    const response = await fetch('/graphql', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ query: '{ __schema { queryType { name } } }' })
    });
    return response.status === 200;
  } catch (e) {
    return false;
  }
}

async function checkForSwagger() {
  try {
    const response = await fetch('/swagger-ui.html');
    return response.status === 200;
  } catch (e) {
    return false;
  }
}

async function checkDirectoryListing() {
  try {
    const response = await fetch('/api/');
    const text = await response.text();
    return text.includes('Index of') || text.includes('Directory listing');
  } catch (e) {
    return false;
  }
}

// Run the audit
completeAPIAudit();

The Bottom Line

API security is often an afterthought, but it shouldn't be:

  • APIs are everywhere - Every dynamic site has them
  • Documentation exposes everything - Swagger/GraphQL tells attackers exactly what to target
  • Authentication is often broken - Tokens in URLs, missing rate limits
  • Old versions persist - v1 APIs with known vulnerabilities still running
  • GraphQL amplifies risk - One query can fetch entire databases

Find your APIs. Lock them down. Monitor continuously. Your data depends on it.


Discover APIs instantly: Fusebox finds hidden endpoints, analyzes authentication, and detects API vulnerabilities while you browse. Secure your API attack surface. $29 one-time purchase.