PDP (Punto de Decisión de Políticas)

Autorización Security Notes Jan 6, 2025 JAVASCRIPT

Definition

Cada vez que intentas acceder a algo protegido - un archivo, un endpoint de API, un registro de base de datos - alguien necesita responder la pregunta: “¿Debería permitirse a este usuario hacer esta acción en este recurso ahora mismo?” Ese tomador de decisiones es el Policy Decision Point (PDP). Es el cerebro de tu sistema de autorización, el componente que realmente piensa a través de las reglas y dice sí o no.

El PDP recibe todo el contexto que necesita para tomar una decisión: quién está pidiendo (el usuario y sus atributos como rol, departamento, nivel de autorización), qué están intentando hacer (leer, escribir, eliminar), qué están intentando acceder (y sus atributos como sensibilidad, propietario, clasificación), y factores ambientales (hora del día, dirección IP, tipo de dispositivo). Luego consulta políticas - las reglas que tu organización ha definido - y determina si esta solicitud específica debería ser permitida.

Lo que hace poderosos a los PDPs es que centralizan la lógica de autorización. En lugar de esparcir comprobaciones de permisos por todo tu código (“if user.role == ‘admin’…” en cien lugares), tienes un componente que encarna todas tus reglas de acceso. Esto significa que cambiar una política actualiza el enforcement en todas partes instantáneamente. También significa que tus políticas pueden ser auditadas, probadas, y evolucionadas independientemente de tu código de aplicación. Los PDPs modernos soportan reglas sofisticadas como “los ingenieros pueden leer documentos de su propio equipo durante horas laborales” o “el personal médico puede acceder a historiales de pacientes solo cuando tienen una relación de cuidado activa.”

Example

Los PDPs son los tomadores de decisiones invisibles detrás del control de acceso sofisticado:

Sistemas de Salud: Cuando un médico intenta ver los historiales de un paciente, el PDP evalúa: ¿Este médico está tratando actualmente a este paciente? ¿El médico está en el departamento apropiado? ¿Es durante horas normales de atención o una emergencia? ¿Hay restricciones solicitadas por el paciente? El PDP podría permitir acceso a resultados de pruebas recientes pero bloquear acceso a notas psiquiátricas basándose en diferentes reglas de política.

Plataformas de Trading Financiero: Un trader intenta ejecutar una orden grande. El PDP comprueba: ¿Está esto dentro de los límites de trading del trader? ¿Este valor está en su lista aprobada? ¿El equipo de compliance ha marcado algún problema? ¿El mercado está abierto actualmente? ¿Cuál es su exposición al riesgo? Una sola decisión involucra docenas de reglas de política evaluadas en milisegundos.

Infraestructura Cloud (AWS IAM): Cuando tu código intenta leer de un bucket S3, el PDP de AWS evalúa tus políticas IAM, políticas de recursos, políticas de organización, y políticas de sesión. Decide si tu principal específico, realizando esta acción específica, en este recurso específico, con estas condiciones específicas, debería ser permitido.

Sistemas de Gestión Documental: Un empleado intenta descargar un documento. El PDP comprueba: ¿Su rol permite descargas? ¿El documento está clasificado apropiadamente para su nivel de autorización? ¿Están en un dispositivo gestionado? ¿Su sesión es reciente o deberían re-autenticarse? ¿Hay retenciones legales que previenen la descarga?

API Gateways: Cada llamada API golpea el PDP del gateway: ¿Esta API key es válida para este endpoint? ¿El llamador ha excedido su rate limit? ¿Su tier de suscripción incluye esta característica? ¿Están llamando desde un rango IP permitido?

Analogía

El Juez en el Tribunal: Cuando un caso llega al tribunal, el juez (PDP) no hace arrestos ni guarda las salidas - eso es la policía (PEP). El juez examina la evidencia (atributos), consulta la ley (políticas), considera precedentes, y emite un veredicto (permitir/denegar). El rol del juez es puramente tomar decisiones, basándose en reglas que no creó pero que es experto en interpretar.

El Controlador de Tráfico Aéreo: Cuando un avión solicita aterrizar, el controlador (PDP) no guía físicamente el avión - toma decisiones. Considera: disponibilidad de pista, clima, otras aeronaves, situaciones de combustible, emergencias. Basándose en todos estos factores y reglas de la FAA, decide: autorizado para aterrizar, patrón de espera, o desviar. La decisión está separada del enforcement.

El Oficial de Préstamos: Cuando solicitas una hipoteca, el oficial de préstamos (PDP) recopila tu información (ingresos, puntuación crediticia, ratio de deuda, historial laboral) y la evalúa contra las políticas de préstamo del banco. No escribe las políticas ni desembolsa los fondos - toma la decisión de aprobación basándose en reglas y datos.

El Oficial de Inmigración con el Reglamento: En el control fronterizo, el oficial consulta regulaciones sobre requisitos de visa, restricciones de viaje, y condiciones de entrada. Examina tu pasaporte, visa, propósito, duración, y destino, luego toma una decisión basada en la ley de inmigración. Los sistemas informáticos ayudándoles son esencialmente PDPs - recopilando atributos y comprobando políticas.

Code Example


// Policy Decision Point implementation
class PolicyDecisionPoint {
  constructor(policyStore) {
    this.policyStore = policyStore
    this.cache = new Map()
  }

  // Main authorization decision method
  async authorize(request) {
    const { subject, resource, action, environment } = request

    // Check cache first (with TTL)
    const cacheKey = this.getCacheKey(request)
    const cached = this.cache.get(cacheKey)
    if (cached && cached.expiry > Date.now()) {
      return cached.decision
    }

    try {
      // Retrieve applicable policies
      const policies = await this.policyStore.getPolicies({
        resourceType: resource.type,
        action: action
      })

      // Evaluate policies in order of priority
      const decision = this.evaluatePolicies(
        policies,
        subject,
        resource,
        action,
        environment
      )

      // Cache decision (short TTL for security)
      this.cache.set(cacheKey, {
        decision,
        expiry: Date.now() + 60000 // 1 minute
      })

      // Audit the decision
      await this.auditDecision(request, decision)

      return decision
    } catch (error) {
      // Fail closed - deny on error
      return {
        decision: 'DENY',
        reason: 'Policy evaluation error',
        error: error.message
      }
    }
  }

  evaluatePolicies(policies, subject, resource, action, environment) {
    let finalDecision = 'DENY' // Default deny
    let obligations = []
    let advice = []

    for (const policy of policies) {
      const result = this.evaluatePolicy(
        policy,
        subject,
        resource,
        action,
        environment
      )

      if (result.effect === 'DENY') {
        // Any deny = final deny (deny overrides)
        return {
          decision: 'DENY',
          reason: result.reason,
          matchedPolicy: policy.id
        }
      }

      if (result.effect === 'PERMIT') {
        finalDecision = 'PERMIT'
        obligations = obligations.concat(result.obligations || [])
        advice = advice.concat(result.advice || [])
      }
    }

    return {
      decision: finalDecision,
      obligations, // Must be enforced (e.g., log access)
      advice,      // Should be enforced (e.g., notify owner)
      evaluatedPolicies: policies.map(p => p.id)
    }
  }

  evaluatePolicy(policy, subject, resource, action, environment) {
    // Check if policy target matches
    if (!this.matchesTarget(policy.target, subject, resource, action)) {
      return { effect: 'NOT_APPLICABLE' }
    }

    // Evaluate condition
    const conditionMet = this.evaluateCondition(
      policy.condition,
      subject,
      resource,
      action,
      environment
    )

    if (!conditionMet) {
      return { effect: 'NOT_APPLICABLE' }
    }

    return {
      effect: policy.effect, // PERMIT or DENY
      reason: policy.description,
      obligations: policy.obligations,
      advice: policy.advice
    }
  }

  evaluateCondition(condition, subject, resource, action, environment) {
    // Example: evaluate complex condition expression
    // "subject.department === resource.department AND environment.time.hour < 17"

    try {
      // Use safe expression evaluator (NOT eval()!)
      const context = {
        subject: subject.attributes,
        resource: resource.attributes,
        action: action,
        environment: environment
      }

      return this.safeEvaluate(condition, context)
    } catch (error) {
      // Fail closed on evaluation error
      return false
    }
  }

  getCacheKey(request) {
    return JSON.stringify({
      subject: request.subject.id,
      resource: request.resource.id,
      action: request.action
    })
  }

  async auditDecision(request, decision) {
    await auditLog.log({
      timestamp: new Date().toISOString(),
      type: 'authorization_decision',
      subject: request.subject.id,
      resource: request.resource.id,
      action: request.action,
      decision: decision.decision,
      reason: decision.reason,
      policies: decision.evaluatedPolicies
    })
  }
}

// Using PDP in API
const pdp = new PolicyDecisionPoint(policyStore)

app.delete('/api/documents/:id', async (req, res) => {
  const decision = await pdp.authorize({
    subject: {
      id: req.user.id,
      attributes: req.user
    },
    resource: {
      id: req.params.id,
      type: 'document',
      attributes: await getDocumentAttributes(req.params.id)
    },
    action: 'DELETE',
    environment: {
      time: new Date(),
      ipAddress: req.ip,
      userAgent: req.headers['user-agent']
    }
  })

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

  // Enforce obligations
  for (const obligation of decision.obligations || []) {
    await enforceObligation(obligation)
  }

  // Proceed with delete
  await deleteDocument(req.params.id)
  res.status(204).send()
})

Notas de Seguridad

SECURITY NOTES

CRÍTICO: PDP es el tomador de decisiones de seguridad central. Vulnerabilidades: (1) Bypass de políticas - …

Configuración:

  • si PDP puede ser eludido, toda la autorización falla, (2) Inyección de políticas - nunca permitir entrada de usuario en expresiones de política sin sanitización, (3) Envenenamiento de caché - decisiones en caché para usuario/contexto incorrecto, (4) Divulgación de información - mensajes de error revelan detalles de políticas, (5) DoS de rendimiento - políticas complejas pueden ser explotadas para sobrecargar PDP, (6) Decisiones inconsistentes - condiciones de carrera cuando políticas cambian a mitad de solicitud.
  • Mejores prácticas: Implementar PDP como fuente única de verdad para decisiones de autorización, nunca duplicar lógica de autorización en múltiples lugares, fallar cerrado (denegar en error/timeout), cachear cuidadosamente con claves específicas por usuario y TTL corto, validar todos los atributos de entrada, usar evaluadores de expresiones seguras (nunca eval()), implementar circuit breakers y timeouts, auditar todas las decisiones con contexto completo, monitorear patrones de denegación inusuales, probar conflictos de políticas y casos extremos, versionar políticas y soportar despliegue gradual, separar PDP de PEP (aplicación) para seguridad y escalabilidad.

Mejores Prácticas:

    • si PDP puede ser eludido, toda la autorización falla, (2) Inyección de políticas - nunca permitir entrada de usuario en expresiones de política sin sanitización, (3) Envenenamiento de caché - decisiones en caché para usuario/contexto incorrecto, (4) Divulgación de información - mensajes de error revelan detalles de políticas, (5) DoS de rendimiento - políticas complejas pueden ser explotadas para sobrecargar PDP, (6) Decisiones inconsistentes - condiciones de carrera cuando políticas cambian a mitad de solicitud.
    • Mejores prácticas: Implementar PDP como fuente única de verdad para decisiones de autorización, nunca duplicar lógica de autorización en múltiples lugares, fallar cerrado (denegar en error/timeout), cachear cuidadosamente con claves específicas por usuario y TTL corto, validar todos los atributos de entrada, usar evaluadores de expresiones seguras (nunca eval()), implementar circuit breakers y timeouts, auditar todas las decisiones con contexto completo, monitorear patrones de denegación inusuales, probar conflictos de políticas y casos extremos, versionar políticas y soportar despliegue gradual, separar PDP de PEP (aplicación) para seguridad y escalabilidad.