PEP (Policy Enforcement Point)

Authorization Security Notes Jan 6, 2025 JAVASCRIPT

Definition

Imagine a fancy building with a security guard at the door. The guard doesn’t decide who’s allowed in - that’s management’s job. But when you show up, the guard is the one who actually checks your credentials, calls management if needed, and physically blocks or allows your entry. That guard is the Policy Enforcement Point (PEP) - the muscle of your authorization system, the component that actually says “you shall pass” or “access denied.”

The PEP sits between users and protected resources, intercepting every access request. When someone tries to delete a document, read a file, or call an API endpoint, the PEP steps in first. It gathers the relevant context (who’s asking, what they want, what resource they’re targeting), asks the PDP (Policy Decision Point) whether this should be allowed, and then enforces that decision. If the PDP says deny, the PEP blocks the request. If the PDP says allow, the PEP lets it through. The PEP doesn’t think - it acts.

What makes PEPs crucial is their placement. They must be unavoidable checkpoints that every request must pass through. If there’s any way to reach a protected resource without going through the PEP, your entire authorization system has a bypass vulnerability. This is why API gateways, reverse proxies, and middleware are common PEP implementations - they’re positioned so that no request can sneak around them. A PDP without a PEP is just rules on paper. A PEP without a PDP is just a guard without instructions. Together, they form complete access control.

Example

PEPs are the checkpoints enforcing security decisions across modern systems:

API Gateway as PEP: Every request to your company’s API hits Kong, Apigee, or AWS API Gateway first. The gateway extracts the JWT token, figures out what endpoint is being called, queries the authorization service (PDP), and either forwards the request to the backend or returns 403 Forbidden. The backend never sees unauthorized requests.

Kubernetes Admission Controllers: When someone tries to deploy a pod in Kubernetes, admission controllers act as PEPs. They intercept the deployment request, check if it violates any policies (resource limits, image sources, security contexts), and either allow the deployment or reject it with an explanation.

Database Row-Level Security: PostgreSQL’s RLS policies turn the database into a PEP. When a user queries patient records, PostgreSQL intercepts the query and modifies it to only return rows that user is authorized to see. The application doesn’t need to filter - the database enforces access at the query level.

Web Application Firewall (WAF): Cloudflare or AWS WAF acts as a PEP for web traffic. It intercepts every HTTP request, evaluates it against security rules (SQL injection patterns, rate limits, geographic restrictions), and either allows the request through or blocks it. Your application server never processes malicious requests.

File System Permissions: When a process tries to read /etc/shadow, the operating system’s kernel acts as PEP. It checks the process’s effective UID against the file’s permissions and either allows the read or returns “Permission denied.” Every file operation goes through this kernel-level enforcement.

Analogy

The Security Guard at the Door: A security guard doesn’t write the guest list (that’s management/PDP), but they’re the ones who check IDs, verify names against the list, and physically block entry for unauthorized people. If the guard steps away, anyone can walk in - that’s why PEPs must be always-on and unavoidable.

The Bouncer at the Club: The club owner sets policies (dress code, VIP list, capacity limits). The bouncer (PEP) enforces them - checking IDs, eyeing outfits, counting heads. The bouncer might radio the manager for special cases (calling the PDP), but they’re the ones physically at the door making sure rules are followed.

The Toll Booth Operator: Highway authorities decide toll prices and who gets discounts (PDP). The toll booth (PEP) is where you actually stop, pay, and get let through - or get denied if your payment fails. There’s no way to use the highway without passing through a toll booth, just like there should be no way to access resources without passing through a PEP.

The Airport Security Checkpoint: TSA policies are set by federal guidelines (PDP). The checkpoint with X-ray machines and agents (PEP) is where those policies are enforced. Every passenger must go through - there’s no way to the gate that bypasses security. If TSA says no liquids over 3oz, the checkpoint is where your water bottle gets confiscated.

Code Example


// Policy Enforcement Point implementation
class PolicyEnforcementPoint {
  constructor(pdp) {
    this.pdp = pdp // Policy Decision Point
    this.obligationHandlers = new Map()
  }

  // Express middleware as PEP
  middleware() {
    return async (req, res, next) => {
      try {
        // Extract authorization context
        const authzRequest = this.buildAuthzRequest(req)

        // Query PDP for decision
        const decision = await this.pdp.authorize(authzRequest)

        if (decision.decision === 'DENY') {
          return res.status(403).json({
            error: 'Forbidden',
            message: decision.reason || 'Access denied'
          })
        }

        if (decision.decision === 'PERMIT') {
          // Enforce obligations before allowing access
          await this.enforceObligations(decision.obligations)

          // Store decision context for downstream handlers
          req.authzContext = {
            decision,
            obligations: decision.obligations,
            advice: decision.advice
          }

          return next()
        }

        // INDETERMINATE or error - fail closed
        return res.status(403).json({
          error: 'Forbidden',
          message: 'Authorization could not be determined'
        })

      } catch (error) {
        // Fail closed on error
        console.error('PEP error:', error)
        return res.status(500).json({
          error: 'Internal Server Error',
          message: 'Authorization check failed'
        })
      }
    }
  }

  buildAuthzRequest(req) {
    return {
      subject: {
        id: req.user?.id,
        attributes: {
          roles: req.user?.roles || [],
          department: req.user?.department,
          ...req.user
        }
      },
      resource: {
        id: req.params.id || req.path,
        type: this.getResourceType(req.path),
        attributes: {
          path: req.path,
          method: req.method
        }
      },
      action: this.getAction(req.method),
      environment: {
        time: new Date(),
        ipAddress: req.ip,
        userAgent: req.headers['user-agent'],
        protocol: req.protocol,
        sourceNetwork: this.getNetworkZone(req.ip)
      }
    }
  }

  getAction(method) {
    const actionMap = {
      'GET': 'READ',
      'POST': 'CREATE',
      'PUT': 'UPDATE',
      'PATCH': 'UPDATE',
      'DELETE': 'DELETE'
    }
    return actionMap[method] || method
  }

  getResourceType(path) {
    // Extract resource type from path
    const match = path.match(/^/api/([^/]+)/)
    return match ? match[1] : 'unknown'
  }

  async enforceObligations(obligations = []) {
    for (const obligation of obligations) {
      const handler = this.obligationHandlers.get(obligation.type)

      if (!handler) {
        throw new Error(`No handler for obligation: ${obligation.type}`)
      }

      await handler(obligation)
    }
  }

  registerObligationHandler(type, handler) {
    this.obligationHandlers.set(type, handler)
  }
}

// Setup PEP with PDP
const pdp = new PolicyDecisionPoint(policyStore)
const pep = new PolicyEnforcementPoint(pdp)

// Register obligation handlers
pep.registerObligationHandler('audit-log', async (obligation) => {
  await auditLog.log({
    action: obligation.action,
    resource: obligation.resource,
    timestamp: new Date()
  })
})

pep.registerObligationHandler('notify-owner', async (obligation) => {
  await notificationService.send({
    to: obligation.ownerId,
    message: `Your resource ${obligation.resourceId} was accessed`
  })
})

pep.registerObligationHandler('require-mfa', async (obligation) => {
  if (!obligation.user.mfaVerified) {
    throw new Error('MFA verification required')
  }
})

// Apply PEP to routes
app.use('/api/documents', pep.middleware())
app.use('/api/sensitive', pep.middleware())

// Alternative: [Resource](https://reference.apios.info/terms/resource/)-specific PEP
class ResourcePEP {
  constructor(pdp, resourceType) {
    this.pdp = pdp
    this.resourceType = resourceType
  }

  async checkAccess(userId, resourceId, action) {
    const decision = await this.pdp.authorize({
      subject: { id: userId },
      resource: {
        id: resourceId,
        type: this.resourceType
      },
      action,
      environment: { time: new Date() }
    })

    if (decision.decision !== 'PERMIT') {
      throw new ForbiddenError(decision.reason)
    }

    return decision
  }
}

// Usage in business logic
const documentPEP = new ResourcePEP(pdp, 'document')

async function deleteDocument(userId, documentId) {
  // PEP check before business logic
  await documentPEP.checkAccess(userId, documentId, 'DELETE')

  // Proceed with deletion
  await db.documents.delete(documentId)
}

Security Notes

SECURITY NOTES

CRITICAL: Policy Enforcement Point (PEP) enforces authorization decisions. Validates every request.

PEP Responsibilities:

  • Request interception: Intercepts requests at boundary
  • Policy request: Calls PDP for authorization decision
  • Enforcement: Allows/denies based on decision
  • Logging: Logs all decisions and enforcement

Implementation:

  • API gateway: Implement PEP in API gateway
  • Middleware: Implement as middleware in application
  • Proxy: Implement as reverse proxy in front of service

Best Practices:

  • Fail closed: Deny if PDP unavailable
  • Performance: Cache decisions locally
  • Logging: Log all enforcement decisions
  • Monitoring: Monitor enforcement failures
  • Testing: Test with various policies

Security:

  • Cannot be bypassed: PEP must be on critical path
  • Verify decisions: Ensure decisions being enforced
  • Error handling: Don’t expose policy details in errors

Standards & RFCs