Feature image for secure coding.

There are many sophisticated forms of access control nowadays. Yet breaches are still common. Why does access control fail? This article answers that question by describing some of the flaws that lead to broken access control (also known as broken authorization or privilege escalation) and demonstrates secure coding.

What is access control?

Data protection is crucial, particularly in the field of information security. Personal information, financial records, and other sensitive data should not be accessible to everyone. Access control is a critical element of data security that designates who is permitted to view or change resources and information in a computing environment. Depending on users’ specified roles and associated rights, they are either granted or refused access to different resources and functions by appropriately planned and executed authorization frameworks.

If access control is inadequate or flawed, a hostile actor could gain access to restricted material, edit or remove content, and carry out unauthorized tasks, even potentially take over the whole site. The only thing limiting the attack's harm is the rights given to a compromised user account.

3 Methods of access control

There are three general methods for implementing logical access control in a software system: 

1. Discretionary access control (DAC)

In discretionary access control, access to particular resources is controlled by the data's owner. Users have complete control over all their objects and attributes when using this sort of access control. Access to objects can be restricted based on who is attempting to get access to them. Most operating systems implement this type of access control using attributes (read, write, execute, etc.) to indicate the rights assigned to each user for the object being protected.

The objects of the system (files, directories, etc.) in Linux systems include three traditional permissions: read (r), write (w), and execute (x). The root user grants these permissions to the group and other users (users who are not owners or members of the group).

2. Role-based access control (RBAC)

In a complicated system with many users and functions, discretionary access restrictions do not offer enough granularity to permit a well-defined and organized segmentation. Personnel specialists in human resources, for instance, shouldn't be allowed to create network accounts.

In this situation, assigning rights based on roles rather than individual users or groups offers more freedom. For instance, RBAC permits access related to job titles. RBAC substantially eliminates discretion when granting access to objects.

The modern solution for RBAC is the Lightweight Directory Access Protocol (LDAP). The Red Hat Directory Server provides an affordable LDAP solution. OpenLDAP and FreeIPA are open source solutions.

3. Managed access control (MAC)

In managed access control, a central authority controls access permissions depending on several degrees of security. This access method offers additional security for access and privilege handling.

MAC tags every system component before being subjected to the access control policies set by the administrators. Even when a person passes other security restrictions, MAC examines the tags. The predefined MAC policies will determine whether to permit the operation.

Modern Linux systems (including Red Hat Enterprise Linux and Fedora) support Security Enhanced Linux (SELinux) to provide MAC.

Examples of broken access control

Broken access control refers to various problems that result from the improper application of checks which determine user access. Implementing authorization across dynamic systems is difficult, and policies can become inconsistent when user roles, authentication libraries, and protocols change. All web applications, databases, operating systems, and other technical infrastructures dependent on permission restrictions are vulnerable to such flaws.

There are two primary types of authorization attacks to think about from the user's perspective: horizontal and vertical bypassing.

Horizontal bypassing of authorization control

Horizontal bypassing means that an unprivileged user gains access to other users' accounts with equivalent privileges. Consider a scenario where an application requests account information from a downstream method and accepts unscreened data from the method. An insecure downstream method might obtain account information through a query string like the following:

https://developers.redhat.com/author?Id=273801

https://developers.redhat.com/author?Id=273802

An attacker can readily change the ID argument in the HTTP request to access data from one or many users' accounts. Similarly, when an unscreened user input directly accesses objects, it is called an insecure direct object reference (IDOR).

Vertical bypassing of authorization control

Vertical bypassing of authorization control means that an attacker gets a higher level of privilege (i.e., root or superuser privilege) than the system intended to grant.

For instance, suppose an attacker navigates to the second URL in the following example, where access to the admin dashboard should need administrative permissions:

https://developers.redhat.com/contributor/dashboard

​​​​​​​https://developers.redhat.com/admin/dashboard

Suppose the service does not verify that the current user's role matches the role necessary to access particular resources. In that case, a person without administrative rights can easily access any URL by simply knowing or guessing the targeted URL and visiting it.

How to prevent broken access control

Developers can take several fundamental precautions to guard against broken access control attacks. A strong process begins with a review of the application's access control needs and the creation of a security policy that aligns with the results. A review should include an access control matrix, a detailed statement of what kinds of users are permitted access, and a list of the permitted and prohibited uses of that access.

Utilize role-based access control (RBAC) to impose proper limits on each role. Be sure that the access control mechanism is appropriately applied, on the server side, on all pages and API endpoints for web applications. Simply asking for direct access to a website or resource should not allow users to gain access to any unauthorized functionality or data.

Here is a checklist to prevent broken access control:

  • Never use obfuscation as the sole method of access control.
  • Specify the degree of access permitted for each resource at the code level, denying all access by default.
  • Deny access by default if a resource isn't meant to be shared with the general public.
  • Audit and thoroughly test access control to ensure it functions as intended.

A secure coding example

Let's look at how to ensure proper access control in a sample program. The example in this article is written in the Go programming language, which lacks native solutions for this issue. Some other frameworks provide specialized methods for implementing strong access control.

The following Go framework takes some action for users who have been authenticated. The code submits the user ID to a service named​​​​​​​ authenticationMiddleware to guarantee that only authorized users trigger ActionHandler:

e := echo.New()

e.POST("/action/:id", ActionHandler, authenticationMiddleware)

This access check might not be sufficient. Correct authorization must be maintained wherever the program provides access to resources to prevent a bypass. For instance, suppose the ActionHandler method invoked by the program offers access to a sensitive resource:

func ActionHandler(c echo.Context) error {

    id := c.Param("Id")

    resource := getResourceBy("Id")

    resource.Action()

    return c.String(http.StatusOK, "OK")

}

Developers of ActionHandler must make sure that access control checks are carried out before granting access:

func ActionHandler(c echo.Context) error {

    id := c.Param("id")

    resource := getResourceBy("id")

    if resource.Owner() != getOwnerFromContext(c) {

        return c.String(http.StatusUnauthorized, "User is not Authorized for this Action.")

    }

    resource.Action()

    return c.String(http.StatusOK, "OK")

}

Preventing broken access control is crucial

Be diligent in using the tools that manage and monitor the security of your assets, even if it feels like they require a lot of work. You can find information about authorization bypassing in the OWASP CheetSheet. Stop cyberattacks using secure coding techniques and ongoing security testing. Learn more about secure coding practice by reviewing the OWASP secure coding practice guide. Go programmers can read Black Hat Go for information about security penetration testing.

Last updated: October 30, 2023