In this project we’ll take an existing conference application written in ASP.NET Core, and implement validation in order to better secure our application from bad data entry, as well as injection attacks.
If you want to use Visual Studio (highly recommended) follow the following steps:
- If you already have Visual Studio installed make sure you have .NET Core installed by running the "Visual Studio Installer" and making sure ".NET Core cross-platform development" is checked.
- If you need to install Visual Studio download it at https://visualstudio.microsoft.com/ by selecting "Community 2019" from the "Dowload Visual Studio" drop down list. (If you're using Windows you'll want to check "ASP.NET" and ".NET Core cross-platform development" on the workloads screen during installation.)
- Open the .sln file in Visual Studio.
- To run the application simply press the Start Debug button (green arrow) or press F5.
- If you're using Visual Studio on Windows, to run tests open the Test menu, click Run, then click on Run all tests. (results will show up in the Test Explorer)
- If you're using Visual Studio on macOS, to run tests select the ConferenceTrackerTests Project, then go to the Run menu, then click on Run Unit Tests. (results will show up in the Unit Tests panel)
(Note: All tests should fail at this point. This is by design. As you progress through the project, more and more tests will pass. All tests should pass upon completion of the project.)
If you would rather use something other than Visual Studio:
- Install the .NET Core SDK from https://dotnet.microsoft.com/download once that installation completes, you're ready to get started!
- To run the application go into the ConferenceTracker project folder and type
dotnet run
. - To run the tests go into the ConferenceTrackerTests project folder and type
dotnet test
.
- Frontend validation
- Model validation
- Database validation
- Antiforgery tokens (prevent cross-site request forgery attacks)
- Prevent overposting attacks
Note: This isn't the only way to accomplish implementation. However, this is what the project's tests are expecting. Implementing the features in a different way will likely result in being marked as incomplete / incorrect.
Validation has two important security concerns: the first is ensuring data integrity which prevents the submission of incomplete or bad data, and the second is preventing malicious efforts such as forgery and injection attacks.
The basis for validation in ASP.NET Core is DataAnnotations. They can be used to facilitate front end validation, ModelState validation, and database validation. As such, we'll make DataAnnotations available to our model, and setup properties that we want to be not-null
.
- In our
Speaker
class atConferenceTracker/Entities/
, addRequired
attributes where appropriate. Note:Required
is part ofSystem.ComponentModel.DataAnnotations
which we already have ausing
directive for. Usually you'll need to add this yourself.- Add the
Required
attribute to the following properties:Id
FirstName
LastName
Description
- Add the
We also want to make sure the data submitted is the correct data type to help avoid problems when we're handling that data.
- Add
DataType
attributes to all appropriate properties in ourSpeaker
class.- Add the
DataType
attribute with an argument ofDataType.Text
to the following properties:FirstName
LastName
- Add the
DataType
attribute with an argument ofDataType.MultilineText
to theDescription
property. - Add the
DataType
attribute with an argument ofDataType.EmailAddress
to theEmailAddress
property. - Add the
DataType
attribute with an argument ofDataType.PhoneNumber
to thePhoneNumber
property.
- Add the
We should also ensure what is submitted adheres to the size expectations of our database by limiting the length of our strings.
- Add
StringLength
attributes to all appropriate properties in ourSpeaker
class.- Add the
StringLength
attribute with aMaximumLength
of100
, and aMinimumLength
of2
to the following properties:FirstName
LastName
- Add the
StringLength
attribute with aMaximumLength
of500
, and aMinimumLength
of10
to theDescription
property.
- Add the
Sometimes we need to validate things that are not built into DataAnnotations
. In these cases we can programmatically do validation on models using the IValidateObject
interface.
- Using the
IValidatableObject
interface, setup ourSpeaker
class to validate that ourEmailAddress
property isn't a"Technology Live Conference"
email address.- Set up our
Speaker
class to inherit theIValidatableObject
interface. Note: your code will not compile at this point, but will soon once theValidate
method is implemented. - Create a new method,
Validate
, with the following characteristics:- Add an access modifier of
public
. - Have a return type of
IEnumerable<ValidationResult>
. - Add a parameter of type
ValidationContext
. - It declares a variable of type
List<ValidationResult>
, and instantiates it to a new empty list ofValidationResult
objects. - It checks if the
EmailAddress
property is notnull
and ends with"TechnologyLiveConference.com"
. (UseStringComparison
to make this case insensitive)- If this is
true
, add a newValidationResult
with anErrorMessage
of"Technology Live Conference staff should not use their conference email addresses."
.
- If this is
- Finally, it returns the
List<ValidationResult>
variable.
- Add an access modifier of
- Set up our
While we've set up the DataAnnotations
needed for our validation, we still have to actually wire it up on our frontend. This client side validation helps reduce server load by preventing invalid submissions.
- Set up client side validation on our
ConferenceTracker/Views/Speaker/Create.cshtml
view.- At the end of our
Create
view, add aScripts
section
.- Add the section using
@section Scripts { }
. - Inside our
Scripts
section, use@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
to add_ValidateScriptsPartial
to ourScripts
section. The_ValidateScriptsPartial
is included in the template by default. It contains references tojquery.validate
. ASP.NET Core usesjquery.validate
for client side validation.)
- Add the section using
- Add a validation summary to our
Create
view'sCreate
form.- Just inside our
Create
form, before it's firstdiv
, add adiv
tag with the following attributes:asp-validation-summary
set to"ModelOnly"
class
set to"text-danger"
- Just inside our
- For each of our
Create
form's inputs, add aspan
tag with the following attributes:asp-validate-for
set to the same value as theasp-for
of the correspondinginput
class
set to"text-danger"
- At the end of our
Now that we have client side validation setup on our Speaker's Create form, we should also set up our server side validation. Client side validation is helpful, but shouldn't be relied on by itself as it is easy to bypass accidentally or maliciously.
- Setup
ModelState
validation on ourSpeakerController
'sHttpPost
Create
action.- Add a condition to our
SpeakerController
'sHttpPost
Create
action that checksModelState.IsValid
.- If
true
, the action should perform theCreate
andRedirectToAction
just like it did before. - If
false
, the action shouldreturn
View
with an argument ofspeaker
. ASP.NET Core will automatically carry any validation errors back to the client so long as you've provided the model that failed validation.
- If
- Add a condition to our
We may have both client side and ModelState
validation, but we're still potentially vulnerable to cross-site request forgery and overposting attacks.
- Setup the
HttpPost
Create
action to validate anAntiForgeryToken
and useBind
on ourspeaker
parameter to prevent stuffing.- On our
HttpPost
Create
action, add theValidateAntiForgeryToken
attribute. That's it! Anytime you make aForm
in ASP.NET Core it, automatically adds a hidden input with the antiforgery token. - Instead of just accepting
speaker
as is, we should use theBind
attribute with an argument of"Id,FirstName,LastName,Description,EmailAddress,PhoneNumber"
to restrict the action to only accepting those properties. Otherwise someone could maliciously alter their submission to set theIsStaff
property.
- On our
You've completed the tasks of this project, if you want to continue working on this project some next steps would be to setup rate limiting, caching, and utilize middleware to IP filter to help mitigate attacks and their severity.
Otherwise now is a good time to continue the ASP.Net Core path to expand your understanding of the ASP.Net Core framework or delve into the C# path to expand your knowledge of the C# programming language.