![[Showcase] RASP.Net follow-up: Sink Detection and benchmarks with real backends](https://external-preview.redd.it/oKYDqT0aW7ozMlzXSTW3SFLuFQXaFYFxXj0BLsfCRm4.png?width=1080&crop=smart&auto=webp&s=77cf959b4722b097734a3028827ac72dababdf82)
[Showcase] RASP.Net follow-up: Sink Detection and benchmarks with real backends
TL;DR: 5 months in, RASP.Net moved from WAF-style request inspection to true Sink protection (SQL, SSRF, Path Traversal, etc.) via IL-rewriting and MonoMod. Benchmarks against real backends show the overhead is statistically invisible. Looking for feedback on the architecture.
So, 5 months ago i did my first post here talking about the RASP.Net project (and its unreal/crazy fast hot-path overhead) from there I've finished the XSS detection engine (and suddenly it wasn't crazy fast anymore), removed reflection from the hot-path using source generators instead to check nested props.
And now I've finished the beginning of the sink protection, moving it from the HTTP/gRPC messages to the sinks to stop being a "WAF in process", also ventured more into c++ for the taint tracking that's still v1, the entrypoint scan still exists, they are lighter and used to start the taint and context for the sinks
Here are the sink coverage:
- EF Core IDbCommandInterceptor for SQL
- DelegatingHandler plus SocketsHttpHandler.ConnectCallback for SSRF
- System.Text.Json type-info modifier for deserialization. Works under AOT and trimming.
- MonoMod runtime patching for FileStream/File.* (path traversal) and Process.Start (command injection), since the BCL has no hook for these. Opt-in, and it'll ship as a separate package, i don't think it should end up arriving as a dependency nobody asked.
- A native ICorProfilerCallback profiler rewriting IL at JIT time for taint tracking, same idea from Contrast and Datadog. v1 only propagates taint through String.Concat(string, string) and it's Windows-only. I kept the scope tiny on purpose for now.
We also have a per-request AsyncLocal context flowing from entrypoint to sink so when an alert is raised it also can tells which method and caller produced it
Now to the numbers, last time i reported a 4.5ns delta, it was one SearchValues inspection over a no-op interceptor; The comparable number today is the full source-generated scan of a message: ~109ns clean, vs 1120ns for the old reflection path, but for the sink pivot I've improved the methodology so here is what the guards cost under load:
| Sink | RASP off (p50 / p99) | RASP on (p50 / p99) |
|---|---|---|
Path Traversal (FileStream) |
851 µs / 1349 µs | 858 µs / 1404 µs |
Command Injection (Process.Start) |
60.76 ms / 108.22 ms | 61.50 ms / 104.59 ms |
| SQL (EF Core → Postgres) | 7.76 ms / 16.97 ms | 7.50 ms / 16.50 ms |
SSRF (HttpClient) |
299 µs / 756 µs | 306 µs / 914 µs |
As i said in the title the setup is real: real Kestrel host, real backends (Postgres via Testcontainers, real subprocess, real outbound HTTP), 25 concurrent workers for 20s per endpoint.
These numbers represent the latencies with the entire sink wired or totally absent
A file open, a process spawn, a DB round trip or an outbound HTTP call costs so much more than the inspection that the guard never shows up in the percentiles.
One thing i wasn't expecting is that the SSRF DNS check fires roughly once per pooled connection rather than once per request, i was building an opt-in DNS cache for it and ended up removing it
So here are some questions to hopefully start some conversation in the comments and give me feedback and/or ideas
- Would you allow MonoMod patching of
FileStreamin a production process, even opt-in? where you would draw that line? - Taint tracking that only covers
String.Concat(string, string)is an useful foundation, or just security theater until the rest are in? - Anything in the benchmark setup that would make you distrust the "no measurable overhead" claim?
Our next milestone is NuGet packaging (net8.0;net10.0, MonoMod split off, MinVer for versioning).
Repo: https://github.com/JVBotelho/RASP.Net
ADRs 006/007 have the full reasoning if you want to tear those apart too.
Everything here is under the v1.1.0 tag