DEV Community

Cover image for Bitmasking for Backend RBAC
Mueed
Mueed

Posted on

Bitmasking for Backend RBAC


What is Bitmasking?

Bitmasking is a technique that uses binary numbers (bits) to store and manage data efficiently. Each bit (0 or 1) in a number represents a specific state, such as whether a permission is granted or a setting is enabled. By using bitwise operations (like AND, OR, and XOR), we can combine, check, or toggle these states quickly. Bitmasking is powerful because it packs multiple pieces of information into a single number, making it compact and fast.


Why is Bitmasking Important?

Bitmasking is highly efficient and offers several key benefits:

  • Saves Space: A single number can represent multiple settings or permissions, reducing storage needs compared to lists of strings.
  • Super Fast: Bitwise operations are low-level, executed directly by the computer's processor, making them much faster than operations like searching through lists.
  • Flexible: Easily combine, check, or modify states with simple operations.
  • Universal: Works across nearly all programming languages and is widely used in various industries.

Uses of Bitmasking

Bitmasking is applied in both software and non-technical fields:

In Software

  • Permissions and Access Control: Manages user permissions (e.g., read, write, delete) in systems like Role-Based Access Control (RBAC).
  • Flag Management: Tracks settings or states (e.g., on/off flags) in low-memory systems, such as embedded devices, using minimal memory.
  • Networking: Extracts information from IP addresses or subnet masks using bitwise operations.
  • Graphics and Gaming: Used for image masks, collision detection, or tracking game states (e.g., active power-ups).
  • Encryption: Employs bitwise operations like XOR in algorithms such as AES or XOR ciphers.
  • Optimization: Speeds up set manipulations or mathematical calculations (e.g., multiplying by powers of 2).

In Non-Technical Fields

  • Electronics: Controls hardware settings, such as enabling specific pins on a microcontroller.
  • Robotics: Manages sensor states (e.g., which sensors are active) in compact systems.
  • Telecommunications: Encodes signals or manages channel states in communication systems.
  • Data Compression: Represents patterns or flags in compressed data formats.

Bitmasking’s ability to simplify complex state management makes it a versatile technique across these domains.


Why String-Based Permissions Are Problematic

In traditional Role-Based Access Control (RBAC), roles are linked to lists of permissions stored as strings (e.g., view_email, update_phone). For example, a "Reviewer" role might have view_email and view_phone. To check if a user can update_phone, the system searches through the list, comparing each string. This approach has two major drawbacks:

  • Slow Performance: Searching a list of strings takes time, especially with many permissions or frequent requests.
  • Large Data Size: Storing and sending string lists, particularly in JSON Web Tokens (JWTs), increases data size, slowing network requests and often requiring extra backend calls.

Understanding Bitwise Operations

Bitmasking relies on bitwise operations, which work directly on binary digits and are extremely fast. The key operations are:

  • AND (&): Checks if specific bits are set (e.g., to verify a permission).
  • OR (|): Combines settings by setting bits to 1 (e.g., to assign multiple permissions).
  • XOR (^): Toggles bits (flips 0 to 1 or 1 to 0), useful for switching states.
  • LEFT SHIFT (<<): Moves bits left, multiplying by powers of 2 (e.g., 1 << 2 = 4, or 0b0100).
  • RIGHT SHIFT (>>): Moves bits right, dividing by powers of 2.

For example, to create a permission for the 3rd bit (position 2, starting from 0):

  • 1 << 2 = 4 (binary: 0b0100)

How Bitmasking Works in RBAC

Bitmasking simplifies permission management by assigning each permission a unique power of two (ensuring only one bit is 1 in its binary form):

  • READ = 1 (0b0001)
  • CREATE = 2 (0b0010)
  • UPDATE = 4 (0b0100)
  • DELETE = 8 (0b1000)

It uses two main steps:

  1. Combining Permissions (OR): Combine permissions into one number using |. Example: A user with READ (1) and UPDATE (4):
    • 0b0001 | 0b0100 = 0b0101 (decimal: 5) The number 5 represents both permissions.
  2. Checking Permissions (AND): Check if a user has a permission using &. If the result equals the permission value, they have it. Example: User has 0b0101 (5, READ + UPDATE).
    • Check READ (1): 0b0101 & 0b0001 = 0b0001 (equals 1) → Access Granted.
    • Check CREATE (2): 0b0101 & 0b0010 = 0b0000 (not 2) → Access Denied.

These operations are fast because they work at the binary level.


A Simple Example: Audio Mixer Permissions

Imagine building an audio mixer with four channels: Left, Right, Up, and Down. Each channel is active (1) or inactive (0), represented by a 4-bit number:

  • LEFT = 1 << 0 = 1 (0b0001)
  • RIGHT = 1 << 1 = 2 (0b0010)
  • UP = 1 << 2 = 4 (0b0100)
  • DOWN = 1 << 3 = 8 (0b1000)

Step 1: Activate Channels
To activate Left and Up:

  • permissions = LEFT | UP = 0b0001 | 0b0100 = 0b0101 (decimal: 5)

Step 2: Toggle Channels
To toggle Right and Up (flip their states):

  • permissions = permissions ^ (RIGHT | UP) = 0b0101 ^ (0b0010 | 0b0100) = 0b0101 ^ 0b0110 = 0b0011 (decimal: 3, now Left and Right are active).

This uses one number to manage all channel states.


Benefits of Bitmasking

  • Fast: Bitwise operations are instant, regardless of the number of permissions.
  • Compact: A single number (e.g., 5) holds multiple permissions, saving space in databases and JWTs.
  • Flexible: Combine or toggle permissions without creating new roles.
  • Universal: Works in languages like Python, JavaScript, Java, and more.
  • Clean Code: Simplifies permission logic for easier maintenance.

Implementing Bitmasking in a Backend

Here’s a simple way to use bitmasking for RBAC:

  1. Define Permissions: Assign power-of-two values.

    READ = 1      # 0b0001
    CREATE = 2    # 0b0010
    UPDATE = 4    # 0b0100
    DELETE = 8    # 0b1000
    
  2. Store Permissions: Save the combined permissions (e.g., 5 for READ + UPDATE) in the database and JWT.

  3. Check Permissions: Verify access in your backend.

    if (user_permissions & required_permission) == required_permission:
        # Allow access
    else:
        # Deny access (403 Forbidden)
    
  4. Middleware: Add a check before each route to ensure required permissions.

Handling Many Permissions

A 64-bit number can hold up to 64 permissions. For more, use an array of numbers (virtual bitfield). For example, permission 70 would be in the second number (index 1, bit 6). This keeps bitmasking fast and scalable.

Limitations of Bitmasking

  • Learning Curve: Bitwise operations can be confusing for beginners.
  • Limited Size: A single number supports 32 or 64 permissions, though arrays can help.
  • Not Universal: Best for permissions or flags, not all problems.

Try It Yourself: Hands-On Bitmasking Demo

For a practical, hands-on experience with bitmasking in RBAC, check out the FastAPI Bitmasking RBAC Demo on GitHub:

FastAPI Bitmasking RBAC Demo

This public repository provides a working FastAPI application with clear comments and a step-by-step guide to set up and test bitmasking-based permission control. Follow the instructions to explore how bitwise operations manage permissions efficiently in a real backend system.


Top comments (0)