Ways Developer Can Secure An ASP.NET Application (Redux 2022)

Updating my post back in 2015 here for year 2022.
Cross-Site Scripting (XSS)
- 
    According to OWASP’s definition of XSS, XSS is a type of injection that occurs when a web application uses input from a user within the output it generates without validating or encoding it 
- 
    To avoid XSS in ASP.NET, use Razor’s expression syntax with the @symbol preceeding variable names, e.g.@InputString, as this will escape HTML characters in the input string by defaultFor example the following code: @("<span>Hello World</span>")will render the following HTML: <span>Hello World</span>
- 
    Use the HttpUtility.HtmlEncode() function inside your Razor code: <%= HttpUtility.HtmlEncode(UserInput) %>
- 
    Avoid using HtmlHelper.Raw(), unless you validate and sanitize the input string variable 
Same Origin Policy and Cross Origin Resource Sharing (CORS)
- 
    According to MDN’s definition of same origin policy, same origin policy restricts how a document or script loaded by one origin can interact with a resource from another origin 
- 
    2 URLs have different origin if any of the following is different: - protocol (httpvshttps)
- domain (https://my-site.comvshttps://www.my-site.com)
- port (:443vs:5000)
 
- protocol (
- 
    CORS on the other hand, according to OWASP’s definition of CORS, allows a web application to expose resources to all or restricted domain and allows a web client to make AJAX request for resource on other domain than its source domain 
- 
    The process involves the browser sending the Origin HTTP request header (with origin URL as its value) for cross domain request to the server. The server then sending the Access-Control-Allow-Origin HTTP response header (with origin URL as its value) to allow CORS. Then lastly the browser checking if URL in Access-Control-Allow-Originheader matches the origin URL.
- 
    To enable CORS per endpoint in ASP.NET: - Use [EnableCors] attribute at the controller level
- And call CorsHttpConfigurationExtensions.EnableCors() on startup/configuration
 
- 
    You can enable CORS in web.configusing <customHeaders> but this will apply for all requests:<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="https://..." /> </customHeaders> </httpProtocol> </system.webServer>
SQL Injection
- 
    Avoid concatenating user input to SQL query statements 
- 
    Use parameterized query or prepared statements In your SQL query use SQL variables: SELECT * FROM product WHERE id = @idAnd in your ASP.NET code, use SQL command parameters: command.Parameters.AddWithValue("@id", id);
- 
    Additionally validate user input 
- 
    Avoid execution of raw SQL even in Entity Framework 6 (EF6) - EF6 calls such as as Database.SqlQuery() and Database.ExecuteSqlCommand() 
- 
    For Entity Framework Core (EF Core), see Passing parameters in EF Core 
- 
    Also, avoid returning IQueryable in EF and instead call ToList() before returning results 
Cross-Site Request Forgery (CSRF)
- 
    According to OWASP’s definition of CSRF, CSRF forces an end user to execute unwanted actions on a web application in which they are currently authenticated. 
- 
    An example of CSRF is luring a user to visit the attacker’s site where the attacker is able to send an authenticated HTTP request to the server being attacked that the user has previously logged in 
- 
    To prevent CSRF in ASP.NET, antiforgery tokens are used and they work because the malicious page cannot read the user’s tokens, due to same-origin policies Use HtmlHelper.AntiForgeryToken() to add antiforgery token to a <form>element:@using (Html.BeginForm("DeleteProduct", "Admin")) { @Html.AntiForgeryToken() ... }And add the [ValidateAntiForgeryToken] attribute to the controller action: [HttpPost] [ValidateAntiForgeryToken] public ActionResult DeleteProduct(...) { ... }
- 
    For ASP.NET Core, see Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core 
Web.config
- 
    ASP.NET or specifically IIS by default forbids downloading the web.configfile via HTTP as this is configured in it’s config under <system.web>/<httpHandlers>:<system.web> <httpHandlers> <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler" /> </httpHandlers> </system.web>
- 
    But still it should not contain sensitive information as this web.configfile can be stored in the project’s source control repository
- 
    You can store your app settings and connections strings in an external file which you can refer to in your web.config(see <appSettings> file attribute and <connectionStrings> configSource attribute):
    <appSettings file="secrets.appSettings.config">
        ...
    </appSettings>
    <connectionStrings configSource="secrets.connectionStrings.config">
    </connectionStrings>
- 
    Encrypt sections of your web.configfile (see Encrypting and Decrypting Configuration Sections)
- 
    If you are using a cloud provider, use the Key Vault in Azure or Secrets Manager in AWS to store sensitive information 
Password Hashing
- 
    You can use Rfc2898DeriveBytes to hash password 
- 
    Or use ASP.NET Core Identity PasswordHasher (see Exploring the ASP.NET Core Identity PasswordHasher) 
Cookies
- Secure cookies in ASP.NET code (see HttpCookie Class) by setting the following properties:
    - 
Secure - set to trueto only transmit cookie using SSL, that is over HTTPS
- 
HttpOnly - set to trueto prevent client-side script from accessing the cookie
- 
SameSite - set to strictto prevent cookie from being sent in all cross-site browsing contexts or set tolaxfor a more balanced approach between security and usability
 cookie = new HttpCookie("NewCookie"); cookie.Secure = true; cookie.HttpOnly = true; cookie.SameSite = SameSiteMode.Strict // or SameSiteMode.Lax
- 
Secure - set to 
Sessions
- 
    Secure sessions by setting properties in web.configunder<system.web>/<sessionState>(see SessionStateSection Class) and<system.web>/<httpCookies>(see HttpCookiesSection Class):<system.web> <sessionState cookieless="false" regenerateExpiredSessionId="false" timeout="20" /> <httpCookies httpOnlyCookies="true" requireSSL="true" sameSite="Strict" /> </system.web>
- 
    Setting <sessionState> cookielessattribute tofalsewill prevent encoding the session ID in the URL which is prone to a security attack and instead use a cookie to store the session ID
- 
    Setting <sessionState> regenerateExpiredSessionIdattribute tofalsewill prevent using the same session ID value when expired
- 
    Setting a <sessionState> timeoutis recommended to prevent a session running too long which make it more vulnerable to security attack
Enforce HTTPS
- 
    Enforce HTTPS in ASP.NET code by redirecting after checking HttpRequest.IsSecureConnection: if (!HttpContext.Current.Request.IsSecureConnection) { Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl); }
- 
    You can add a rewrite rule in web.config:<system.webServer> <rewrite> <rules> <rule name="HTTP to HTTPS redirect" stopProcessing="true"> <match url="(.*)" /> <conditions> <add input="{HTTPS}" pattern="off" ignoreCase="true" /> </conditions> <action type="Redirect" redirectType="Permanent" url="https://{HTTP_HOST}/{R:1}" /> </rule> </rules> </rewrite> </system.webServer>
- 
    Or you can add the HTTP Strict-Transport-Security (HSTS) header to the response 
- 
    For ASP.NET Core, see Enforce HTTPS in ASP.NET Core for information on UseHttpsRedirection()andUseHsts()
Error handling
- 
    Having a custom error page allows you to tailor your error messages from displaying too much information or from displaying sensitive information that can lead to a security attack 
- 
    In ASP.NET, you can add custom error handler in Global.asaxfile underApplication_Error():protected void Application_Error(object sender, EventArgs e) { Exception exception = Server.GetLastError(); System.Diagnostics.Debug.WriteLine(exception); Response.Redirect("/Home/Error"); }
- 
    You can configure custom error messages/pages in web.configFor example you can add this to your web.configfile:<customErrors mode="On"> <error statusCode="404" redirect="~/Home/MyCustomErrorPage" /> </customErrors>Then in your HomeControllercode, you would have this method defined:public ActionResult MyCustomErrorPage(...) { ... }And of course the corresponding view file MyCustomErrorPage.cshtmlshould have been created as well