Definition
Modelo de autorización que toma decisiones de acceso a nivel detallado y granular - controlando acceso no solo a recursos, sino a campos específicos, operaciones o datos basados en condiciones complejas. Va más allá del “puede el usuario acceder a esta API” de grano grueso a “puede este usuario leer este campo específico en este registro específico ahora mismo”.
Example
Usuario puede ver registros de clientes pero solo ver números de tarjeta de crédito enmascarados a menos que tengan rol “pci-compliance” Y la solicitud sea desde red interna Y el log de auditoría esté habilitado. Nivel de campo: mostrar campo “salario” solo a departamento de RRHH o al propio registro del empleado.
Analogía
Como un hospital donde doctores pueden ver todos los registros de pacientes, enfermeras ven la mayoría pero no notas psiquiátricas, personal de facturación solo ve información de seguro, y el paciente puede ver todo sobre sí mismo pero nada sobre otros. El acceso se adapta precisamente al rol, contexto y el ítem de datos específico.
Code Example
// Fine-grained authorization example
class FinegrainedAuthz {
constructor() {
this.policies = new Map()
}
// Field-level authorization
canReadField(user, resource, fieldName) {
const field = resource[fieldName]
// Email: visible to owner or admin
if (fieldName === 'email') {
return user.id === resource.userId || user.roles.includes('admin')
}
// SSN: only for compliance officers from internal network
if (fieldName === 'ssn') {
return (
user.roles.includes('compliance') &&
user.network === 'internal' &&
user.mfaVerified === true
)
}
// Salary: HR or own record
if (fieldName === 'salary') {
return (
user.department === 'hr' ||
user.id === resource.userId
)
}
// Default: public fields
return true
}
// Operation-level authorization
canPerformOperation(user, resource, operation) {
// Delete: only owner or admin, not on locked resources
if (operation === 'delete') {
return (
(user.id === resource.userId || user.roles.includes('admin')) &&
!resource.isLocked
)
}
// Approve: only managers of same department
if (operation === 'approve') {
return (
user.roles.includes('manager') &&
user.department === resource.department
)
}
return false
}
// Filter response based on permissions
filterFields(user, resource) {
const filtered = {}
for (const [key, value] of Object.entries(resource)) {
if (this.canReadField(user, resource, key)) {
filtered[key] = value
} else {
// Return masked or null for unauthorized fields
filtered[key] = this.getMaskedValue(key, value)
}
}
return filtered
}
getMaskedValue(fieldName, value) {
if (fieldName === 'ssn') return '***-**-****'
if (fieldName === 'email') return '***@***.***'
if (fieldName === 'salary') return null
return '[REDACTED]'
}
}
// API endpoint with fine-grained authorization
app.get('/api/employees/:id', async (req, res) => {
const authz = new FinegrainedAuthz()
const user = req.user
const employee = await db.employees.findById(req.params.id)
if (!employee) {
return res.status(404).json({ error: 'Not found' })
}
// Apply fine-grained filtering
const filtered = authz.filterFields(user, employee)
// Log what was filtered for audit
const redactedFields = Object.keys(employee).filter(
key => !(key in filtered) || filtered[key] === null
)
if (redactedFields.length > 0) {
await auditLog.log({
action: 'field_redaction',
user: user.id,
resource: employee.id,
redactedFields
})
}
res.json(filtered)
})
// Conditional field exposure in GraphQL
const resolvers = {
Employee: {
salary: (parent, args, context) => {
const user = context.user
const authz = new FinegrainedAuthz()
if (authz.canReadField(user, parent, 'salary')) {
return parent.salary
}
throw new ForbiddenError('Not authorized to view salary')
}
}
}
Notas de Seguridad
CRÍTICO: La autorización de grano fino es compleja y propensa a errores. Vulnerabilidades comunes: (1) Bypass de autorización vía agregación - …
Configuración:
- usuario no puede ver salarios individuales pero puede ver promedio departamental que los revela, (2) Aplicación inconsistente - verificar en lectura pero olvidar en actualización/eliminación, (3) Filtrado del lado del cliente - nunca filtrar campos sensibles en frontend, siempre del lado del servidor, (4) Fugas de canal lateral - mensajes de error o diferencias de tiempo revelan datos no autorizados, (5) Envenenamiento de caché - respuestas en caché servidas a usuarios no autorizados, (6) Ataques por lotes - combinar múltiples solicitudes para inferir datos protegidos, (7) Degradación de rendimiento - verificaciones complejas en cada campo pueden matar el rendimiento.
- Mejores prácticas: Centralizar lógica de autorización (no dispersar verificaciones por todas partes), probar exhaustivamente con matrices de permisos, implementar a nivel de consulta de base de datos cuando sea posible (seguridad a nivel de fila), auditar todas las decisiones de autorización, usar denegar por defecto (permiso explícito requerido), cachear decisiones de autorización cuidadosamente (TTL corto, específico por usuario), monitorear patrones de acceso inusuales, implementar limitación de tasa, fallar cerrado (denegar acceso en error), nunca exponer existencia de recursos no autorizados.
Mejores Prácticas:
- usuario no puede ver salarios individuales pero puede ver promedio departamental que los revela, (2) Aplicación inconsistente - verificar en lectura pero olvidar en actualización/eliminación, (3) Filtrado del lado del cliente - nunca filtrar campos sensibles en frontend, siempre del lado del servidor, (4) Fugas de canal lateral - mensajes de error o diferencias de tiempo revelan datos no autorizados, (5) Envenenamiento de caché - respuestas en caché servidas a usuarios no autorizados, (6) Ataques por lotes - combinar múltiples solicitudes para inferir datos protegidos, (7) Degradación de rendimiento - verificaciones complejas en cada campo pueden matar el rendimiento.
- Mejores prácticas: Centralizar lógica de autorización (no dispersar verificaciones por todas partes), probar exhaustivamente con matrices de permisos, implementar a nivel de consulta de base de datos cuando sea posible (seguridad a nivel de fila), auditar todas las decisiones de autorización, usar denegar por defecto (permiso explícito requerido), cachear decisiones de autorización cuidadosamente (TTL corto, específico por usuario), monitorear patrones de acceso inusuales, implementar limitación de tasa, fallar cerrado (denegar acceso en error), nunca exponer existencia de recursos no autorizados.