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 default

    For example the following code:

      @("<span>Hello World</span>")
    

    will render the following HTML:

      &lt;span&gt;Hello World&lt;/span&gt;
    
  • 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 (http vs https)
    • domain (https://my-site.com vs https://www.my-site.com)
    • port (:443 vs :5000)

  • 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-Origin header matches the origin URL.

  • To enable CORS per endpoint in ASP.NET:

  • You can enable CORS in web.config using <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

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.config file 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.config file 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>

Password Hashing

Cookies

  • Secure cookies in ASP.NET code (see HttpCookie Class) by setting the following properties:
    • Secure - set to true to only transmit cookie using SSL, that is over HTTPS
    • HttpOnly - set to true to prevent client-side script from accessing the cookie
    • SameSite - set to strict to prevent cookie from being sent in all cross-site browsing contexts or set to lax for 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
    

Sessions

  • Secure sessions by setting properties in web.config under <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> cookieless attribute to false will 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> regenerateExpiredSessionId attribute to false will prevent using the same session ID value when expired

  • Setting a <sessionState> timeout is 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() and UseHsts()

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.asax file under Application_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.config

    For example you can add this to your web.config file:

      <customErrors mode="On">
          <error statusCode="404" redirect="~/Home/MyCustomErrorPage" />
      </customErrors>
    

    Then in your HomeController code, you would have this method defined:

      public ActionResult MyCustomErrorPage(...)
      {
          ...
      }
    

    And of course the corresponding view file MyCustomErrorPage.cshtml should have been created as well