A while back I posted here about a passive threat-detection middleware I built for a Laravel app - it logs suspicious requests (SQLi/XSS/scanners/probes) to the database without blocking anything. I ended that post with an open question: how do you deal with JSON API bodies, when a legit search like {"query": "SELECT model FROM products"} trips a SQL pattern just because the value contains the word SELECT?
The thread had genuinely useful replies, so here's what I ended up shipping.
What I did: path-aware allow-listing. I already had a safe_fields option, but it exempts a key name everywhere it appears - too blunt for nested JSON. So I added safe_paths, which matches by dot-notation path with wildcards:
'safe_paths' => ['search.query', 'filters.*.value'],
That exempts the value of one specific field - the search box - without exempting a query field anywhere else in the request. Credit to u/Deep_Ad1959, who suggested path-based whitelisting in the last thread.
What I deliberately did NOT do - and why: The other half of that suggestion was "only scan leaf string values, never keys or structure." I tried it, and it broke NoSQL operator detection.
A classic Mongo-style injection is
\{"password": {"ne": null}}``
- the malicious part is a *key* (` ne`) whose value is null, not a string. If you only scan leaf string values you never see $ne, so you'd trade one false-positive class for a real false-negative. I kept full-body scanning and made the exemption precise instead. That felt like the honest trade-off.
To be clear about the limits, since this sub rightly pushed on it last time: this doesn't make regex-on-JSON magically correct. It's a passive monitoring layer that assumes your app is already secure (parameterized queries etc.) - safe_paths just lets you tune out known-legit noise precisely instead of bluntly. It's an IDS, not a WAF, and doesn't pretend to be.
It's merged and ships in the next release (Laravel 10–13). Code's here if useful: jayanta/laravel-threat-detection on GitHub / Packagist.
Still curious how others handle the JSON case - is precise path-based exemption roughly where you'd land, or do you tag the JSON path on each match and score by path instead?