Download the PHP package mulertech/scanner-ban-bundle without Composer
On this page you can find all versions of the php package mulertech/scanner-ban-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package scanner-ban-bundle
ScannerBanBundle
Bans scanners on what they request, never on what they claim to be.
The rule
A User-Agent is a string the client writes about itself. It can cost its sender something, never earn
them anything. A requested path is different: asking for /.env or /wp-login.php is the scan
itself, and giving it up means giving up the scan. So the weight of a rejected request comes from
the path, and identity only ever gets in the way of an exemption.
| Rejected request | Weight |
|---|---|
A path from an attack list (/.env, /.git/config, /wp-login.php) |
probe_weight, enough to ban on its own |
| A plausible path of the site | not_found_weight: a broken link elsewhere, a stale bookmark, a retired article |
| A resource of a page (asset prefix, asset extension, subresource fetch) | nothing |
A POST on the login route rejected as a bad request |
probe_weight |
That last line covers credential probing: a login form always sends its username field, so a bad
request on that route came from a client posting foreign field names. The rule keys on the route,
not on the field name, so renaming _username changes nothing.
Two consequences worth knowing:
- A page resource never counts. One page asking for five missing images produces five rejections the visitor never asked for, and that is the likeliest false positive there is.
- A crawler is exempt only once its address confirms its claim, through the reverse then forward
lookup its operator documents. An unverified
Googlebotheader is measured like any other client, and it never lifts a ban that is already in place.
The bundle also answers a rejected probe itself, with an empty 400, which keeps it off the error
channel. An anonymous client error is not an application failure and must not page a human; the
warning it logs instead is the trace it leaves.
What it is not
It does not replace a Content Security Policy, and it does not need one. A CSP is a set of instructions carried out by the visitor's browser while it renders your page. A scanner never renders your page, so it never carries them out: the policy has no effect on it whatsoever. The two protect different parties, the visitor against injected content and the server against being probed, and neither depends on the other.
Requirements
- PHP 8.4 or later
- Symfony 7.0 or 8.0
- A PSR-6 cache pool shared between requests (
cache.appby default) framework.trusted_proxiesset correctly if the application sits behind a reverse proxy. Without itRequest::getClientIp()returns the proxy address on every request, and the first ban locks out every visitor at once.
Installation
Register the bundle if Flex did not:
Enabling it in prod alone is the usual choice: a development session produces missing paths all
day long, and a test suite has no reason to be counted.
Usage
The bundle works with no configuration beyond the login route. Everything below shows its defaults.
allowed_ips takes a comma separated list of addresses or CIDR ranges. They are never counted and
never blocked, and a malformed request coming from one of them stays loud: from an address you
own, it is a client bug worth an alert, and silencing it would hide the only signal there is.
The loopback is always allowed, whatever allowed_ips holds. A request whose client address is the
loopback came from inside the container: a health check, a post-deployment probe, the application
calling itself. Such a caller arrives with the User-Agent of the tool that made the call, curl or
none at all, which is exactly what the blocklist refuses.
Keep the operational values in the project, not in a shared example. The thresholds published here are defaults, so a site that leaves them untouched is a site whose thresholds are public.
Lists
A project serving CSP reports should add its report endpoint to exempt_paths: browsers post there
on their own, with no session and no referrer, and a burst must never read as a sweep.
Verified crawlers
Only crawlers whose operator documents a reverse lookup can appear here, because the entry grants an
exemption and an unverifiable entry is a hole. A crawler that publishes address ranges instead of
reverse records does not need one: it does not request /.env, so the path rule already leaves it
alone.
Digest
The bundle ships a notifier that writes the digest to the log. A project with a channel of its own
implements the interface and aliases it. The alias target must implement
ScannerBanNotifierInterface, so an existing messaging service is reached through a small adapter
rather than aliased directly:
The bundle carries no schedule of its own, so run the digest from yours. It empties as it sends:
Observability
The bundle answers a probe before Symfony's error listener runs, so nothing it handles reaches the
error channel. Its own records are a warning for a rejected login probe and an info for a ban.
Behind a fingers_crossed handler both are buffered and dropped, so give them a channel that writes
unconditionally:
Monolog registers a monolog.logger.<channel> service for every declared channel, so pointing the
bundle at it is all that is left:
Testing
All versions of scanner-ban-bundle with dependencies
psr/cache Version ^2.0 || ^3.0
psr/log Version ^2.0 || ^3.0
symfony/config Version ^7.0 || ^8.0
symfony/console Version ^7.0 || ^8.0
symfony/dependency-injection Version ^7.0 || ^8.0
symfony/event-dispatcher Version ^7.0 || ^8.0
symfony/http-foundation Version ^7.0 || ^8.0
symfony/http-kernel Version ^7.0 || ^8.0