csharp.dotnet.security.audit.misconfigured-lockout-option.misconfigured-lockout-option

profile photo of returntocorpreturntocorp
Author
unknown
Download Count*

A misconfigured lockout mechanism allows an attacker to execute brute-force attacks. Account lockout must be correctly configured and enabled to prevent these attacks.

Run Locally

Run in CI

Defintion

rules:
  - id: misconfigured-lockout-option
    message: A misconfigured lockout mechanism allows an attacker to execute
      brute-force attacks. Account lockout must be correctly configured and
      enabled to prevent these attacks.
    severity: WARNING
    metadata:
      likelihood: LOW
      impact: HIGH
      confidence: LOW
      category: security
      cwe:
        - "CWE-307: Improper Restriction of Excessive Authentication Attempts"
      owasp:
        - A07:2021 - Identification and Authentication Failures
      references:
        - https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures
        - https://cwe.mitre.org/data/definitions/307.html
      subcategory:
        - audit
      technology:
        - dotnet
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    languages:
      - csharp
    patterns:
      - pattern-either:
          - pattern: |
              $SIGNIN.PasswordSignInAsync(...,lockoutOnFailure: false,...);
          - pattern: |
              $SIGNIN.CheckPasswordSignInAsync(...,lockoutOnFailure: false,...);
      - pattern-inside: |
          public async $TYPE<IActionResult> $METHOD(...) {
            ...
          }

Examples

misconfigured-lockout-option.cs

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // ruleid: misconfigured-lockout-option
        var result = await _signInManager.PasswordSignInAsync(Input.Email, 
            Input.Password, Input.RememberMe, 
            lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl,
                Input.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    return Page();
}

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    if (ModelState.IsValid)
    {
        // ok: misconfigured-lockout-option
        var result = await _signInManager.PasswordSignInAsync(Input.Email, 
            Input.Password, Input.RememberMe, 
            lockoutOnFailure: true);

        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl,
                Input.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    return Page();
}