# 설계서: ORDS/VPD 실제 실행 감사 증적 (#567)

> **상태**: Implemented
> **작성**: [AI] Architect · **최종수정**: 2026-07-01
> **추적성** — Redmine: #567 · 관련 이슈: #561
> · 구현: `ProbeResult`, `FgaExecutionEvidence`, `OrdsProbeService`, `OrdsMetadataService`, `probe-result.html`, `22_*`, `29_*`, `42_*`, `43_*`
> · 검증: `ProbeResultTest`, `ProbeResultTemplateRenderTest`, `mvn test`

## 목적

권한 결과 확인에서 보여 주는 SQL을 두 종류로 분리한다.

| 구분 | 출처 | 용도 |
|---|---|---|
| DB 감사 실행 증적 | Oracle FGA 감사 행 | **실제로 실행된 SQL**과 DB가 기록한 VPD `RLS_INFO` 확인 |
| 참고용 SQL 재현 | Handler 응답/동일 정책 함수 | 사람이 권한 규칙과 SELECT 형태를 이해하는 보조 설명 |

재현 SQL이나 정책 함수를 다시 호출해 얻은 predicate를 실제 실행 증적으로 주장하지 않는다. `V$SQL`의 최신 cursor 후보를 찾아 추정하는 방식도 요청별 상관관계가 없어 증적 화면의 기준으로 사용하지 않는다.

## 해결 방식

```text
[권한 결과 확인]
      │ UUID 생성
      ▼
X-VPD-Probe-Id HTTP header
      │
      ▼
[ORDS Handler]
 set_vpd_context(auth, probe_id)
      │                    └─ DBMS_SESSION.SET_IDENTIFIER(probe_id)
      ▼
[VPD 보호 SELECT]
      │
      ├─ DBMS_RLS가 정책 predicate 적용
      ▼
[DBMS_FGA SELECT 감사]
 SQL_TEXT + RLS_INFO + CLIENT_ID 저장
      │
      ▼
[Backoffice]
 UNIFIED_AUDIT_TRAIL(ADB)에서 CLIENT_IDENTIFIER=probe_id로 한 행 조회
 DBA_FGA_AUDIT_TRAIL은 전통 FGA DB fallback
      │
      ▼
DB 감사 실행 증적 화면
```

- `CLIENT_ID`는 백오피스가 매 probe마다 생성한 UUID다. 토큰, 사용자명, 권한 값은 넣지 않는다.
- ORDS connection pool 재사용 시 남는 식별자가 없도록 `set_vpd_context` 시작과 `clear_vpd_context`에서 `DBMS_SESSION.CLEAR_IDENTIFIER`를 호출한다.
- FGA는 `SQL_TEXT`와 VPD 정책명·predicate가 담긴 `RLS_INFO`를 DB 감사 trail에 저장한다. 이 Autonomous DB에서는 `UNIFIED_AUDIT_TRAIL`을 사용하며, 전통 FGA 환경은 `DBA_FGA_AUDIT_TRAIL`을 fallback으로 조회한다.
- FGA 감사 행이 없으면 기존의 권한 결과 성공/0행 상태를 실패로 바꾸지 않는다. 다만 “실제 실행 증적 없음”으로 명확히 표시한다.

## 데이터 계약

### ORDS 요청

```http
X-VPD-Probe-Id: 8db9fc64-c71b-4b9e-9514-2fcaa355d9f3
Authorization: Bearer <masked>
```

`X-VPD-Probe-Id`는 일반 object handler, 기본 문서 handler, vector handler 모두 `:probe_id`로 bind한다.

### 화면 모델

`FgaExecutionEvidence`는 다음 값을 보관한다.

- 감사 시각 `EVENT_TIMESTAMP_UTC` (`EXTENDED_TIMESTAMP` fallback)
- DB 실행 사용자 `DBUSERNAME` (`DB_USER` fallback)
- 요청 식별자 `CLIENT_IDENTIFIER` (`CLIENT_ID` fallback)
- 문장 유형 `ACTION_NAME` (`STATEMENT_TYPE` fallback)
- 실제 SQL `SQL_TEXT`
- 실제 VPD 정보 `RLS_INFO`

`ProbeResult`는 이 값을 별도 필드로 보관한다. 기존 `vpdPredicate`/`effectiveSql`은 “참고용 SQL 재현”에만 사용한다.

## 설치와 운영 절차

1. `ADMIN`으로 [42_agent_ords_fga_execution_audit.sql](../../../sql/adb/42_agent_ords_fga_execution_audit.sql)을 실행해 활성 보호 객체마다 FGA `SELECT` 정책을 만든다.
2. `CB_ORDS`로 [22_agent_ords_security_ords_handler_setup.sql](../../../sql/adb/22_agent_ords_security_ords_handler_setup.sql), [29_agent_ords_vector_search_ords.sql](../../../sql/adb/29_agent_ords_vector_search_ords.sql), [43_agent_ords_probe_id_handler_patch.sql](../../../sql/adb/43_agent_ords_probe_id_handler_patch.sql)을 순서대로 실행한다.
3. 새 권한 결과 확인을 실행한다. 이전 요청에는 `CLIENT_ID`가 없으므로 소급해 매칭하지 않는다.
4. 백오피스 실행 계정이 Autonomous의 `UNIFIED_AUDIT_TRAIL`을 조회할 수 있어야 한다. 전통 FGA 환경은 `DBA_FGA_AUDIT_TRAIL`을 fallback으로 사용한다. 일반 계정이면 감사 조회 권한을 가진 전용 observer 계정을 사용한다.

## 인수조건

- [ ] probe마다 새로운 request ID가 ORDS header에 전달된다.
- [ ] Handler가 request ID를 `CLIENT_IDENTIFIER`로 설정하고 종료 시 제거한다.
- [ ] FGA가 활성 보호 객체의 `SELECT`를 감사한다.
- [ ] 화면은 같은 request ID의 `SQL_TEXT`와 `RLS_INFO`만 실제 실행 증적으로 보여 준다.
- [ ] FGA 행이 없거나 읽기 권한이 없을 때 다른 cursor나 재현 predicate를 실제 값처럼 대신 보여 주지 않는다.
- [ ] 재현 SQL은 참고용으로만 표시되며 실제 증적과 시각적으로 구분된다.
- [ ] Bearer token 원문은 header, 감사 correlation ID, 화면 어느 곳에도 기록하지 않는다.

## 실패 처리

| 상황 | 화면 동작 |
|---|---|
| FGA 정책 미적용 | 권한 결과는 표시하고, FGA 적용 안내를 표시 |
| ORDS Handler가 구버전 | request ID가 DB에 설정되지 않아 해당 요청의 감사 행을 표시하지 않음 |
| 감사 조회 권한 없음 | 권한 결과는 유지하고, audit trail 권한 안내 표시 |
| `RLS_INFO` 비어 있음 | `SQL_TEXT`는 표시하고 audit 설정 확인 안내 표시 |
| 0행 결과 | VPD 차단 결과와 FGA `SELECT` 감사 행을 함께 표시 |

## 리스크와 선택 근거

- VPD policy function 안에서 별도 DML 로그를 남기지 않는다. 정책 함수에는 DB 상태 변경 제약이 있어, 감사 책임을 FGA에 둔다.
- `V$SQL`은 shared pool cursor cache이며 보존·권한·동시성에 따라 해당 요청을 신뢰성 있게 찾을 수 없다. 운영 진단 보조로는 가능하지만 요청 증적의 기준으로는 부적합하다.
- FGA audit trail은 보존 정책을 운영에서 정해야 한다. 장기 감사가 필요하면 Unified Audit 보존·전송 정책을 별도로 설정한다.

## 테스트 계획

- 단위: `ProbeResult`가 FGA 증적과 참고용 재현 SQL을 서로 다른 필드로 유지한다.
- 템플릿: FGA `SQL_TEXT`, `RLS_INFO`, `CLIENT_ID`가 표시되고 V$SQL 후보 목록은 표시되지 않는다.
- 통합: ADMIN/CB_ORDS 설치 후 각 보호 객체에 probe 실행 → `UNIFIED_AUDIT_TRAIL.CLIENT_IDENTIFIER`가 화면 요청 ID와 같은지, `RLS_INFO`에 해당 VPD 정책이 있는지 확인한다.
