OWASP for MVC Developers
The OWASP Top 10 is the industry's standing list of the web vulnerabilities that actually get exploited. The good news: ASP.NET Core MVC ships defenses for the big four β if you don't disable them. Most real-world .NET vulnerabilities are a developer opting out of a default. This lesson covers the four you'll be asked about in any public-sector code review.
Injection: let EF parameterize
SQL injection happens when user input is concatenated into a query string. EF Core LINQ
queries (.Where(c => c.LastName == input)) are parameterized automatically
β the input travels as data, never as SQL. The risk reappears at the escape hatches:
FromSqlRaw with string interpolation, or hand-built SqlCommand text.
If you must drop to raw SQL, pass parameters (FromSqlInterpolated does this
correctly; $"...{input}..." inside FromSqlRaw does not). Rule: user input may appear in a
parameter, never in the SQL string.
XSS: Razor encodes, Html.Raw un-encodes
Cross-site scripting is injection into HTML instead of SQL. Razor's @model.Notes output
HTML-encodes by default, so a stored <script> tag renders as harmless text. The opt-out
is Html.Raw() β treat every use as a code-review flag. Legitimate uses (rendering CMS
content you control) should be rare, documented, and never fed by user input. Watch the side
doors too: JavaScript string contexts (var name = '@name'; needs JS encoding, not HTML
encoding) and jQuery's .html() (prefer .text() for user data). Kendo UI grids have the
same knife: column templates and encoded: false bypass encoding.
CSRF: anti-forgery tokens
Cross-site request forgery tricks a logged-in user's browser into submitting your form from another site β the auth cookie rides along automatically. The defense is the anti-forgery token pair:
<form asp-action="Approve"> @* tag helper emits the hidden token *@
[ValidateAntiForgeryToken] // on the POST action
public IActionResult Approve(int id) { ... }
Better than per-action attributes: register AutoValidateAntiforgeryTokenAttribute as a
global filter so every unsafe verb (POST/PUT/DELETE) is validated by default and forgetting
becomes impossible. Ajax posts (jQuery, Kendo) need the token added to the request data or a
header β a small shared JS helper pays for itself.
Over-posting: bind ViewModels, never entities
If an action takes an EF entity (Edit(Candidate model)), the model binder will happily set
any property a malicious client posts β including IsApproved or DeterminationStatus that
your form never rendered. This is over-posting (mass assignment). The fix is structural,
not a [Bind] attribute you'll forget: actions accept ViewModels containing only the fields
the screen edits, and AutoMapper (or explicit code) copies the permitted values onto the
entity. You already keep entities and ViewModels separate for design reasons; this is the
security reason.