Skip to content
Ashish.
All posts
Diagram illustrating the separation of intrinsic and derived attributes in an ABAC system.
6 min readDevelopmentArchitects, Backend Developers#abac#access control#attribute model#security architecture#backend development

Designing an Attribute Model for ABAC

A practical guide to designing an effective attribute model for Attribute-Based Access Control, covering subject, resource, and environment attributes.

By Ashish KumarPart 2 of Attribute-Based Access Control

Designing an Attribute Model

Most engineers treat Attribute-Based Access Control (ABAC) as a drop-in replacement for Role-Based Access Control (RBAC). They create a table called attributes and start stuffing keys into it. This approach fails because ABAC is not about storage; it is about context. An attribute model is a semantic contract between the system enforcing the access control (the Policy Enforcement Point, or PEP) and the system making the decision (the Policy Decision Point, or PDP).

If you design the attribute model poorly, your policies will become fragile. Every time a database schema changes, you must rewrite your access rules. A robust attribute model decouples the identity of the data from the logic that controls it.

The Core Domains

According to the NIST Special Publication 800-162 [link to nist.gov], an ABAC system relies on four distinct categories of attributes. Effective abac design requires strict adherence to these four domains. Your model must provide a clean interface for each.

  1. Subject Attributes: These describe who is making the request. Examples include user.id, user.department, user.clearance_level, and user.manager_id. Note that these are not just static fields from a user table; they are the contextual view of the user required by security policy.
  2. Resource Attributes: These describe what is being accessed. Examples include resource.type, resource.classification (e.g., "Confidential"), resource.owner, and resource.tenant_id.
  3. Environment Attributes: These describe when and where the request occurs. Examples include environment.time, environment.ip_address, environment.device_id, and environment.is_secure_network.
  4. Action Attributes: Often overlooked, these describe how the access is attempted. Examples include action.method (GET, POST), action.resource_format (JSON, CSV).

Intrinsic vs. Derived: The Critical Distinction

The most common design error is conflating stored data with evaluated context. You must separate Intrinsic Attributes from Derived Attributes.

Intrinsic attributes are facts stored in a system of record. They are stable and immutable over short periods.

  • subject.role = "engineer"
  • resource.classification = "internal"

Derived attributes are computed at the time of evaluation based on intrinsic attributes or external signals. They represent state rather than fact.

  • subject.is_on_call = true (computed from on-call schedule service)
  • environment.is_risky_location = true (computed by checking environment.ip_address against a threat intel feed)

This separation is a fundamental principle of secure security architecture, preventing lateral movement if one attribute source is compromised. Why does this distinction matter? Because of policy churn. If you embed business logic into your attribute model (e.g., storing subject.can_access_payroll as a boolean flag), you tie your access control to a specific business rule. If HR changes the criteria for payroll access, you must update the attribute store and the policy. By keeping attributes atomic (intrinsic) and pushing the logic to the PDP, you isolate the complexity.

A Worked Scenario: The Payroll Request

Let’s trace the mechanism of a real request to see how the attribute model functions under load.

Actor: Alice, a senior engineer in the Engineering department. Resource: payroll_data.csv (Classification: Confidential). Context: Alice is accessing from her corporate laptop at 2:00 PM on a Tuesday.

Step 1: Attribute Gathering (PEP)

The PEP intercepts the HTTP request GET /api/v1/payroll_data.csv. It does not evaluate logic yet. It gathers attributes from their respective sources:

{
  "subject": {
    "id": "alice@example.com",
    "role": "engineer",
    "department": "engineering",
    "manager_id": "bob@example.com"
  },
  "resource": {
    "id": "payroll_data.csv",
    "classification": "confidential",
    "owner": "hr-department"
  },
  "environment": {
    "time": "2024-05-21T14:00:00Z",
    "ip_address": "10.0.0.5",
    "device_id": "laptop-alice-01",
    "network_type": "corporate_vpn"
  },
  "action": {
    "method": "GET"
  }
}

Notice that the PEP did not calculate if Alice is allowed. It simply aggregated the raw context. This is crucial. The PEP is a data aggregator, not a logic engine.

Avoiding the "God Object" Anti-Pattern

A common pitfall is creating a single flat table of attributes for all subjects and resources. This leads to attribute bloat, security leakage, and over-engineering.

  1. Attribute Bloat: If you store every possible attribute for every user, you create a massive, slow-to-query structure. Instead, use a federated model. The PEP should query only the necessary sources. For example, do not fetch user.favorite_color when evaluating access to a bank account.
  2. Security Leakage: Do not expose internal implementation details as attributes. For example, avoid exposing user.password_hash_strength or resource.db_query_plan as attributes. These are implementation details, not access control contexts.
  3. Over-engineering derived attributes: Avoid creating complex dependency chains for derived attributes that cause latency. Keep derived logic simple and idempotent. If a derived attribute requires multiple nested calls to external services, consider pre-computing it or simplifying the policy logic.

Performance Implications

Fetching derived attributes on every request has a cost. If your attribute model requires calling an external geolocation API for every environment.ip_address, you introduce latency.

Optimization Strategy: Cache derived attributes where possible. If environment.is_risky_location is determined by an IP reputation service, cache the result for a short TTL (e.g., 5 minutes). However, ensure that the cache invalidation strategy is reliable. If an IP is flagged as malicious, the cache must expire quickly.

Conclusion

Designing an attribute model is an exercise in abstraction. You are building a lens through which the PDP views the world. The lens must be wide enough to capture all necessary context but narrow enough to exclude noise.

  1. Separate intrinsic from derived: Keep facts in the store, compute context in the policy.
  2. Define clear domains: Subject, Resource, Environment, Action.
  3. Minimize coupling: Policies should depend on attribute names, not attribute values.

By treating the attribute model as a semantic contract rather than a database schema, you create an ABAC system that is scalable, maintainable, and secure.

Related posts