paint-brush
OWASP Top 10 .NET Protection: A Guide (Part 1)by@wownetort
3,472 reads
3,472 reads

OWASP Top 10 .NET Protection: A Guide (Part 1)

by Nikita StarichenkoOctober 15th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The OWASP Top 10 is a standard awareness document for developers and web application security. It represents a broad consensus about the most critical security risks to web applications. Injection vulnerabilities are often found in SQL, LDAP, XPath, or No.SQL queries, OS commands, XML parsers, SMTP headers, expression languages, and ORM queries. ASP.NET Core Identity Framework is well-configured by default, where it uses secure password hashes and PBK function for random passwords.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - OWASP Top 10 .NET Protection: A Guide (Part 1)
Nikita Starichenko HackerNoon profile picture

The OWASP Top 10 is a standard awareness document for developers and web application security. It represents a broad consensus about the most critical security risks to web applications.

In this article, I want to cover the first part of the TOP 10 vulnerabilities and how to protect against them using .NET.

1. Injection

What it is?

Almost any source of data can be an injection - vector, environment variables, parameters, external and internal web services, and all types of users. Injection flaws occur when an attacker can send hostile data to an interpreter. Injection vulnerabilities are often found in SQL, LDAP, XPath, or NoSQL queries, OS commands, XML parsers, SMTP headers, expression languages, and ORM queries. Injection can result in data loss, corruption, or disclosure to unauthorized parties, loss of accountability, or denial of access.

What to do?

SQL Injection

  1. Using an object-relational mapper (ORM) or stored procedures is the most effective way of countering the SQL Injection vulnerability.
  2. Use parameterized queries where a direct SQL query must be used.
  3. Practise Least Privilege - Connect to the database using an account with a minimum set of permissions required to do it's job i.e. not the same account

Good example:

    var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
    context.Database.ExecuteSqlCommand(
        sql,
        new SqlParameter("@FirstName", firstname),
        new SqlParameter("@Id", id));

Bad example:

    string strQry = "SELECT * FROM Users WHERE UserName='" + txtUser.Text + "' AND Password='"
                    + txtPassword.Text + "'";
    EXEC strQry // SQL Injection vulnerability!

OS Injection

  1. Use System.Diagnostics.Process.Start to call underlying OS functions.
  2. System.Diagnostics.Process process = new System.Diagnostics.Process();
    System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
    startInfo.FileName = "validatedCommand";
    startInfo.Arguments = "validatedArg1 validatedArg2 validatedArg3";
    process.StartInfo = startInfo;
    process.Start();
  3. Use whitelist validation on all user-supplied input. Input validation prevents improperly formed data from entering an information system.
  4. //User input
    string ipAddress = "127.0.0.1";
    
    //check to make sure an ip address was provided
    if (!string.IsNullOrEmpty(ipAddress))
    {
     // Create an instance of IPAddress for the specified address string (in
     // dotted-quad, or colon-hexadecimal notation).
     if (IPAddress.TryParse(ipAddress, out var address))
     {
      // Display the address in standard notation.
      return address.ToString();
     }
     else
     {
      //ipAddress is not of type IPAddress
     }
    }

LDAP injection

  1. Almost any characters can be used in Distinguished Names. However, some must be escaped with the backslash \ escape character.
  2. The space character must be escaped only if it is the leading or trailing character in a component name, such as a Common Name. Embedded spaces should not be escaped.

2. Broken Authentication

What it is?

Attackers can detect broken authentication using manual means and exploit them using automated tools with password lists and dictionary attacks. Attackers have to gain access to only a few accounts, or just one admin account to compromise the system. Depending on the domain of the application, this may allow money laundering, social security fraud, and identity theft, or disclose legally protected highly sensitive information.

What to do?

  1. Use ASP.net Core Identity. ASP.net Core Identity framework is well configured by default, where it uses secure password hashes and an individual salt. Identity uses the PBKDF2 hashing function for passwords, and they generate a random salt per user.
  2. Set a secure password policy
  3. //startup.cs
    services.Configure<IdentityOptions>(options =>
    {
     // Password settings
     options.Password.RequireDigit = true;
     options.Password.RequiredLength = 8;
     options.Password.RequireNonAlphanumeric = true;
     options.Password.RequireUppercase = true;
     options.Password.RequireLowercase = true;
     options.Password.RequiredUniqueChars = 6;
    
    
     options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
     options.Lockout.MaxFailedAccessAttempts = 3;
    
     options.SignIn.RequireConfirmedEmail = true;
    
     options.User.RequireUniqueEmail = true;
    });
  4. Set a cookie policy
  5. //startup.cs
    services.ConfigureApplicationCookie(options =>
    {
     options.Cookie.HttpOnly = true;
     options.Cookie.Expiration = TimeSpan.FromHours(1)
     options.SlidingExpiration = true;
    });

3. Sensitive Data Exposure

What it is?

Rather than directly attacking crypto, attackers steal keys, execute man-in-the-middle attacks, or steal clear text data off the server, while in transit, or from the user’s client, e.g. browser. The most common flaw is simply not encrypting sensitive data. When crypto is employed, weak key generation and management, and weak algorithm, protocol and cipher usage is common, particularly for weak password hashing storage techniques. Failure frequently compromises all data that should have been protected. Typically, this information includes sensitive personal information (PII) data such as health records, credentials, personal data, and credit cards, which often require protection as defined by laws or regulations such as the EU GDPR or local privacy laws.

What to do?

  1. Use a strong hash to store password credentials.
  2. Enforce passwords with a minimum complexity that will survive a dictionary attack i.e. longer passwords that use the full character set (numbers, symbols and letters) to increase the entropy.
  3. Use a strong encryption routine such as AES-512 where personally identifiable data needs to be restored to it's original format.
  4. Use TLS 1.2 for your entire site.
  5. Have a strong TLS policy, use TLS 1.2 wherever possible.
  6. Ensure headers are not disclosing information about your application.

Example for headers

app.UseHsts(hsts => hsts.MaxAge(365).IncludeSubdomains());
app.UseXContentTypeOptions();
app.UseReferrerPolicy(opts => opts.NoReferrer());
app.UseXXssProtection(options => options.FilterDisabled());
app.UseXfo(options => options.Deny());

app.UseCsp(opts => opts
 .BlockAllMixedContent()
 .StyleSources(s => s.Self())
 .StyleSources(s => s.UnsafeInline())
 .FontSources(s => s.Self())
 .FormActions(s => s.Self())
 .FrameAncestors(s => s.Self())
 .ImageSources(s => s.Self())
 .ScriptSources(s => s.Self())
 );

4. XML External Entities (XXE)

What it is?

Attackers can exploit vulnerable XML processors if they can upload XML or include hostile content in an XML document, exploiting vulnerable code, dependencies or integrations. By default, many older XML processors allow specification of an external entity, a URI that is dereferenced and evaluated during XML processing. These flaws can be used to extract data, execute a remote request from the server, scan internal systems, perform a denial-of-service attack, as well as execute other attacks.

What to do?

  1. Use LINQ to XML. Both the XElement and XDocument objects in the System.Xml.Linq library are safe from XXE injection by default.
  2. Use XmlDictionaryReader. System.Xml.XmlDictionaryReader is safe by default, as when it attempts to parse the DTD, the compiler throws an exception saying that "CData elements not valid at top level of an XML document".
  3. Using XmlDocument set XmlResolver to null.
  4.  static void LoadXML()
     {
       string xxePayload = "<!DOCTYPE doc [<!ENTITY win SYSTEM 'file:///C:/Users/testdata2.txt'>]>"
                         + "<doc>&win;</doc>";
       string xml = "<?xml version='1.0' ?>" + xxePayload;
    
       XmlDocument xmlDoc = new XmlDocument();
       // Setting this to NULL disables DTDs - Its NOT null by default.
       xmlDoc.XmlResolver = null;
       xmlDoc.LoadXml(xml);
       Console.WriteLine(xmlDoc.InnerText);
       Console.ReadLine();
     }
  5. Use XmlNodeReader. System.Xml.XmlNodeReader objects are safe by default and will ignore DTDs even when constructed with an unsafe parser or wrapped in another unsafe parser.
  6. Use XmlReader. System.Xml.XmlReader objects are safe by default.
  7. Use XmlTextReader. In .NET Framework versions 4.5.2 and up, XmlTextReader's internal XmlResolver is set to null by default, making the XmlTextReader ignore DTDs by default.
  8. Using XPathNavigator give it a  safe parser like XmlReader (which is safe by default) in the XPathDocument's constructor.
  9. XmlReader reader = XmlReader.Create("example.xml");
    XPathDocument doc = new XPathDocument(reader);
    XPathNavigator nav = doc.CreateNavigator();
    string xml = nav.InnerXml.ToString();
  10. Use XslCompiledTransform. System.Xml.Xsl.XslCompiledTransform (an XML transformer) is safe by default as long as the parser it's given is safe.

5. Broken Access Control

What it is?

Exploitation of access control is a core skill of attackers. Access control is detectable using manual means, or possibly through automation for the absence of access controls in certain frameworks. Access control weaknesses are common due to the lack of automated detection, and lack of effective functional testing by application developers. The technical impact is attackers acting as users or administrators, or users using privileged functions, or creating, accessing, updating or deleting every record.

What to do?

  1. Ensure cookies are sent via httpOnly:
  2. CookieHttpOnly = true,
  3. Reduce the time period a session can be stolen in by reducing session timeout and removing sliding expiration:
  4. ExpireTimeSpan = TimeSpan.FromMinutes(60),
    SlidingExpiration = false
  5. Protect LogOn, Registration and password reset methods against brute force attacks by throttling requests (see code below), consider also using ReCaptcha.
  6. [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    [AllowXRequestsEveryXSecondsAttribute(Name = "LogOn",
    Message = "You have performed this action more than {x} times in the last {n} seconds.",
    Requests = 3, Seconds = 60)]
    public async Task<ActionResult> LogOn(LogOnViewModel model, string returnUrl)
  7. Authorize users on all externally facing endpoints. The .NET framework has many ways to authorize a user, use them at method level:
  8. [Authorize(Roles = "Admin")]
    [HttpGet]
    public ActionResult Index(int page = 1)
    
    
    // or better yet, at controller level:
    [Authorize]
    public class UserController
  9. When you have a resource (object) which can be accessed by a reference (in the sample below this is the id) then you need to ensure that the user is intended to be there
  10. // Insecure
    public ActionResult Edit(int id)
    {
      var user = _context.Users.FirstOrDefault(e => e.Id == id);
      return View("Details", new UserViewModel(user);
    }
    
    // Secure
    public ActionResult Edit(int id)
    {
      var user = _context.Users.FirstOrDefault(e => e.Id == id);
      // Establish user has right to edit the details
      if (user.Id != _userIdentity.GetUserId())
      {
            HandleErrorInfo error = new HandleErrorInfo(
                new Exception("INFO: You do not have permission to edit these details"));
            return View("Error", error);
      }
      return View("Edit", new UserViewModel(user);
    }

P.S. Thanks for reading! Part two coming soon.

Read more about OWASP - https://owasp.org/www-project-top-ten/