Tallan Blog

Tallan’s Experts Share Their Knowledge on Technology, Trends and Solutions to Business Challenges

Creating Secure AJAX HTML Forms in ASP.NET Core MVC, Part I: Client-Side and Server-Side Validation

In this two-part series, I will show you how to create a secure form that submits using Ajax. In part one of this series, we will create an HTML form and secure it from XSS and SQL Injection by validating user input through client-side and server-side validation.

Most modern websites have a need to take in information from a user. This is commonly done through HTML forms; the user enters information into form fields and the website submits an HTTP POST request to the server. The server can then use this information and/or store it to meet a wide variety of business needs. However, allowing any information from any source can prove disastrous for a system and is commonly the point of attack for malicious parties. SQL injection, Cross-Site Scripting (XSS), and Cross-Site Request Forgery (CSRF) are common ways a malicious party can hijack your website through an unsecure form. The first step in protecting your website from these kinds of attacks is to implement validation. Client-side validation uses JavaScript to check input values and provides the first line of defense for your web application. However, client-side validation can easily be turned off in most cases by using a web browser’s developer tools. Thus, server-side validation is almost always required as well.

Understanding Threats to Your Web Application

Out of the three security threats mentioned earlier, Cross-Site Scripting and SQL injection can be mitigated by implementing validation. In case you are not familiar with either concept, I will provide a brief overview:

Cross-Site Scripting (XSS)

XSS is one of the most commonly exploited security vulnerabilities by attackers. This vulnerability enables attackers to place JavaScript (or some other client-side script) directly onto a web page. When another user loads the affected page, the malicious script runs and can steal cookies or session tokens. The attacker can also manipulate the DOM and redirect the browser to another page. XSS works by tricking the website into embedding a <script> tag on a rendered page. If nothing is stopping an attacker from entering that script instead of an expected value, there is no telling what damage they could do.

Imagine a website where a user can input their username and see it displayed on a profile page. If an attacker is able to successfully enter a malicious script because there is no validation, anyone who visits that user’s profile page will be subject to the attack.

SQL Injection

SQL Injection allows attackers to execute SQL code on a database, which allows them to access, modify, or delete data without regard for the user’s permissions. Any query that uses dynamic SQL is particularly vulnerable to attack. If form values are used in a database query, then the input should be validated and/or sanitized before the query is executed. For example, consider the following query that searches for a user profile from a value provided by an HTML form:

var sqlQuery = $“SELECT * FROM Person WHERE name = ‘{username}’”

Now what if an attacker entered some bad information in place of a real username?

var sqlQuery = $”SELECT * FROM Profile WHERE username = ‘Bob’;DROP TABLE Profile”

Clearly “Bob’:DROP TABLE Profile” is not a legitimate name, but if form input values are not sanitized, you can see how disastrous the outcomes could be. Both are legitimate queries, so both SQL statements will run regardless of the original intent of the query.

Validation and Model Binding in ASP.NET Core MVC

Validation in ASP.NET Core MVC works through model binding and attributes. The first step is to create a Plain-old C# Object (POCO) class with properties that represent each field of the HTML form. Next, is to add attributes to each property that specify restrictions to each property such as if it is required or not, length, min or max values for numbers, what type of value it is, and so on. You can even use a Regular Expression to match the input to a specific format.

In the example below, I have created a simple model to represent an HTML form for gathering contact information. The first property, ‘Name’ is required must match the regular expression defined by the RegularExpression attribute. In this case, the ‘Name’ field must be only letters (uppercase or lowercase) or  apostrophes up to 25 characters. The second property ‘Email’ is also required and must be an email address as defined by the EmailAddress attribute. We have also changed the way this is property is displayed in any validation messages by adding a ‘Display’ attribute. Finally, the ‘Message’ property is also required and can be no longer than 10 characters. Other useful built-in attributes include: CreditCard, Compare, Phone, Range, Remote, and Url.

ASP.NET Core MVC Screen Shot

Client-side Validation

As mentioned, client-side validation is the first line of defense in protecting your web application. By restricting what type of values a user can enter into a field, we can ensure that no harmful information is submitted to the server. As a bonus, it also prevents a round trip to the server, since already know the information is bad or missing.

Client-side validation in .NET Core MVC is performed by JavaScript through the jQuery Unobtrusive Validation front-end library, which is written by Microsoft and built on top of the popular jQuery Validate plugin.  jQuery Unobtrusive Validation works with Razor Tag Helpers and HTML Helpers to more easily streamline the process of adding validation to form fields. Tag Helpers and HTML Helpers use validation attributes and type metadata from model properties to create HTML 5 data- attributes. jQuery Unobtrusive Validation then reads the data- attributes and passes this information onto jQuery Validate. This process prevents manually copying any server-side validation to the client. So, by using the model shown above, including jQuery Unobtrusive Validation, and using the asp-for and asp-validation-for tags as show below, we have effectively provided client-side and server-side validation in one fell swoop.

In the example below, notice the HTML tag helpers “asp-for”, “asp-validation-for”. This is what will bind these input fields to the view model we created in the previous step. “asp-for” binds an input or label to the property and “asp-validation-for” provides validation feedback should the user attempt to provide an invalid input. The data annotations we placed on each property specify these validation rules. Finally, “asp-validation-summary” tag helper will display a summary of all error messages if the user tries to submit the form with errors.

Also, it is important that ‘novalidate’ attribute is included on the form tag. This prevents and browser from performing its own validation and showing redundant validation messages.

novalidate attribute screenshot

Each validation message will display a unique message for each field. Notice in the example below how the messages change depending on the datatype, data annotation, and user input. If the fields are blank, the user is informed that they must be filled in. However, once the user starts entering information, the error messages change based on what is entered. We now know that “Name” is too short, “E-Mail” must be an actual email address (thanks to the “EmailAddress” data annotation), and “Message” is too long. This functionality comes out of the box and makes the normally tedious process of form validation quick and easy. This functionality can be customized further with additional data attributes and even jQuery if so desired.

CSREF jQuery screenshot

Notice how the “Email” property is displayed “E-mail” on the web page? This is because we specified how to display the name with the “Display” data annotation.

CSREF jQuery screenshot2


Server-Side Validation

Now that we have client-side validation covered, we will need to take one more step to fully ensure server-side validation is implemented. As mentioned, client-side validation can be useful for preventing unnecessary trips to the server, but it should not be the only method of validation, because JavaScript can easily be altered or turned off in most modern web browsers. We added attributes to our model and added tag helpers to our input fields as part of the client-side validation implementation, but we also have to consider what happens if invalid data makes it to the controller.

In the following example, we are checking the ModelState of the model. If the ModelState is valid, then the form values have passed validation and the controller action can carry on with its task. If not, then an error should be returned. If the ‘Name’ is anything other than what our regular expression has defined a name it be (most likely nothing resembling a SQL statement or a JavaScript script), the ModelState will be invalid. Likewise, if ‘Email’ is not an email and ‘Message’ is too long, the ModelState will also be invalid.


The IsValid property on the ModelState Controller property gives us all the information we need. With this small step, we have implemented server-validation on top of our client validation. Thanks to ASP.NET Core MVC, this process is extremely easy.


I have shown some of the dangers to using HTML forms in your web application and how to ensure data entered by users will not cause harm to your website using validation. By Implementing both client-side and server-side validation, an important step has been taken to secure your HTML form against XSS and SQL Injection. In Part 2 of this blog post, I’ll show you how to create a secure HTML form that submits using AJAX and how to prevent another type of security vulnerability affecting HTML forms: Cross-Site Request Forgery.

Share this post:

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>