Back to: ASP.NET Core Tutorials For Beginners and Professionals
Blacklist and Whitelist Checks using Data Annotation in ASP.NET Core MVC
In this article, I will discuss Blacklist and Whitelist Checks using Data Annotation in ASP.NET Core MVC Applications. Please read our previous article discussing Remote Validations in ASP.NET Core MVC.
Blacklist and Whitelist Checks using Data Annotation in ASP.NET Core MVC
In ASP.NET Core MVC, implementing blacklist and whitelist checks is one of the security measures to control access to resources and to filter inputs based on defined lists of allowed (whitelist) or disallowed (blacklist) entities. This can help prevent unauthorized access, SQL injection, cross-site scripting (XSS), and other malicious activities.
- Whitelist: Only allows items that appear on the list. It’s generally considered more secure than a blacklist because it explicitly permits only known safe entities.
- Blacklist: Blocks items that appear on the list. It can be harder to manage because it requires anticipating all possible harmful inputs.
Both blacklisting and whitelisting are methods used to filter and validate data. Blacklisting involves blocking specific values, while whitelisting permits only specific values.
Create Custom Data Annotations
ASP.NET Core does not provide built-in annotations for blacklist and whitelist checks, so we need to create Custom Data Annotation Attributes. Let’s create custom data annotation attributes in ASP.NET Core MVC for Black and White Lists.
Whitelist Attribute:
This attribute will only allow specific values from a predefined list. So, first, create a custom data annotation attribute named WhitelistAttribute.cs within the Models folder and then copy and paste the following code into it.
using System.ComponentModel.DataAnnotations; namespace DataAnnotationsDemo.Models { public class WhitelistAttribute : ValidationAttribute { public string[] AllowedValues { get; set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value != null && AllowedValues.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase)) { return ValidationResult.Success; } return new ValidationResult($"The value {value} is not allowed."); } } }
Blacklist Attribute:
This attribute will block specific values from a predefined list. So, create a custom data annotation attribute named BlackListAttribute.cs within the Models folder and copy and paste the following code into it.
using System.ComponentModel.DataAnnotations; namespace DataAnnotationsDemo.Models { public class BlacklistAttribute : ValidationAttribute { public string[] DisallowedValues { get; set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var email = value as string; if (string.IsNullOrEmpty(email)) { return ValidationResult.Success; } var domain = email.Split('@').LastOrDefault(); if (domain != null && DisallowedValues.Contains(domain, StringComparer.OrdinalIgnoreCase)) { return new ValidationResult($"The domain {domain} is not allowed."); } return ValidationResult.Success; } } }
Define Your Model
Now, we need to create a model class to restrict certain inputs by applying the above WhitelistAttribute and BlackListAttribute. So, create a class file named UserInput.cs and copy and paste the following code. Apply the custom annotations to the appropriate properties in your model.
namespace DataAnnotationsDemo.Models { public class UserInput { public int Id { get; set; } [Whitelist(AllowedValues = new string[] { "Admin", "User", "Guest" })] public string Role { get; set; } [Blacklist(DisallowedValues = new string[] { "example.com", "test.com" })] public string EmailDomain { get; set; } } }
Handling Validation in the Controller
In your controller, you can check the validation status of your model as part of your action methods. Next, modify the Home Controller as follows:
using DataAnnotationsDemo.Models; using Microsoft.AspNetCore.Mvc; namespace DataAnnotationsDemo.Controllers { public class HomeController : Controller { public ActionResult Create() { return View(); } [HttpPost] public ActionResult Create(UserInput model) { //Check if the Model State is Valid if (ModelState.IsValid) { //Save the Data into the Database //Redirect to a Different View return RedirectToAction("Successful"); } //Return to the same View and Display Model Validation error return View(model); } public string Successful() { return "Role Added Successfully"; } } }
Create a View:
Ensure your views appropriately display validation messages. Next, create a view named Create.cshtml within the Views/Home folder and then copy and paste the following code:
@model DataAnnotationsDemo.Models.UserInput @{ ViewData["Title"] = "Create"; } <div class="row"> <form asp-controller="Home" asp-action="Create" method="Post" class="mt-3"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group row mb-3"> <label asp-for="Role" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="Role" class="form-control"> <span asp-validation-for="Role" class="text-danger"></span> </div> </div> <div class="form-group row mb-3"> <label asp-for="EmailDomain" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="EmailDomain" class="form-control"> <span asp-validation-for="EmailDomain" class="text-danger"></span> </div> </div> <div class="form-group row mb-3"> <div class="col-sm-10"> <button type="submit" class="btn btn-primary">Create</button> </div> </div> </form> </div>
Now, run the application and see if everything is working as expected. If you provide the wrong value, then you will get an error message, as shown in the image below:
Real-Time Example: Comment Filtering for a Blog Post using Black List and White List
Let’s create a complete example in an ASP.NET Core MVC project where we implement server-side validation to restrict comments on a blog post if they contain certain offensive words.
Create Custom Data Annotations
First, create the OffensiveWordBlacklist Attribute that inherits from ValidationAttribute. This attribute will check if the comment contains any words from a predefined list of offensive words. So, create a class file named OffensiveWordBlacklistAttribute.cs and then copy and paste the following code:
using System.ComponentModel.DataAnnotations; namespace DataAnnotationsDemo.Models { public class OffensiveWordBlacklistAttribute : ValidationAttribute { public string[] DisallowedWords { get; set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value is string commentText) { var foundWords = new List<string>(); // Check each word in the list of disallowed words foreach (var word in DisallowedWords) { // Case insensitive check if the comment contains the disallowed word if (commentText.Contains(word, StringComparison.OrdinalIgnoreCase)) { foundWords.Add(word); } } // If any offensive words are found, return a ValidationResult with all offending words if (foundWords.Any()) { return new ValidationResult($"The comment contains disallowed words: {string.Join(", ", foundWords)}"); } } return ValidationResult.Success; } } }
Explanation:
- List of Found Words: The foundWords list collects all the offensive words that appear in the comment.
- Check Each Word: The loop checks each word in the DisallowedWords array to see if it appears in the comment.
- Record Offenses: If a word is found, it’s added to the foundWords list.
- Validation Result: If any offensive words are found, a ValidationResult is returned that lists all of these words. If none are found, ValidationResult.Success is returned
Create the Model
Next, we will define the BlogComment model, which represents a user’s comment on a blog post. This model will include the comment text and a custom validation attribute to check for offensive words. So, create a class file named BlogComment.cs and then copy and paste the following code:
using System.ComponentModel.DataAnnotations; namespace DataAnnotationsDemo.Models { public class BlogComment { [Required] [EmailAddress] public string UserEmail { get; set; } [Required] public string UserName { get; set; } [Required] [OffensiveWordBlacklist(DisallowedWords = new string[] { "offensive1", "offensive2", "offensive3" })] public string CommentText { get; set; } } }
Create the MVC Controller
Now, let’s implement a controller with actions to create and post comments. The Create GET action displays the form, and the Create POST action handles the form submission. So, create an MVC Empty Controller named BlogCommentController within the Controllers folder and then copy and paste the following code:
using DataAnnotationsDemo.Models; using Microsoft.AspNetCore.Mvc; namespace DataAnnotationsDemo.Controllers { public class BlogCommentController : Controller { [HttpGet] public IActionResult Create() { return View(); } [HttpPost] public IActionResult Create(BlogComment blogComment) { if (ModelState.IsValid) { //Save the Data into the Database //Redirect to a Different View return RedirectToAction("Successful"); } // If validation fails, show form again with validation messages return View(blogComment); } public string Successful() { return "Comment Posted Successfully!"; } } }
Create the View
Next, create a view named Create.cshtml within the Views/BlogComment folder, and then copy and paste the following code:
@model DataAnnotationsDemo.Models.BlogComment @{ ViewData["Title"] = "Blogpost Comment"; } <div class="row"> <form asp-controller="BlogComment" asp-action="Create" method="Post" class="mt-3"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group row mb-3"> <label asp-for="UserName" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="UserName" class="form-control"> <span asp-validation-for="UserName" class="text-danger"></span> </div> </div> <div class="form-group row mb-3"> <label asp-for="UserEmail" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="UserEmail" class="form-control"> <span asp-validation-for="UserEmail" class="text-danger"></span> </div> </div> <div class="form-group row mb-3"> <label asp-for="CommentText" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <textarea asp-for="CommentText" class="form-control"></textarea> <span asp-validation-for="CommentText" class="text-danger"></span> </div> </div> <div class="form-group row mb-3"> <div class="col-sm-10"> <button type="submit" class="btn btn-primary">Create</button> </div> </div> </form> </div>
With the above changes in place, run the application and navigate to BlogComment/Create URL. Fill out the form with offensive words in the comment section, and then click on submit. The application will then process the comment on the server side and return it with the error message shown in the image below.
Note: In real-world applications, blacklists, especially for words, can be extensive. Instead of hardcoding the list, you might store it in a database or configuration file. The custom data annotation can then be modified to fetch the list from the source, ensuring it can be updated without changing the application code.
In the next article, I will discuss Displaying and Formatting Data Annotation Attributes in ASP.NET Core MVC Application. In this article, I try to explain Blacklist and Whitelist Checks using Data Annotation in ASP.NET Core MVC Application with examples. I hope you enjoy this Blacklist and Whitelist Checks using Data Annotation in the ASP.NET Core MVC article.