Access Control List in .NET Framework

Last Updated: Nov 13, 2025
7 min read
Legacy Archive
Legacy Guidance: This article preserves historical web development content. For modern .NET 8+ best practices, visit our Tutorials section.

Introduction

Once you complete developing a web application, you need to secure it. There will be portions of your application that need protection from unauthorized access. Securing an application may require extra hardware to build complex multi-layer systems with firewalls and highly secure features. Security enables you to provide access to specified users after they're authenticated and authorized to access resources in your web application. The Access Control List (ACL) is a key component used in the authorization process.

Understanding Security Fundamentals

The basic concepts of security are authentication, authorization, impersonation, and data or functional security. Let's break down each concept:

Authentication: The process that identifies a user, ensuring only that user is provided access to resources. Think of it as showing your ID card at a building entrance.

Authorization: The process that determines whether a particular user can access the resources they request. This is like checking if your ID card grants you access to specific floors in the building.

Impersonation: The process that provides access to resources under a different identity. This is useful when an application needs to perform actions on behalf of a user.

Data or functional security: The process of securing a system physically, updating the operating system, and using robust software to prevent vulnerabilities.

How Security Components Work Together

Several elements work in coordination to provide security features: the operating system, Internet Information Server (IIS), and the .NET Framework.

Here's how the process flows:

Windows uses its own list of user accounts for identifying and authenticating users. When users access a website, IIS identifies them based on information provided by Windows. After identification, IIS passes this information to ASP.NET, which then checks for authorization.

Let's look at a practical example of configuring authentication in web.config:

web.config Authentication
<configuration>
    <system.web>
        <authentication mode="Windows">
            <forms loginUrl="~/Login.aspx" 
                   timeout="30" 
                   slidingExpiration="true" />
        </authentication>
        
        <authorization>
            <deny users="?" />  <!-- Deny anonymous users -->
            <allow users="*" /> <!-- Allow authenticated users -->
        </authorization>
    </system.web>
</configuration>

Authentication in Detail

To restrict access to certain resources, you need a process to identify users. Authentication enables you to verify users through various methods:

  • Username and password combination
  • Digital certificates
  • Smart cards
  • Biometric authentication like fingerprints

The validity of the information provided by the user helps identify them, granting access to requested resources. Successful identification means the user is authenticated.

Here's how you can implement forms authentication in ASP.NET:

ASP.NET Forms Authentication
// Login.aspx.cs
protected void LoginButton_Click(object sender, EventArgs e)
{
    string username = UsernameTextBox.Text;
    string password = PasswordTextBox.Text;
    
    // Validate credentials against database
    if (ValidateUser(username, password))
    {
        // Create authentication ticket
        FormsAuthentication.SetAuthCookie(username, RememberMeCheckBox.Checked);
        
        // Redirect to requested page or default page
        string returnUrl = Request.QueryString["ReturnUrl"];
        if (!string.IsNullOrEmpty(returnUrl))
        {
            Response.Redirect(returnUrl);
        }
        else
        {
            Response.Redirect("~/Default.aspx");
        }
    }
    else
    {
        ErrorLabel.Text = "Invalid username or password";
    }
}

private bool ValidateUser(string username, string password)
{
    // Hash the password before comparing
    string hashedPassword = HashPassword(password);
    
    // Check against database
    using (SqlConnection conn = new SqlConnection(connectionString))
    {
        string query = "SELECT COUNT(*) FROM Users WHERE Username = @Username AND PasswordHash = @PasswordHash";
        SqlCommand cmd = new SqlCommand(query, conn);
        cmd.Parameters.AddWithValue("@Username", username);
        cmd.Parameters.AddWithValue("@PasswordHash", hashedPassword);
        
        conn.Open();
        int count = (int)cmd.ExecuteScalar();
        return count > 0;
    }
}

In ASP.NET Core, authentication is configured differently:

ASP.NET Core Authentication
// Program.cs or Startup.cs
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login";
        options.AccessDeniedPath = "/Account/AccessDenied";
        options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        options.SlidingExpiration = true;
    });

// Login action in controller
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await _userService.ValidateUser(model.Username, model.Password);
        
        if (user != null)
        {
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.Username),
                new Claim(ClaimTypes.Email, user.Email),
                new Claim(ClaimTypes.Role, user.Role)
            };
            
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var authProperties = new AuthenticationProperties
            {
                IsPersistent = model.RememberMe,
                ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30)
            };
            
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authProperties);
            
            return RedirectToAction("Index", "Home");
        }
        
        ModelState.AddModelError(string.Empty, "Invalid username or password");
    }
    
    return View(model);
}

Authorization and Access Control Lists

After authentication, the next step is determining whether the authenticated user has access to the requested resources. This process is known as authorization.

In Windows-based systems, resources have an Access Control List that provides a list of users who have access to that resource. The list also specifies the type of access, such as read, write, modify, and delete, for each user.

For example, if a user requests an ASP page, the operating system checks whether the user has read access to the page. If the user has read permission, the operating system allows IIS to fetch the page.

Here's how you can implement role-based authorization in web.config:

web.config Role Authorization
<configuration>
    <location path="Admin">
        <system.web>
            <authorization>
                <allow roles="Administrator" />
                <deny users="*" />
            </authorization>
        </system.web>
    </location>
    
    <location path="Reports">
        <system.web>
            <authorization>
                <allow roles="Administrator,Manager" />
                <deny users="*" />
            </authorization>
        </system.web>
    </location>
</configuration>

In your ASP.NET pages, you can use the Authorize attribute:

ASP.NET Authorize Attribute
// Require authentication
[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

// Require specific role
[Authorize(Roles = "Administrator")]
public class AdminController : Controller
{
    public ActionResult Dashboard()
    {
        return View();
    }
}

// Require multiple roles
[Authorize(Roles = "Administrator,Manager")]
public class ReportsController : Controller
{
    public ActionResult ViewReports()
    {
        return View();
    }
}

In ASP.NET Core, authorization is more flexible with policy-based authorization:

ASP.NET Core Policy Authorization
// Configure authorization policies
services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => 
        policy.RequireRole("Administrator"));
    
    options.AddPolicy("ManagerOrAdmin", policy =>
        policy.RequireRole("Manager", "Administrator"));
    
    options.AddPolicy("MinimumAge", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));
});

// Use in controllers
[Authorize(Policy = "AdminOnly")]
public class AdminController : Controller
{
    public IActionResult Dashboard()
    {
        return View();
    }
}

[Authorize(Policy = "ManagerOrAdmin")]
public IActionResult ViewReports()
{
    return View();
}

Working with File Access Control Lists

File Access Control Lists are set for a given file or directory. In .NET, you can programmatically work with ACLs using the FileSystemAccessRule class.

Here's how to read and modify file ACLs:

FileACLManager.cs
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

public class FileACLManager
{
    public void DisplayFileACL(string filePath)
    {
        // Get the file's ACL
        FileInfo fileInfo = new FileInfo(filePath);
        FileSecurity fileSecurity = fileInfo.GetAccessControl();
        
        // Get all access rules
        AuthorizationRuleCollection rules = fileSecurity.GetAccessRules(true, true, typeof(NTAccount));
        
        Console.WriteLine($"Access rules for {filePath}:");
        foreach (FileSystemAccessRule rule in rules)
        {
            Console.WriteLine($"User: {rule.IdentityReference.Value}");
            Console.WriteLine($"Rights: {rule.FileSystemRights}");
            Console.WriteLine($"Type: {rule.AccessControlType}");
            Console.WriteLine("---");
        }
    }
    
    public void GrantReadAccess(string filePath, string username)
    {
        FileInfo fileInfo = new FileInfo(filePath);
        FileSecurity fileSecurity = fileInfo.GetAccessControl();
        
        // Create access rule for read permission
        FileSystemAccessRule accessRule = new FileSystemAccessRule(
            username,
            FileSystemRights.Read,
            AccessControlType.Allow);
        
        // Add the rule
        fileSecurity.AddAccessRule(accessRule);
        
        // Apply changes
        fileInfo.SetAccessControl(fileSecurity);
        
        Console.WriteLine($"Read access granted to {username} for {filePath}");
    }
    
    public void RevokeAccess(string filePath, string username)
    {
        FileInfo fileInfo = new FileInfo(filePath);
        FileSecurity fileSecurity = fileInfo.GetAccessControl();
        
        // Create access rule to remove
        FileSystemAccessRule accessRule = new FileSystemAccessRule(
            username,
            FileSystemRights.FullControl,
            AccessControlType.Allow);
        
        // Remove the rule
        fileSecurity.RemoveAccessRule(accessRule);
        
        // Apply changes
        fileInfo.SetAccessControl(fileSecurity);
        
        Console.WriteLine($"Access revoked for {username} on {filePath}");
    }
    
    public bool CheckUserAccess(string filePath, string username, FileSystemRights rights)
    {
        FileInfo fileInfo = new FileInfo(filePath);
        FileSecurity fileSecurity = fileInfo.GetAccessControl();
        
        AuthorizationRuleCollection rules = fileSecurity.GetAccessRules(true, true, typeof(NTAccount));
        
        foreach (FileSystemAccessRule rule in rules)
        {
            if (rule.IdentityReference.Value.Equals(username, StringComparison.OrdinalIgnoreCase))
            {
                if ((rule.FileSystemRights & rights) == rights && 
                    rule.AccessControlType == AccessControlType.Allow)
                {
                    return true;
                }
            }
        }
        
        return false;
    }
}

// Usage example
FileACLManager aclManager = new FileACLManager();

// Display current ACL
aclManager.DisplayFileACL(@"C:\MyApp\Config\settings.xml");

// Grant read access to a user
aclManager.GrantReadAccess(@"C:\MyApp\Config\settings.xml", "DOMAIN\\JohnDoe");

// Check if user has write access
bool hasWriteAccess = aclManager.CheckUserAccess(
    @"C:\MyApp\Config\settings.xml", 
    "DOMAIN\\JohnDoe", 
    FileSystemRights.Write);

Console.WriteLine($"User has write access: {hasWriteAccess}");

Working with Directory ACLs

You can also manage ACLs for directories:

Directory ACL Management
public void SetDirectoryPermissions(string directoryPath, string username)
{
    DirectoryInfo dirInfo = new DirectoryInfo(directoryPath);
    DirectorySecurity dirSecurity = dirInfo.GetAccessControl();
    
    // Create access rule for full control
    FileSystemAccessRule accessRule = new FileSystemAccessRule(
        username,
        FileSystemRights.FullControl,
        InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
        PropagationFlags.None,
        AccessControlType.Allow);
    
    // Add the rule
    dirSecurity.AddAccessRule(accessRule);
    
    // Apply changes
    dirInfo.SetAccessControl(dirSecurity);
    
    Console.WriteLine($"Full control granted to {username} for {directoryPath}");
}

public void RemoveInheritedPermissions(string directoryPath)
{
    DirectoryInfo dirInfo = new DirectoryInfo(directoryPath);
    DirectorySecurity dirSecurity = dirInfo.GetAccessControl();
    
    // Disable inheritance and preserve inherited permissions
    dirSecurity.SetAccessRuleProtection(true, true);
    
    // Apply changes
    dirInfo.SetAccessControl(dirSecurity);
    
    Console.WriteLine($"Inheritance disabled for {directoryPath}");
}

Implementing Custom Authorization

You can create custom authorization logic for more complex scenarios:

CustomAuthorizationFilter.cs
public class CustomAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        
        if (!user.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult();
            return;
        }
        
        // Custom logic: Check if user has access based on IP address
        string ipAddress = context.HttpContext.Connection.RemoteIpAddress?.ToString();
        if (!IsAllowedIP(ipAddress))
        {
            context.Result = new ForbidResult();
            return;
        }
        
        // Custom logic: Check if user's account is active
        string userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!IsAccountActive(userId))
        {
            context.Result = new ForbidResult();
        }
    }
    
    private bool IsAllowedIP(string ipAddress)
    {
        // Check against allowed IP addresses
        var allowedIPs = new[] { "192.168.1.100", "10.0.0.50" };
        return allowedIPs.Contains(ipAddress);
    }
    
    private bool IsAccountActive(string userId)
    {
        // Check database for account status
        return true; // Simplified for example
    }
}

// Apply to controller or action
[ServiceFilter(typeof(CustomAuthorizationFilter))]
public class SecureController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Best Practices for Access Control

Here are some best practices when implementing access control:

  • Use the principle of least privilege: Grant users only the permissions they need to perform their tasks.
  • Implement defense in depth: Use multiple layers of security, including authentication, authorization, and encryption.
  • Regularly audit access controls: Review and update ACLs periodically to ensure they reflect current requirements.
  • Use role-based access control (RBAC): Assign permissions to roles rather than individual users for easier management.
  • Log access attempts: Keep records of both successful and failed access attempts for security monitoring.

Here's an example of logging access attempts:

AccessLogger.cs
public class AccessLogger
{
    private readonly ILogger<AccessLogger> _logger;
    
    public AccessLogger(ILogger<AccessLogger> logger)
    {
        _logger = logger;
    }
    
    public void LogAccessAttempt(string username, string resource, bool success)
    {
        if (success)
        {
            _logger.LogInformation(
                "User {Username} successfully accessed {Resource} at {Timestamp}",
                username, resource, DateTime.UtcNow);
        }
        else
        {
            _logger.LogWarning(
                "User {Username} failed to access {Resource} at {Timestamp}",
                username, resource, DateTime.UtcNow);
        }
    }
}

Wrapping Up

Access Control Lists are essential for securing your .NET applications. By properly implementing authentication and authorization, you can ensure that only authorized users access sensitive resources. The .NET Framework provides robust tools for working with ACLs, both at the application level and the file system level. Understanding these concepts and implementing them correctly will help you build secure, reliable applications that protect your users and their data.

Quick FAQ

What is an Access Control List (ACL) in .NET?

An Access Control List (ACL) in .NET is a list associated with a resource (like files or directories) that specifies which users or groups have permission to access it and what type of access (read, write, etc.) they have.

What is the difference between authentication and authorization?

Authentication verifies the identity of a user (who you are), while authorization determines what authenticated users are allowed to do (what you can access).

How do you implement forms authentication in ASP.NET?

Forms authentication in ASP.NET uses a login page to validate credentials, then sets an authentication cookie using FormsAuthentication.SetAuthCookie() to maintain the user's session across requests.

What is role-based authorization in .NET?

Role-based authorization in .NET assigns permissions to roles (e.g., Admin, User) rather than individual users, and uses attributes like [Authorize(Roles = "Admin")] to restrict access to actions or controllers.

How can you manage file ACLs programmatically in .NET?

In .NET, manage file ACLs using System.Security.AccessControl classes like FileSecurity and FileSystemAccessRule to add, remove, or check access rules for files and directories.

Back to Articles