# Apache directives: refuse DokuWiki's media manager to cookieless clients

THIS IS THE ACTUAL FIX for the crawl. The fail2ban jail is only hygiene on top of it.

## Where to put this

Two options, both fine. If the host is managed by a panel that regenerates vhost config on save (ISPConfig3, Plesk, cPanel), never edit the generated vhost file directly — it will be discarded on the next panel update or rebuild. On a hand-managed Apache install with no such panel, editing the vhost/conf file directly is completely normal; skip straight to Option 2 instead of Option 1. See `../README.md` for which of these steps are panel-specific.

### Option 1 — `.htaccess` in the wiki's web root

RECOMMENDED WHILE TUNING, and the only option if you have no panel-level directives field. Takes effect on save: no panel round-trip, no vhost regeneration, no Apache reload, and a mistake breaks only this site rather than preventing Apache from starting. Requires `AllowOverride` to include `FileInfo` — verify with:

```bash
grep -rn "AllowOverride" /etc/apache2/sites-enabled/ | grep -i <this-vhost>
```

**PASTE AT THE TOP** of DokuWiki's existing root `.htaccess`, ABOVE DokuWiki's own `RewriteRule`s. Those pretty-URL rules end with `[QSA,L]`; placing this block after them means the `[L]` may already have ended rule processing. Omit the `RewriteEngine` line if one is already present above.

### Option 2 — vhost-level, once the rule is proven

On a panel-managed host use the panel's own directives field (e.g. in ISPConfig3: Sites > the site > Options tab > "Apache Directives"); on a hand-managed host, put it directly in the vhost's `<Directory>`/`<VirtualHost>` block. Slightly faster (no per-request `.htaccess` stat walk) and immune to a DokuWiki upgrade overwriting the web root. Repeat for each wiki vhost.

Suggested path: Option 1 to get it working and iterate, then move it to Option 2 once the curl checks below pass. If you leave it in `.htaccess`, note it somewhere so a future DokuWiki upgrade does not silently drop it.

## The rule

```apache
<IfModule mod_rewrite.c>
    RewriteEngine On

    # Any request carrying do=media in the query string, from a client that
    # sends no Cookie header at all, is refused by Apache without ever
    # invoking PHP.
    RewriteCond %{QUERY_STRING} (?:^|&)do=media(?:&|$)
    RewriteCond %{HTTP_COOKIE} ^$
    RewriteRule ^ - [F,L]
</IfModule>
```

## Why "no cookies at all" rather than a named session cookie

DokuWiki's session cookie name comes from the `DOKU_SESSION_NAME` constant (`inc/init.php` calls `session_name(DOKU_SESSION_NAME)`), which can be overridden in `inc/preload.php`. Keying the rule on a literal name would break silently on an upgrade or a config change.

More importantly, a session cookie is NOT proof of being logged in — DokuWiki starts a session for anonymous readers too. So no cookie test can actually distinguish "logged-in editor" from "anonymous browser".

The cookieless test sidesteps both problems. It works because of how this specific attack behaves: 9 IPs, one or two requests each, no prior page load, therefore no cookies. Any real browser that has loaded a single wiki page already has a cookie and is unaffected.

**Be clear about what this is:** a cheap, name-independent crawler filter, not an authentication gate. A crawler that accepts cookies and fetches one page first would get through. If you need a true gate, see [Stronger options](#stronger-options-if-the-cookieless-filter-proves-insufficient) below.

What this does NOT block, deliberately:

- `/lib/exe/fetch.php?media=...` — the images themselves
- `/lib/exe/detail.php?media=...` — the single-image detail view
- normal page views, search, diffs, exports

Anonymous readers keep a fully working wiki. Because Apache answers with 403 before PHP starts, each hit becomes a few hundred bytes of static response instead of a DokuWiki page render — which is what solves the resource exhaustion, regardless of how many IPs the attacker rotates.

## Verify

Run from a shell, not from a logged-in browser:

```bash
# Cookieless media manager - must print 403
curl -s -o /dev/null -w '%{http_code}\n' \
  'https://kb.unixservertech.com/start?do=media&ns=wiki'

# Same request WITH any cookie - must print 200, proving editors pass
curl -s -o /dev/null -w '%{http_code}\n' -b 'x=1' \
  'https://kb.unixservertech.com/start?do=media&ns=wiki'

# Image fetch must still work - must print 200
curl -s -o /dev/null -w '%{http_code}\n' \
  'https://kb.unixservertech.com/lib/exe/fetch.php?media=software:rust:rustdeskdownload.png'
```

Then open the media manager in a logged-in browser and confirm it works.

## Also worth doing (one line, helps with well-behaved crawlers)

Add to the wiki's `robots.txt`, so compliant crawlers never enter the parameter space at all:

```
Disallow: /*?do=
```

This does nothing against the swarm in this incident — it ignored `robots.txt` — but it keeps legitimate crawlers out of the trap.

## Stronger options, if the cookieless filter proves insufficient

1. **IP allowlist** — a real gate, no DokuWiki dependency, but you must maintain it and it blocks editing from arbitrary locations:

   ```apache
   RewriteCond %{QUERY_STRING} (?:^|&)do=media(?:&|$)
   RewriteCond %{REMOTE_ADDR} !^203\.0\.113\.
   RewriteRule ^ - [F,L]
   ```

2. **HTTP Basic auth scoped to `do=media`** — a real gate, but editors get a second login prompt on top of DokuWiki's own.

3. **Handle it inside DokuWiki** with a small action plugin that hooks the media manager and checks `auth_isadmin()`/`auth_quickaclcheck()`. This is the only place a genuine "is this user an editor" decision can be made; Apache cannot see it.
