정책 기반 접근 제어(Policy-Based Access Control, PBAC)

중앙에서 정의된 정책들을 기반으로 접근 권한을 결정하는 접근 제어 방식.
각 정책은 “누가”, “무엇을”, “어떤 조건에서” 할 수 있는지를 정의하며, 이러한 정책들은 프로그래밍 방식으로 표현되고 평가된다.

현대적인 클라우드 환경이나 마이크로서비스 아키텍처에서 특히 유용하다.
AWS IAM, Azure RBAC 등의 클라우드 서비스들이 PBAC를 구현한 대표적인 예시.

작동 방식:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
class Policy {
    constructor(name, conditions, effect) {
        this.name = name;
        this.conditions = conditions;
        this.effect = effect; // 'allow' 또는 'deny'
    }

    evaluate(context) {
        try {
            // 모든 조건을 평가
            return this.conditions.every(condition => condition(context));
        } catch (error) {
            console.error(`Policy evaluation error: ${error.message}`);
            return false;
        }
    }
}

class PolicyEngine {
    constructor() {
        this.policies = new Map();
    }

    addPolicy(policy) {
        this.policies.set(policy.name, policy);
    }

    evaluateAccess(context) {
        let finalDecision = false;

        for (const policy of this.policies.values()) {
            const matches = policy.evaluate(context);
            
            if (matches) {
                finalDecision = policy.effect === 'allow';
                // 명시적인 거부 정책이 있으면 즉시 거부
                if (policy.effect === 'deny') {
                    return false;
                }
            }
        }

        return finalDecision;
    }
}

// 정책 조건 예시들
const conditions = {
    isWorkingHours: (context) => {
        const hour = context.time.getHours();
        return hour >= 9 && hour < 18;
    },

    isInternalNetwork: (context) => {
        return context.ipAddress.startsWith('192.168.');
    },

    hasRole: (role) => (context) => {
        return context.user.roles.includes(role);
    },

    hasPermission: (permission) => (context) => {
        return context.user.permissions.includes(permission);
    }
};

// 정책 엔진 사용 예시
const policyEngine = new PolicyEngine();

// HR 문서 접근 정책
const hrDocumentPolicy = new Policy(
    'HR_Document_Access',
    [
        conditions.isWorkingHours,
        conditions.isInternalNetwork,
        conditions.hasRole('HR'),
        conditions.hasPermission('read_hr_documents')
    ],
    'allow'
);

// 주말 접근 제한 정책
const weekendRestrictionPolicy = new Policy(
    'Weekend_Restriction',
    [
        (context) => {
            const day = context.time.getDay();
            return day === 0 || day === 6;
        }
    ],
    'deny'
);

policyEngine.addPolicy(hrDocumentPolicy);
policyEngine.addPolicy(weekendRestrictionPolicy);

// 접근 시도 예시
const accessContext = {
    user: {
        name: 'Alice',
        roles: ['HR'],
        permissions: ['read_hr_documents']
    },
    time: new Date('2024-12-17T14:00:00'), // 평일 오후 2시
    ipAddress: '192.168.1.100',
    resource: 'employee_records'
};

const hasAccess = policyEngine.evaluateAccess(accessContext);
console.log(`Access granted: ${hasAccess}`);

주요 특징

  1. 유연성: 다양한 조건과 규칙을 조합하여 세밀한 접근 제어가 가능하다.
  2. 중앙 집중식 관리: 정책을 중앙에서 관리하여 일관성을 유지하고 관리를 용이하게 한다.
  3. 컨텍스트 인식: 사용자 신원, 리소스 특성, 시간, 위치 등 다양한 컨텍스트 정보를 고려한다.
  4. 동적 평가: 접근 요청 시 실시간으로 정책을 평가하여 결정을 내린다.

장점

  1. 세밀한 접근 제어: 복잡한 비즈니스 규칙과 요구사항을 정책에 반영할 수 있다.
  2. 변화에 대한 빠른 대응: 정책 변경만으로 접근 제어 로직을 신속하게 수정할 수 있다.
  3. 일관성 유지: 중앙에서 관리되는 정책으로 전체 시스템의 일관성을 보장한다.

예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class AdvancedPolicyEngine {
    constructor() {
        this.policies = new Map();
        this.auditLog = [];
    }

    addPolicy(policy) {
        this.policies.set(policy.name, policy);
    }

    async evaluateAccess(context) {
        const decisions = [];
        const startTime = Date.now();

        try {
            for (const policy of this.policies.values()) {
                const decision = {
                    policyName: policy.name,
                    effect: policy.effect,
                    matches: await policy.evaluate(context),
                    timestamp: new Date()
                };

                decisions.push(decision);

                if (decision.matches && policy.effect === 'deny') {
                    this.logDecision(context, decisions, 'denied');
                    return false;
                }
            }

            const finalDecision = decisions.some(d => d.matches && d.effect === 'allow');
            this.logDecision(context, decisions, finalDecision ? 'allowed' : 'denied');
            
            return finalDecision;

        } catch (error) {
            this.logError(context, error);
            throw error;
        }
    }

    logDecision(context, decisions, result) {
        const logEntry = {
            timestamp: new Date(),
            user: context.user.name,
            resource: context.resource,
            action: context.action,
            decisions: decisions,
            finalResult: result,
            contextSnapshot: { context }
        };

        this.auditLog.push(logEntry);
    }

    logError(context, error) {
        const errorEntry = {
            timestamp: new Date(),
            type: 'error',
            user: context.user.name,
            error: error.message,
            stack: error.stack,
            context: { context }
        };

        this.auditLog.push(errorEntry);
    }

    getAuditLog(filters = {}) {
        return this.auditLog.filter(entry => {
            return Object.entries(filters).every(([key, value]) => 
                entry[key] === value
            );
        });
    }
}

참고 및 출처