TL;DR: Twenty findings are usually four causes expressed across twenty pages. I grouped them, ordered the fixes by dependency instead of severity, and explain why mail authentication came first.
The scenario
I ran this as a black-box review: no credentials, only the publicly visible surface. Here is what I saw before I touched anything.
A global industrial supplier running a multilingual content platform with a large media library, an online catalogue and customer service integration. Assessment covered the public content surface, the media library, mail authentication, header policy and the plugin estate.
I removed all identifying information. The target is described only by sector and technology class so the pattern can be reused.
The pattern
The pattern I recognised — and that I keep finding in similar estates:
Twenty findings is not twenty problems. It is usually three or four problems expressed across twenty pages. The same missing header appears on every template, the same user enumeration endpoint serves the same eight accounts on every language variant, the same mail policy weakness affects every newsletter. Counting occurrences inflates the report and obscures the plan, which is why the useful output of an assessment this size is a grouping.
Once grouped, the ordering follows from dependencies rather than severity. Mail authentication is first because phishing against a supplier is the most plausible scenario and the fix is a DNS change nobody can revert by accident. Header policy is second because it is a one-line change per tier that stops a class of attacks permanently. User enumeration is third, because it has no direct exploit but is the entry condition for credential attacks, and it is the one finding that will come back if the site is redeployed from a template.
The category that is usually last and should not be is version hygiene. Plugins at current releases create no work; plugins past end of life create permanent work and are usually owned by an agency, which makes them a conversation rather than a task. Doing the permanent work early while the agency relationship is cooperative is worth more than doing it late when it is not.
Findings
| Severity | Finding | Evidence |
|---|---|---|
| HOCH | User enumeration across all language variants | Same accounts exposed on every locale |
| HOCH | No security headers on any template | Template-level gap, not a per-page gap |
| MEDIUM | Mail authentication not enforced for the sending domain | Newsletter and order mail spoofable |
| MEDIUM | Media library enumerable without authentication | Full asset inventory readable |
| MEDIUM | Administrative paths reachable on a predictable prefix | Consistent discovery path |
| LOW | Plugin and core versions exposed | Exact build identifiable across locales |
The attack path
Reproduction in my lab
Every command below targets a lab container I control. Nothing here is aimed at a live system.
Group your own findings by cause rather than by page
# the same finding across every locale of your own site
$ for loc in en de fr it; do
printf '%-4s users=%s headers=%s\n' "$loc" \
"$(curl -s "https://example.org/$loc/wp-json/wp/v2/users" | grep -c '"slug"')" \
"$(curl -sI "https://example.org/$loc/" | grep -ciE 'content-security-policy|x-frame-options')"
done
Detection in your own estate
Measure the template gap across every locale you own
$ for loc in en de fr it es; do
n=$(curl -sI "https://example.org/$loc/" | grep -ciE 'content-security-policy|x-frame-options|x-content-type-options|referrer-policy')
printf '%-4s security-headers=%s/4\n' "$loc" "$n"
done
# and the media inventory
$ curl -s 'https://example.org/wp-json/wp/v2/media?per_page=100' \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print("media objects:", len(d))'
Remediation
Fix the template once, not twenty pages
# put the baseline in the virtual host, not in each template
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; frame-ancestors 'self'"
</IfModule>
ServerTokens Prod
ServerSignature Off
Once an attacker gains access, they can perform the following actions:
Takeaways
- Twenty findings are usually four causes. Group before you prioritise.
- Fix the template, not the pages; page-level fixes do not survive a redesign.
- Mail authentication is DNS, it is hard to revert by accident, and phishing is the plausible threat.
- Templates get redeployed from a clean copy. Add the header baseline to the repository, not the server.