# Mesozoa
Mesozoa is a small animal living between a reverse-proxy and a server, protecting the server from crawlers by forcing the browser to run proof of work.
It inspects request's HTTP header and passes the socket to the server directly (zero-copy).
[Try it online.](https://git.txmn.tk/tuxmain/mesozoa/commits/branch/main) (remove the cookie `mesozoa-proof` or change User-Agent to renew the experience)
## Why?
Why not Anubis? Because it provides no build instructions and only supports Docker.
Why not using Realm completely? Because the hook system is useless and only allows filtering.
And because it looked like a fun little project.
## Install
### Build
[Install rustup](https://rustup.rs) and a nightly Rust toolchain.
cargo build --release
### Run
./target/release/mesozoa -c example-config.yaml
### Apache config
Note that the reverse-proxy must provide the HTTP header `X-Forwarded-For`.
Add this to your virtual host:
```
ProxyPreserveHost On
ProxyRequests Off
ProxyTimeout 600
ProxySet keepalive=Off
ProxyPass http://127.0.0.1:8504/
```
**Note on keepalive**: When keepalive is On, connections between Apache and server are re-used, even for requests from different clients.
This increases server performance as it reduces connection overhead, but prevents Mesozoa from intercepting HTTP headers.
Hence we have to disable keepalive around Mesozoa. This does not prevent using keepalive between Apache and client.
## Challenge protocol
### Challenge generation
Sent by the server as a cookie.
* `secret`: chosen randomly at startup
* `salt`: chosen randomly each time
* `timestamp`: UNIX time in seconds, 64 bits, big endian
* `ua`: `User-Agent` from request header
* `ip`: `X-Forwarded-For` from request header (client's IP)
`set-cookie: mesozoa-challenge=BASE64(salt || timestamp || SHA3-256(secret || salt || timestamp || ip || "/" || ua))`
Where `BASE64` is URL-safe unpadded.
### Challenge verification
Request must contain both cookies `mesozoa-challenge` and `mesozoa-proof`.
Server checks challenge is correct and timestamp not too old.
`cookie: mesozoa-proof=nonce`
`hash = SHA2-256(nonce || challenge)`
Client must find a `nonce` matching `/[0-9a-zA-Z_-]{8}/` such that `hash` starts with at least some number of zeros (in binary representation, MSB-first).
## Security
### Network handling and HTTP parsing
This implementation uses cheap tricks and regexes, is probably not fully compliant to HTTP specs, etc.
You should probably not expose it directly to an open network.
Please use it behind a safer reverse proxy like Apache or Nginx.
### Length-extension attack
SHA3 (used as a MAC in the challenge cookie) is not vulnerable. Values in the hash are either fixed-length, safe, or delimited.
SHA2 (used for PoW) is vulnerable but nonce is at the beginning so this is not a problem.
### PoW
I would like a better PoW: memory-bound and ideally non-parallel. Cuckoo seems a good candidate.
## Contribution
Patches and forks are welcome. Send an e-mail to `tuxmain ât zettascript ðøt org`.
If people are interested, I may switch to a public forge like Codeberg.
The "A" in GNU AGPL means that if you host a publicly available instance of a modified version, then you should also make the modified source code available to users.
For example, this can take the form of a link to a repository in the challenge page or in the protected website.
As the challenge page's source code is directly distributed by the server, you can modify it independently.
(unless adding a compiled object, like WASM. Then you have to publish its source.)
Configuration file can be modified and kept secret, of course.
## License
[Support me via LiberaPay](https://liberapay.com/tuxmain/donate)
No LLM was used to write this program.
GNU AGPL v3, CopyLeft 2025 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.