PoW anti-crawler middle-proxy https://meso.zoai.re/
Find a file
2025-04-06 11:56:40 +02:00
src More redaction on readme and challenge page 2025-04-06 11:56:40 +02:00
.gitignore Initial commit 2025-03-29 22:15:07 +01:00
Cargo.lock working base 2025-04-06 00:03:47 +02:00
Cargo.toml working base 2025-04-06 00:03:47 +02:00
LICENSE working base 2025-04-06 00:03:47 +02:00
README.md More redaction on readme and challenge page 2025-04-06 11:56:40 +02:00
rustfmt.toml working base 2025-04-06 00:03:47 +02:00

Mesozoa

Intermediate reverse proxy that protects your server from crawlers by forcing the browser to run proof of work.

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

Install rustup and a nightly Rust toolchain.

# Build executable at ./target/release/mesozoa
cargo build --release

Must be used behind a reverse proxy providing X-Forwarded-For.

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

GNU AGPL v3, CopyLeft 2025 Pascal Engélibert (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/.