In this project, we’ll take an existing conference application written in ASP.NET Core and implement several simple security measures to help improve application security.
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
.
- Require HTTPS (What is HTTPS? https://en.wikipedia.org/wiki/HTTPS)
- Utilize HSTS (What is HSTS? https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
- Restrict cross origin requests
- Setup basic error handling
- Setup basic logging
- Store and consume user secrets
- Set and configure cookie policy options
Note: This isn't the only way to implement this project. 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.
In order to properly secure any web application, HTTPS's end to end encryption has become effectively a mandatory to help prevent "man in the middle" attacks.
- Redirect users who access the web application through
HTTP
to useHTTPS
instead- In
Startup
class'sConfigure
method, call theUseHttpsRedirection
method onapp
. (This should be done after our database is created, but beforeUseStaticFiles
is called.)
- In
To help prevent "protocol downgrade" and "cookie hijacking" attacks we can utilize HTTP Strict-Transport-Security (HSTS). This should be limited to only happen in production.
- Setup the web application to use the HSTS header
- In
Startup
class'sConfigure
method, as the very first thing we do in that method, call theIsDevelopment
method onenv
, and check if it returnstrue
orfalse
.- If
false
, callUseHsts
onapp
.
- If
- In
We need the ability to see when something has gone wrong in detail as a developer, while also ensuring we don't accidentally expose that information to our users.
- Setup separate error pages for developers and users
- In
Startup
class'sConfigure
method, ifIsDevelopment
returns:false
call theUseExceptionHandler
method onapp
with an argument of"/Home/Error"
before the call toUseHsts
.true
call theUseDeveloperExceptionPage
method onapp
and theUseDatabaseErrorPage
method onapp
.
- In
The best way to prevent "Cross Orgin" attacks is to not use Cross-Origin Resourc Sharing (CORS). However this isn't always an option: in such cases we can specify which domains to allow requests.
- Setup CORS to permit requests from a single domain
- In our
Startup
class, create a newprivate
readonly
field of typestring
named_allowedOrigins
, and set it to the value"_allowedOrigins"
. - In our
Startup
class'sConfigureServices
method, add a call to the methodAddCors
onservices
and provide it an argument ofoptions => { options.AddPolicy(_allowedOrigins, builder => { builder.WithOrigins("http://pluralsight.com"); }); }
. (This is specifying the name of our CORS policy, and providing what domains will be permitted) - In our
Startup
class'sConfigure
method, add a call toUseCors
onapp
with_allowedOrigins
as the arguement. Do this before our call toUseHttpsRedirection
.
- In our
Logging is an important strategy for diagnosing bugs, spotting attempts to compromise security, as well as investigating what happened after something has gone wrong.
Note: Logging methods are not asynchronous, nor should they be as logging asynchronously can cause logged messages to be out of order.
Note: Logging is enabled by default and provides Console
, Debug
, EventSource
, and EventLog
logging. EventLog
is a Windows feature, and on other operating systems EventLog
logging will be ignored.
-
Add logging to our
Startup
class- Update our
Startup
class'sConfigure
method, to log if the application is running in development.- Add a using directive for
Microsoft.Extensions.Logging
. - Update the
Configure
method's signature to take a third parameter of typeILogger<Startup>
with a name oflogger
. - In our existing condition that checks if
IsDevelopment
istrue
, whentrue
callLogInformation
onlogger
with an argument of"Environment is in development"
before our exception handling.
- Add a using directive for
- Update our
-
Add logging to our
PresentationsController
class- In our
PresentationsController
class, add aprivate
readonly
field of typeILogger
named_logger
. - Update our
PresentationsController
update the constructor to take a third parameter of typeILogger<PresentationsController>
namedlogger
and set_logger
tologger
.
We'll add logging to see when our
Edit
action is called, and what outcome occurred via logging. Note some of this logging will duplicate built in logging. We will not add logging to everything. This is simply to familiarize yourself with logging in a controller action.- Update our
PresentationsController
'sEdit
method (theHTTP Get
not theHTTP Post
Edit
method), add the following logging.- As the very first line call
LogInformation
on_logger
with a message"Getting presentation id:" + id + " for edit."
. - Inside the condition where we check if
id
isnull
, before we returnNotFound()
, callLogError
on_logger
with a message"Presentation id was null."
. - Inside the condition where we check if
presentation
isnull
, before we returnNotFound()
, callLogWarning
on_logger
with a message"Presentation id," + id + ", was not found."
. - Immediately before we set our
ViewData
, callLogInformation
on_logger
with a message"Presentation id," + id + ", was found. Returning 'Edit view'
.
- As the very first line call
- In our
We need to be very careful handling sensitive information like connection strings. We absolutely DO NOT want these in our code base where they could accidently be committed to our repository potentially compromising our database credentials. Instead, locally we can use UserSecrets
and in production EnvirornmentVariables
to handle this information securely.
First we need to set up UserSecrets
and EnvironmentVariables
, as of ASP.NET Core 2.0 that's just done by default when CreateDefaultBuilder
is called in our Program
class! So, we can skip to just putting them to use!
- Add a call to retrieve
SecretMessage
fromConfiguration
- Inside our
Startup
class'sConfigureServices
method, set ourSecretMessage
property using to the returned value fromConfiguration["SecretMessage"]
. Normally, you'd use this to contain things like connection strings. However, since we're using anInMemory
database, this is simply being used as an example, and serves no functional purpose.
- Inside our
This is how you set user secrets. However, because they're a secret only stored on your local computer, we can't actually check to see if you did it right. Which is a good thing... but... means we can't tell you if it worked :)
- Create a "Password" secret
- We're going to use the .NET Core CLI (Command Line Interface).
- In the CLI navigate to the
ConferenceTracker
directory, not the solution's directory. (You can use thecd
command to navigate between directories. Example:cd ConferenceTracker
) - Enter the command
dotnet user-secrets init
- this sets the
secretsId
for your project
- this sets the
- Enter the command
dotnet user-secrets set "SecretMessage" "Keep it secret, Keep it safe."
- this sets a secret with the key
"SecretMessage"
with a value "Keep it secret, Keep it safe."
- this sets a secret with the key
While not directly security related, we do need to ensure we're complying with General Data Protection Regulation (GDPR) legislation if our website is accessible to Europe. From the web application perspective this means we need to set our cookie policy.
- Add
CookiePolicy
to ourStartup
class to get user consent for any cookies we might use- Inside
Startup
class'sConfigureServices
method, anywhere before our call toAddControllersWithViews
, callConfigure<CookiePolicyOptions>
onservices
with the argumentoptions => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }
. (Don't worry about specifically what these arguments mean. You'll need to change them based on what cookies you use and what they store) - Inside
Startup
class'sConfigure
method, anywhere before our call toUseRouting
, callUseCookiePolicy
onapp
.
- Inside
You've completed the tasks of this project. Congratulations! If you want to continue working on this project, some next steps would be to expand the existing functionality of the application to include an administration section to manage your existing users, implement caching, and make the information stored in the application available via a secure API.
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.