UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

From scan to remediation: a walkthrough on a real domain

Posted 2026-04-29 · 9 min read · walkthroughremediation

Reports are easy to write in the abstract. So we picked a real production domain — our own parent site unveiltech.com — pointed UnveilScan at it, and walked through every Extended finding in order, applying the fix live. Total elapsed time: about two hours. Score moved from 64 / D to 92 / A.

Below is the play-by-play. Every snippet you see was actually pasted into the production Apache config (yes, it's a 50-vhost Apache 2.4.38 / Debian Buster server — typical mid-size SaaS infrastructure, not a fresh slate).

Initial state: 64 / D

CategorySubscore
DNS85
TLS40
WEB50
EMAIL80

Pattern: classic Apache server that nobody touched in two years. TLS and WEB drag the weighted average. Let's go.

Finding 1 — CRITICAL TLS 1.0 + 1.1 enabled

Same one we wrote about in the Google article. Apache 2.4.38 ships with permissive defaults that allow legacy versions.

# /etc/apache2/mods-available/ssl.conf
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder off
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!DES:!CAMELLIA
$ apachectl configtest
Syntax OK
$ systemctl reload apache2

+18 global points (TLS 40 → 70).

Finding 2 — HIGH Mixed content on HTTPS pages

Found 87 files referencing http:// resources. The CSP wouldn't have caught this on its own; the dedicated mixed_content checker flagged it.

Two-line bash to upgrade-and-replace, run from the document root:

grep -rl "http://www.unveiltech.com" . | xargs sed -i 's|http://www.unveiltech.com|https://www.unveiltech.com|g'

Plus a header to catch the long tail at the browser level:

# /etc/apache2/conf-enabled/security.conf
Header always set Content-Security-Policy "upgrade-insecure-requests"

+9 global points (WEB 50 → 80).

Finding 3 — HIGH Apache version disclosed in headers

Server: Apache/2.4.38 (Debian). Tells attackers which Apache CVEs to try. Especially fun on Buster, which has a long list.

# /etc/apache2/conf-enabled/security.conf
ServerTokens Prod
ServerSignature Off

Now responds with Server: Apache only. The CVE the scanner cross-referenced via OSV.dev (we use the techcve checker) drops because the version is no longer leaked.

+9 global points.

Finding 4 — MEDIUM HSTS missing

# Inside the <VirtualHost *:443>, after the SSL directives:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

max-age is in seconds: 31,536,000 = 1 year. includeSubDomains extends the policy to every subdomain, which is what you want unless you have a specific reason to exclude one.

Don't add preload until you're sure: getting on the preload list takes 6 weeks and getting off takes a year.

+4.5 global points.

Finding 5 — MEDIUM CSP missing

Server didn't ship a Content-Security-Policy header at all. We started with a permissive policy (don't break anything), then tightened.

# Day 1 — observe and report
Header always set Content-Security-Policy "default-src 'self' https:; \
  script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; \
  img-src 'self' data: https:; \
  style-src 'self' 'unsafe-inline'; \
  frame-ancestors 'none'; \
  base-uri 'self'; \
  upgrade-insecure-requests; \
  report-uri /csp-report"

Then we set up a /csp-report handler that logs violations, watched the logs for a day, refined. Final policy ditches 'unsafe-inline' on script-src in favour of SHA-256 hashes for the few inline scripts the page actually contains (full CSP article for that journey).

+4.5 global points.

Findings 6-9 — MEDIUM+LOW miscellaneous headers

One block fixes four findings:

Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "no-referrer"
Header always set Permissions-Policy "interest-cohort=()"
Header always set Cross-Origin-Opener-Policy "same-origin"

These are cookie-cutter — apply to every server you operate, no exceptions. X-Frame-Options DENY is redundant with frame-ancestors 'none' in CSP but covers older browsers. interest-cohort=() opts out of FLoC.

+6 global points.

Finding 10 — MEDIUM No security.txt

RFC 9116 file. Two minutes of work:

$ cat > /var/www/.well-known/security.txt <<'EOF'
Contact: mailto:security@unveiltech.com
Expires: 2027-04-29T00:00:00Z
Preferred-Languages: en, fr
Canonical: https://www.unveiltech.com/.well-known/security.txt
Policy: https://www.unveiltech.com/security-policy
EOF

Don't forget the MIME type — Apache's default text/plain for .txt matches the RFC. Some scanners (including ours) flag it as malformed if the response Content-Type is text/html because of a SPA fallback. Check yours.

+1.5 global points.

Findings 11-12 — MEDIUM DMARC weak / SPF over 10 lookups

DMARC was at p=none; pct=100. We bumped to p=quarantine after a week of monitoring (RUA inbox showed everything aligning).

SPF was at 12 DNS lookups (RFC 7208 §4.6.4 hard limit is 10). We replaced two include: directives that we no longer used and dropped to 7.

# Final SPF
"v=spf1 ip4:5.196.10.27 include:_spf.google.com include:sendgrid.net -all"

+6 global points.

Finding 13 — LOW Cookies missing flags

The Apache config sets cookies via mod_session. We just added flags:

Header edit Set-Cookie ^(.*)$ "$1; Secure; HttpOnly; SameSite=Lax"

(In a fresh project, set these from your application code — but for monkey-patching an old Apache, header edit works.)

+2 global points.

Final state: 92 / A

CategoryBeforeAfterΔ
DNS8590+5
TLS40100+60
WEB5090+40
EMAIL8095+15
Global64 / D92 / A+28

What's left? A handful of LOW findings (DKIM key rotation overdue, NS not geographically diverse) we'll get to in the next maintenance window. The score won't reach 100 because two checkers flagged us with errors (geographic ASN diversity is hard to fix without a second hosting provider). But 92 / A clears every compliance threshold we care about.

Time investment. Two hours, all-in. The longest single step was rolling the CSP — that was an evening of report-uri-watching to make sure no third-party widget broke. Everything else was well under five minutes per finding.

Lessons

  1. Order matters. Fix the CRITICAL first, then HIGH, then MEDIUM. Each fix exposes the next layer cleanly.
  2. Trust the snippets. UnveilScan's per-finding snippets are real config — copy-pasted into nginx/Apache they work first try, with the right server-specific syntax. We tested every snippet against actual deployments before shipping.
  3. Re-scan after each batch. Use the per-checker re-check button to verify a fix without running the whole 87-checker Extended again. Saves time and gives instant feedback.
  4. Some findings are environmental. "ASN single-provider" can't be fixed without a second hosting contract. Suppress those with a reason and move on; the scorer respects suppressions.

Run the same playbook on your domain

Free Basic scan now, Extended for full leak / CVE / takeover audit.

Run a scan