Skip to main content

The First Misconfigurations Attackers Probe on Your Web App

Journal / Field notes
FIELD NOTE Field note

The First Misconfigurations Attackers Probe on Your Web App

I spend a lot of time looking at how attackers approach a brand new web app. The first thing they do is not some fancy reverse‑engineered exploit, they just scan for the low‑hanging misconfigurations that give them a foothold. In my view the most common checks are cheap, fast, and often missed by busy dev teams.

Open Directory Listings

If the web server is set to show a directory index when there is no default file, the attacker can walk through the file tree, pull source files, configuration snippets, or even backup archives. Apache and Nginx both have a flag for this.

# Apache
Options -Indexes
# Nginx
autoindex off;

After you turn the flag off, a quick curl -I http://example.com/secret/ should return a 403 or 404 instead of a list of files. I think it’s worth testing a few URLs manually or with a tiny script to make sure the server is not leaking anything.

Missing Security Headers

Headers like Content‑Security‑Policy, X‑Content‑Type‑Options, and Strict‑Transport‑Security are easy to forget. An attacker can sniff the response and see that the site is not telling the browser to enforce any policy. Adding the headers is just a matter of editing the server config or the app’s middleware.

# Example for Nginx
add_header X-Content-Type-Options nosniff;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'";

Run curl -I https://example.com and look for the header lines. If they are missing, that is a red flag for an attacker scanning the site.

Exposed Version Information

Many frameworks leak the version number in HTTP headers or in error pages. A line like Server: Apache/2.4.41 (Ubuntu) or a stack trace that mentions Django 3.2.5 can give an attacker a clue about which CVEs apply. Strip those out.

# Apache
ServerTokens Prod
ServerSignature Off
# Django (settings.py)
DEBUG = False

After you hide the version, a generic Server: nginx or no server header at all is what I look for when testing.

Unrestricted CORS

CORS policies that allow any origin with * are a playground for cross‑site attacks. An attacker can host a malicious page, load your API, and read the data because the browser thinks it’s allowed.

# Node/Express example
app.use(cors({
    origin: ['https://myapp.com']
}));

If you see Access-Control-Allow-Origin: * in the response header, that is a quick win for an attacker. Tighten the list to only the domains you serve.

Default or Unsecured Admin Interfaces

Many platforms ship with an admin UI at /admin, /phpmyadmin, or /wp-admin. If those endpoints are left open without authentication, an attacker can crawl to them and try default credentials.

# Example: deny access to /phpmyadmin
location /phpmyadmin {
    deny all;
    # or require auth_basic "Restricted";
}

I always try curl -I http://example.com/admin and see if a 401 or a login page appears. If the server returns a 200 with a form, that is a sign that the admin UI is exposed.

Debug Mode Enabled in Production

Frameworks like Flask, Django, or Rails have a debug flag that shows stack traces, environment variables, and even a console in the browser. If DEBUG=True is left on, an attacker can trigger an error and pull a lot of internal data.

# Flask example
app = Flask(__name__)
app.config['DEBUG'] = False

A quick curl -I https://example.com/trigger-error that returns a big HTML dump with a traceback tells the attacker they have a foothold.

How to Detect These Issues Quickly

I bake a few curl commands into a shell script and run it against every new subdomain. The script checks for directory listings, missing headers, open admin pages, and version info. Here is a minimal version:

#!/bin/bash
target=$1

# Check directory listing
curl -s -o /dev/null -w "%{http_code}" $target/ | grep -q 200 && echo "Possible index listed"

# Check security headers
curl -sI $target | grep -i "x-content-type-options" || echo "Missing X-Content-Type-Options"
curl -sI $target | grep -i "strict-transport-security" || echo "Missing HSTS"

# Check CORS
curl -sI $target | grep -i "access-control-allow-origin: *" && echo "Wildcard CORS"

# Check admin endpoint
curl -s -o /dev/null -w "%{http_code}" $target/admin && echo "Admin page returned $?

Running this script on a staging copy of the site before you push to production catches the majority of the easy wins attackers look for.

Mitigation Checklist

  • Disable directory listings on all servers.
  • Add security headers: CSP, HSTS, X‑Content‑Type‑Options.
  • Mask version strings in HTTP headers and error pages.
  • Restrict CORS origins to your own domains.
  • Block or protect admin interfaces with IP allow‑list or strong auth.
  • Turn off debug mode in production environments.

These items are not a silver bullet, but they are the first things an attacker checks. If you get them right, you raise the bar enough that most opportunistic scans move on to a more promising target.

Final Thought

In my view the best defense is to treat the web app like a building under construction. Walk the perimeter, look for unlocked doors, and lock them before the crowds arrive. A quick scan with curl and a few config checks can save you from a whole class of attacks that start with a simple misconfiguration.

Photo by panumas nikhomkhai on Pexels