Missing Cross-Site Request Forgery (CSRF) protection
Impact. A malicious page visited by an authenticated buyer can submit offers, edit/delete their requests, toggle favourites, post chat messages or mutate profile PII with no interaction. Combined with the persistent 30-day sliding cookie, the attack window is effectively permanent until manual logout.
Evidence Source review + live probe
There is no AddAntiforgery() or [ValidateAntiForgeryToken] anywhere in the codebase:
$ grep -rn "AddAntiforgery\|ValidateAntiForgeryToken\|@Html.AntiForgeryToken" . # (no matches)
Login and Register views do not emit a token (Views/Home/Login.cshtml:23-43, Views/Home/Register.cshtml:24-60). POST login from a blank cookie jar reaches the handler:
$ curl -s -X POST "https://wtb.land/Home/Login" \ -d "email=test@example.com&password=wrongpassword" -w "HTTP %{http_code}\n" HTTP 200
Auth cookie configuration in Program.cs:68-69:
options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
Lax blocks CSRF only on non-top-level navigations - a phishing page can still issue <form method=POST> from a top-level link-click. Logout is a GET (HomeController.cs:1338-1342), exploitable via <img src="/Home/Logout">.
Fix Register antiforgery + wire into gRPC transport
// Program.cs - add before app.Build() builder.Services.AddAntiforgery(o => { o.Cookie.Name = "__Host-wtb-csrf"; o.Cookie.SameSite = SameSiteMode.Strict; o.Cookie.SecurePolicy = CookieSecurePolicy.Always; o.HeaderName = "X-CSRF-Token"; }); // After UseAuthorization() app.UseAntiforgery();
Then: decorate LoginPost/RegisterPost with [ValidateAntiForgeryToken]; emit @Html.AntiForgeryToken() inside each form; wire the token into the gRPC-Web transport (grpc-transport.js - read document.cookie for the readable XSRF token and send it as X-CSRF-Token on every request); convert Logout to [HttpPost].