Back to Web Vulnerabilities: SQLi, XSS, CSRF, SSRF
Q6Question 6 of 7

Principle of Least Privilege

Key Concepts
  • Least privilege means every user, service, and credential gets only the minimum permissions needed for its function.
  • It limits blast radius: a compromised identity can only do the small set of things it was explicitly allowed.
  • It applies at every layer: application roles, database grants, cloud IAM policies, file permissions, and network access.
  • Just-in-time elevation grants extra rights only for the minutes needed, then automatically revokes them.
  • Common anti-patterns: wildcard permissions, shared admin accounts, and temporary grants that never get removed.
  • In banking, least privilege is often a regulatory requirement, not just good engineering practice.

Definition

Grant the minimum permission needed so a compromise can do minimum damage.
  • Principle of least privilege (PoLP) – every user, service, process, and credential should be granted only the minimum permissions required to perform its function, and nothing more.
  • Blast radius – limits the damage of mistakes, bugs, and compromised accounts by confining what a breached identity can do.
  • Multi-layer – applies at application roles, database grants, cloud IAM policies, file permissions, and network access.
  • Just-in-time elevation – grant extra rights only for the duration needed, then revoke automatically.
  • Separation of duties – no single identity can both initiate and approve a sensitive action.

At National Bank of India, a teller's key opens only the teller drawer, not the vault, and a cleaner's pass opens only the lobby, not the server room. Nobody carries a master key just in case. If a teller's key is stolen, the thief reaches only the teller drawer, not the whole bank. Least privilege is the bank handing each person and each machine exactly the keys their job needs and no more, so one lost key cannot open everything.

Understanding the Concepts

What is the principle of least privilege?

The principle of least privilege states that every user, service, process, and credential should be granted only the minimum permissions required to perform its function, and nothing more. It is the single most effective control for limiting the blast radius of any security incident.

If a reporting service only needs to read balances, it should only have read access. If it is compromised, it cannot move money because it never had write access.

JAVA
package com.bank.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class LeastPrivilegeConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
            // Reporting service: read-only scope
            .requestMatchers("/api/v1/reports/**")
                .hasAuthority("SCOPE_account:read")
            // Transfer service: write scope required
            .requestMatchers("/api/v1/transfers/**")
                .hasAuthority("SCOPE_transfer:write")
            // Admin endpoints: admin scope only
            .requestMatchers("/api/v1/admin/**")
                .hasAuthority("SCOPE_admin")
            .anyRequest().denyByDefault()
        );
        return http.build();
    }
}
TEXT
Reporting service (scope=account:read):
  GET /api/v1/reports/balances --> 200 OK
  POST /api/v1/transfers      --> 403 Forbidden (no write scope)
  GET /api/v1/admin/users      --> 403 Forbidden (no admin scope)

How does it apply at the database layer?

The application database user should have only the SQL statements it needs. Never grant superuser, DROP, or DELETE * to the application account.

SQL
-- Least privilege database grants
CREATE USER reporting_user WITH PASSWORD 'strong_pass';
GRANT SELECT ON accounts, transactions TO reporting_user;
-- Cannot INSERT, UPDATE, DELETE, or DROP anything

CREATE USER transfer_user WITH PASSWORD 'strong_pass';
GRANT SELECT, INSERT ON transactions TO transfer_user;
GRANT SELECT, UPDATE ON accounts TO transfer_user;
-- Can read and modify needed tables, but cannot DROP or access admin tables

CREATE USER admin_user WITH PASSWORD 'strong_pass';
GRANT ALL ON ALL TABLES TO admin_user;
-- Full access, but this account is never used by the application
-- Only used by DBAs with MFA and audit logging

How does it apply at the cloud IAM layer?

Cloud IAM policies should grant specific actions on specific resources rather than wildcard permissions. Roles should be assumed temporarily rather than using long-lived root credentials.

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::bank-statements-bucket/*"
    }
  ]
}
TEXT
WRONG (wildcard):
  "Action": "*",
  "Resource": "*"
  --> Service can do ANYTHING to ANY resource!

CORRECT (least privilege):
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::bank-statements-bucket/*"
  --> Service can only read/write to one specific bucket

What is just-in-time elevation?

Just-in-time (JIT) elevation grants extra permissions only for the duration needed, then automatically revokes them. An admin does not hold permanent elevated access; they request it, perform the action, and it expires.

JAVA
package com.bank.security;

import java.time.Instant;
import java.time.Duration;

public class JitElevationService {

    public ElevationGrant requestElevation(String adminUser, String permission,
                                           Duration duration) {
        Instant expiry = Instant.now().plus(duration);
        ElevationGrant grant = new ElevationGrant(adminUser, permission, expiry);
        System.out.println("Granted " + permission + " to " + adminUser
            + " until " + expiry);
        return grant;
    }

    public boolean isElevated(ElevationGrant grant) {
        boolean active = Instant.now().isBefore(grant.expiry());
        if (!active) {
            System.out.println("Elevation expired for " + grant.user());
        }
        return active;
    }

    record ElevationGrant(String user, String permission, Instant expiry) {}
}
TEXT
Granted account:unlock to priya_admin until 2024-10-27T10:10:00Z
10 minutes later:
Elevation expired for priya_admin
--> Admin loses elevated access automatically

What is separation of duties?

Separation of duties ensures no single identity can both initiate and approve a sensitive action. In banking, one person creates a transfer and a different person approves it.

JAVA
package com.bank.service;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;

@Service
public class TransferService {

    @PreAuthorize("hasRole('TELLER')")
    public String initiateTransfer(String from, String to, BigDecimal amount,
                                   String initiator) {
        // Teller creates the transfer request
        return "Transfer initiated by " + initiator + ": "
            + amount + " from " + from + " to " + to;
    }

    @PreAuthorize("hasRole('MANAGER') and #approver != #initiator")
    public String approveTransfer(String transferId, String approver,
                                  String initiator) {
        // Manager approves, but cannot be the same person who initiated
        return "Transfer " + transferId + " approved by " + approver;
    }
}
TEXT
Priya (TELLER) initiates transfer --> OK
Priya (TELLER) approves same transfer --> 403 Forbidden (same person)
Rajesh (MANAGER) approves --> OK (different person)

What are common anti-patterns?

The classic anti-patterns are wildcard permissions, shared admin accounts, and temporary grants that never get removed (privilege creep).

TEXT
Anti-patterns:
  1. Wildcard:  "Action": "*", "Resource": "*" (grants everything)
  2. Shared:    One admin account used by 5 people (no accountability)
  3. Creep:     Temporary admin grant given 6 months ago, never revoked
  4. Root:      Application running as root/superuser (full OS access)
  5. All-ports: Security group allowing 0.0.0.0:0-65535 (all traffic)

Fixes:
  1. Specific actions on specific resources
  2. Individual accounts with individual audit trails
  3. Regular access reviews + automated expiry
  4. Non-root service accounts with minimal permissions
  5. Allow only required ports from required sources

How do you enforce least privilege in practice?

Regular access reviews and automated detection of over-privileged identities keep privilege creep in check. Tools like AWS Access Analyzer or custom scripts compare granted permissions against actually-used permissions.

JAVA
package com.bank.security;

import java.util.Set;

public class AccessReviewer {

    public void review(String service, Set<String> granted, Set<String> used) {
        Set<String> unused = new java.util.HashSet<>(granted);
        unused.removeAll(used);

        if (!unused.isEmpty()) {
            System.out.println("WARNING: " + service + " has unused permissions: "
                + unused);
            System.out.println("Recommendation: remove " + unused);
        } else {
            System.out.println(service + ": all permissions actively used.");
        }
    }
}
TEXT
WARNING: reporting-service has unused permissions: [account:write, admin]
Recommendation: remove [account:write, admin]

What should a senior engineer know?

Senior engineers know that least privilege is one of the cheapest, highest-impact controls for shrinking the damage any single compromise can cause. In banking it is frequently a regulatory requirement (PCI-DSS, SOC2), not just good hygiene. They combine it with JIT elevation, separation of duties, automated access reviews, and deny-by-default policies.

Visual Overview

DIAGRAM
  Least Privilege Layers:
  ┌──────────────────────────────────────┐
  │ Application Layer                     │
  │   Reporting: SCOPE_account:read only │
  │   Transfer:  SCOPE_transfer:write    │
  │   Admin:     SCOPE_admin (JIT only)  │
  ├──────────────────────────────────────┤
  │ Database Layer                        │
  │   reporting_user: SELECT only        │
  │   transfer_user: SELECT, INSERT, UPD │
  │   No user has DROP or superuser      │
  ├──────────────────────────────────────┤
  │ Cloud IAM Layer                       │
  │   Specific actions on specific ARNs  │
  │   No wildcard * permissions          │
  │   Roles assumed temporarily          │
  ├──────────────────────────────────────┤
  │ OS / Network Layer                    │
  │   Non-root service accounts          │
  │   Minimal open ports                 │
  │   Network segmentation               │
  └──────────────────────────────────────┘

Key Takeaway

The principle of least privilege says that every user, service, process, and credential should be granted only the minimum permissions needed to do its job, and nothing more. Its purpose is to limit the blast radius: if an identity is compromised or makes a mistake, it can only affect the small set of things it was explicitly allowed to touch. It applies at every layer: application roles and scopes, database grants, cloud IAM policies, file permissions, and network access.

In the National Bank of India project, this is applied concretely. The reporting service only needs to read balances for dashboards, so it gets a read-only scope and nothing else; even if it is compromised, it cannot move money because it never held write access. At the database layer the application user is granted only the statements it needs, and in the cloud, specific actions are granted on specific resources rather than wildcard permissions.

For sensitive actions, least privilege is paired with just-in-time, time-bounded elevation. A read-only service reads successfully but is blocked by deny-by-default when it tries to write, and an admin receives a ten-minute elevation that automatically expires afterward, so a stolen admin session outside that window cannot do the privileged action.

The nuances to stress are separation of duties so no single identity can both initiate and approve a sensitive transfer, avoiding the classic anti-patterns of wildcard permissions, shared admin accounts, and temporary grants that never get removed, and running regular access reviews to catch privilege creep. In a bank this is not just good hygiene: it is frequently a regulatory requirement.

Remember one sentence: grant the minimum permission needed for the job so a single compromise cannot open the vault.

---

Share
Use ← → arrow keys to navigate