
AI 에이전트에게 사내 DB와 문서를 열어 주면 보안 담당자는 사고가 난 뒤에 이렇게 묻습니다. 지난달 영업팀의 누가 AI로 고객 테이블을 조회했는가, 그 사람에게 그 테이블을 볼 권한이 있었는가. 권한 밖의 조회는 도구가 실행되기 전에 코드가 막습니다(「권한은 코드로 막고, 판단은 모델에 맡긴다」). 그런데 막았다는 사실도, 막지 않은 조회가 무엇이었는지도 나중에 보일 수 있어야 합니다. 대화 원문을 증거로 쌓으면 관리자가 남의 대화를 읽는 통로가 생기고, 대화가 지워지는 날 증거도 함께 사라집니다. 원천 DB의 로그에는 모든 조회가 연결 계정 하나의 이름으로 남아서, 누가 물었는지 알 수 없습니다.
그래서 감사 기록은 대화가 아니라 AI가 자산에 닿은 호출 하나를 단위로 남깁니다. SQL 한 번, 문서 검색 한 번, 커넥터 도구 호출 한 번이 각각 한 줄이고, 줄마다 누가, 언제, 어느 DB나 커넥터에, 어떤 결과(성공·거부·실패)로 닿았는지가 실립니다. 질문, 답, 조회 결과 값은 싣지 않습니다. 이 글은 무엇을 어떤 모양으로 남기는지, 관리자가 그 기록을 연 일과 권한이 바뀐 일을 어떻게 남기는지, 앱이 그 기록을 고칠 수 없게 한 방법을 다룹니다.
대화 원문과 원천 DB 로그로는 누가 무엇을 조회했는지 답하지 못한다
대화를 통째로 보관하는 방법은 단순하고, 모델이 무엇을 왜 조회했는지까지 남깁니다. 하지만 대화에는 질문한 사람이 쓴 말과 조회 결과 값이 그대로 들어 있습니다. 감사하려고 대화를 열면 관리자는 감사 대상이 아닌 내용까지 읽게 됩니다. 대화의 수명도 감사와 맞지 않습니다. 대화는 마지막 활동 뒤 1년, 사용자가 지운 뒤 30일이 지나면 지워집니다. 감사 기록은 그보다 오래 남아야 합니다.
원천 DB의 감사 기능(PostgreSQL의 pgAudit, Oracle의 통합 감사 등)은 DB가 실제로 실행한 문장을 남긴다는 점에서 믿을 만합니다. 하지만 직접 연결한 DB에는 OwlAI가 조직의 연결 계정 하나로 접속합니다. DB 로그에는 모든 사용자의 조회가 같은 계정으로 남습니다. 대신 DBA가 OwlAI가 보낸 문장을 가려낼 수 있게 표지를 둘 붙입니다.
- 실행기가 고객 DB에 보내는 모든 문장 맨 앞에
/* OwlAI generated */주석이 붙습니다. DBA는pg_stat_statements나V$SQL에서 이 주석으로 AI가 실행한 문장을 찾습니다. - 접속은
owlai-dbexec라는 클라이언트 이름을 밝힙니다. PostgreSQL에서는application_name, Oracle에서는program으로 보여서, 세션 화면에서 OwlAI의 접속을 알아볼 수 있습니다.
표지로는 OwlAI의 문장인지만 압니다. 누가 그 문장을 실행하게 했는지는 OwlAI의 감사 기록이 답합니다. DBA가 표지로 찾은 문장을 같은 시각의 SQL 실행 기록과 맞춰 보면 실행한 사람이 나옵니다.
SQL은 실행마다 한 줄로 남고, 대화와는 값으로만 이어진다
SQL 실행 도구가 불릴 때마다 감사 기록에 한 줄이 남습니다. 한 턴에서 같은 SQL을 두 번 실행해도 두 줄입니다. 한 줄에 실리는 것은 다음과 같습니다.
- 시각과 실행한 사람
- DB 연결
- 결과: 성공, 거부, 실패. 거부는 권한 검사나 읽기 문장 검사가 실행 전에 막은 것이고, 실패는 실행했지만 DB가 오류를 낸 것입니다.
- 돌려받은 행 수(성공일 때만)
- SQL 원문(길면 잘리고, 잘렸다는 표시가 붙습니다)
- SQL에서 읽어 낸 테이블 목록
- 출처: 채팅 또는 어느 플레이북의 실행
받은 행의 값은 남지 않습니다. 기록이 또 하나의 데이터 사본이 되지 않게 하려는 것입니다.
거부를 남기는 것이 이 기록의 핵심입니다. 모델이 권한 없는 DB에 SQL을 보내면 실행 도구는 실행 전에 거절하고, 그 시도는 거부로 한 줄 남습니다. 보안 담당자는 성공한 조회만이 아니라 막힌 시도까지 같은 목록에서 봅니다. 한 사람의 거부가 갑자기 늘었다면 역할 설정이 그 사람의 업무와 맞지 않거나, 누군가 AI로 권한 밖을 조회하려 한다는 신호입니다.
이 줄은 대화, 턴, 플레이북 실행을 값으로만 가리킵니다. 대화 쪽에 외래 키를 걸지 않았기 때문에 대화가 지워져도 줄은 감사 보존 기간을 따라 남습니다. 테이블 목록은 SQL 문을 훑어 뽑은 색인입니다. 원천 DB에 조회를 한 번 더 보내지 않고 기록하려는 선택이라, 뷰나 동의어를 거친 조회는 실제 테이블이 아니라 그 이름으로 남습니다. 무엇이 실행됐는지의 정본은 함께 남는 SQL 원문입니다.
관리자는 활동 기록 화면의 SQL 실행 이력 탭에서 이 기록을 봅니다. 기간, 사용자, DB 연결, 결과로 거르고 SQL 원문과 테이블 이름으로 검색하며, 조건에 맞는 행을 최대 5,000행까지 CSV로 내보냅니다. 질문과 대화 제목은 이 탭에 없어서, 검색은 SQL 원문과 테이블에만 걸립니다.
사용자 신원 없이 도는 작업은 이 기록에 오르지 않습니다. 관리자가 시작한 스키마 분석이 그렇습니다. 분석은 사람의 조회가 아니라 그 DB를 이해하려는 작업이고, 분석을 시작한 일은 누가 어느 DB에 시작했는지가 관리 이벤트로 남습니다.
문서 검색과 커넥터 호출은 무엇에 닿았는지만 남긴다
문서 검색과 커넥터(회사가 OwlAI에 연결한 MCP 서버) 도구 호출도 같은 모양으로 한 줄씩 남습니다. 남는 것과 남지 않는 것은 기록마다 다릅니다.
| 기록 | 남는 것 | 남지 않는 것 |
|---|---|---|
| SQL 실행 | DB 연결, SQL 원문, 테이블, 행 수, 결과 | 조회 결과 값 |
| 문서 검색 | 검색이 모델에게 내준 문서와 그 콜렉션, 권한이 없어 이름만 알려 준 문서의 수 | 검색어, 문서 본문 |
| 커넥터 도구 호출 | 커넥터, 도구 이름, 결과 | 도구 인자, 도구 결과 |
| 도구 승인 결정 | 도구·커넥터 이름, 결정, 대화·턴(사전 승인이면 플레이북) | 도구 인자 |
커넥터 도구의 인자를 남기지 않는 이유는 그 안에 메일 본문, 이슈 댓글, 고객 데이터가 들어 있기 때문입니다. 인자를 남기면 감사 기록에 그 내용이 쌓이고, 감사 기록이 관리자가 남의 메일을 읽는 자리가 됩니다. 커넥터 호출의 거부에는 원격 서버에 닿기 전에 막힌 호출도 들어갑니다. 역할이 그 커넥터를 허용하지 않아 실행 단계에서 막은 호출이 그렇습니다.
승인이 필요한 도구(「확인하라고 일러도 모델은 메일을 보낸다, 실행 직전에 승인을 받는다」)를 사람이 허락하거나 거부한 결정도 한 줄씩 남습니다. 승인 창의 승인·「항상 허용」·거부·만료, 「항상 허용」 기록으로 묻지 않고 승인한 호출, 같은 호출을 거듭 거부해 묻지 않고 막은 호출, 「항상 허용」 해제, 플레이북 사전 승인의 부여와 해제가 대상입니다. 이 결정은 감사 이벤트로 남아 아래 열람 이벤트와 같은 탭에서 보이고, 커넥터 호출과 같은 이유로 도구 인자는 싣지 않습니다.
권한이 바뀐 일은 무엇을 열고 닫았는지로 남는다
거부 기록은 그 시점의 권한을 기준으로 읽어야 합니다. 그래서 권한이 바뀐 일도 남습니다. 관리자가 역할의 DB, 문서 콜렉션, 커넥터 허용을 바꾸면, 그 요청으로 연 대상과 닫은 대상이 목록으로 한 줄에 실립니다. DB 허용이라면 DB 전체인지 고른 테이블인지도 함께 실립니다.
권한은 사람이 아닌 쪽에서도 바뀝니다.
- 역할 권한을 커넥터 단위로 정리한 업그레이드는 예전의 역할별 도구 규칙을 지웠습니다(「커넥터 도구의 위험은 고객사가 안다, 도구 정책은 설치마다 둔다」). 이 업그레이드는 지운 규칙마다 역할, 대상 도구, 지운 판정(허용·승인 필요·차단), 이유를 한 줄씩 남깁니다. 지우는 일과 남기는 일을 SQL 문 하나로 묶었기 때문에, 지우고 기록하지 못하는 경우가 없습니다.
- 커넥터 서버가 도구를 늘리거나 빼면, 역할을 고치지 않아도 AI가 쓸 수 있는 도구가 바뀝니다. 그래서 목록이 실제로 바뀐 때마다 늘고 빠진 도구 이름과 계기(누가 연결하다 받아 왔는지, 정리 작업이 뺐는지)를 남깁니다.
관리자가 남의 기록을 연 일도 남는다
감사 기록을 볼 수 있는 사람은 관리자이고, 관리자 화면에는 남의 활동이 보입니다. 플레이북은 함께 쓰는 자동화라서 실행 상세에 노드의 출력과 파라미터까지 보이고, 답변 피드백에는 사용자가 피드백과 함께 보낸 대화가 딸려 있습니다. 그래서 관리자가 다른 사람의 기록을 여는 일 자체를 열람 이벤트로 남깁니다.
대상은 SQL 실행 목록과 상세, 플레이북 실행 목록과 상세, 사용자 목록, 답변 피드백 목록과 피드백 대화, 공유 링크 목록입니다. 이벤트 한 줄에는 열람한 사람, 시각, 접속한 IP와 브라우저, 연 자원의 종류가 실리고, 단건 열람이면 그 자원의 주인도 실립니다. 본 내용은 실리지 않습니다. 목록 열람은 요청 한 번에 한 줄이고, 돌려준 행 수와 걸어 둔 거르기 조건을 함께 싣습니다. 행마다 한 줄씩 남기면 페이지 하나가 수십 줄이 되어 감사 화면을 읽을 수 없습니다. 자기 기록만 볼 때는 남기지 않습니다.
이 항목들은 「개인정보의 안전성 확보조치 기준」이 접속기록에 요구하는 다섯 항목(식별자, 접속일시, 접속지 정보, 처리한 정보주체 정보, 수행업무)에 맞춰 짰습니다. 구축형에서는 시스템 현황의 시스템 이벤트 탭, OwlAI SaaS에서는 활동 기록의 관리 이벤트 탭에서 열람 유형으로 봅니다.
OwlAI SaaS의 운영자가 고객 조직의 피드백 대화를 열 때는 기준이 더 엄격합니다. 열람은 플랫폼 감사와 그 조직의 활동 기록 양쪽에 남고, 둘 중 하나라도 남기지 못하면 대화 내용을 내주지 않습니다. 조직의 관리자는 운영자가 무엇을 언제 열었는지 자기 화면에서 봅니다.
앱은 감사 기록을 고치거나 지울 수 없다
기록을 남기는 앱이 그 기록을 지울 수 있으면, 앱의 코드나 계정이 하나만 잘못돼도 증거가 사라집니다. 그래서 감사 테이블의 권한을 DB가 정합니다. 앱이 쓰는 DB 역할은 감사 테이블에 INSERT와 SELECT만 받고, UPDATE와 DELETE는 받지 않습니다. 행을 지울 수 있는 것은 보존 작업 전용 역할 하나이고, 그 역할도 RLS 정책 때문에 1,095일(3년)이 지난 행만 지울 수 있습니다. 화면에는 기록을 지우는 기능이 없습니다.
보존 기간은 observability.accessLogRetentionDays로 늘릴 수 있습니다. 기본값은 1095일이고, 그보다 짧게 적으면 서버가 기동할 때 거절합니다. 짧은 값을 적은 설치가 그 값대로 지워진다고 믿고 운영하는 일이 없게, 보존 작업이 처음 돌 때가 아니라 기동할 때 알립니다. 3년은 위의 기준이 접근 권한의 부여·변경·말소 내역에 요구하는 보관 기간과 같습니다. 조직을 해지할 때 감사 기록을 지우지 않고 남기는 정리 규칙은 「조직 경계는 RLS가 지키고, 지울 테이블은 분류표가 정한다」에서 다룹니다.
설계에서 고른 것
대화 원문 대신 호출 단위 기록을 골랐습니다. 대화를 남기면 모델이 왜 그 테이블을 골랐는지까지 볼 수 있습니다. 하지만 그 대가로 감사 기록이 관리자가 남의 대화를 읽는 통로가 되고, 대화를 지워도 사본이 감사 테이블에 남습니다. 호출 단위 기록으로는 "그 메일에 무엇을 썼나"에 답할 수 없습니다. 대신 "누가 어느 DB에 어떤 SQL을 실행했고 무엇이 막혔나"에는 대화가 지워진 뒤에도 답합니다.
테이블 목록은 원천 DB에 묻는 대신 SQL 문에서 읽어 냅니다. DB의 실행 계획이나 의존 정보를 조회하면 뷰 뒤의 실제 테이블까지 알 수 있지만, 감사 한 줄을 쓰려고 고객의 운영 DB에 조회를 하나 더 보내야 합니다. 그래서 목록은 검색용 색인으로 두고, 정본은 SQL 원문으로 정했습니다.
관리자의 열람은 막는 대신 기록합니다. 구축형에서 관리자는 DB 연결과 역할을 직접 만드는 사람이라, 관리자의 열람을 막는 장치는 실효가 없습니다. 막지는 않되 숨을 수도 없게 하는 쪽을 골랐습니다.
보존 기간의 하한은 설정 검사와 DB 정책 두 곳에 둡니다. 설정 검사만 있으면 DB에 직접 붙은 작업이 하한 안의 행을 지울 수 있고, DB 정책만 있으면 하한보다 짧은 설정이 아무 알림 없이 효과를 잃습니다.
여러 사람이 한 연결 계정으로 DB를 조회하는 AI 에이전트를 운영한다면, 원천 DB 로그로는 사람을 가를 수 없습니다. 도구 호출 하나를 단위로 사람·자산·결과를 남기고, 거부까지 같은 목록에 넣고, 그 기록을 연 일까지 남기면 대화 원문 없이도 "권한 밖을 보지 않았다"를 보일 수 있습니다.
어떤 문제를 해결하나요?

인사이트 · 보안과 감사
볼 권한이 없는 데이터를 AI에게 물으면 어떻게 될까
인사 데이터를 볼 수 없는 영업 사원이 AI에게 "올해 연봉 인상률 평균을 알려 줘"라고 묻습니다. AI가 회사 DB 전체에 접속할 수 있다면 사람에게 닫힌 문이 AI를 거쳐 열립니다.

인사이트 · 회사 DB에 묻기
운영 DB에 AI를 붙이자는 말에 보안팀이 묻는 세 가지
업무 DB에 AI를 연결하자는 이야기가 나오면 DB 관리자와 보안팀은 같은 질문 세 개를 꺼냅니다. AI가 실수로 데이터를 지우면 어떻게 하나.

인사이트 · 왜 업무용 AI인가
ChatGPT는 그렇게 똑똑한데, 왜 회사 일에는 못 쓸까
범용 AI 서비스에 "이번 달 우리 지점별 매출이 어떻게 돼?"라고 물으면 답은 금방 나옵니다. 문장은 매끄럽고 표까지 그려 줍니다. 그런데 숫자를 들여다보면 보고서에 옮길 수가 없습니다.

인사이트 · OwlDB와 함께 쓰면
한 부서에 DB를 열어 주면, 보면 안 되는 테이블까지 따라 열릴까
관리자가 영업팀 역할에 업무 DB를 열어 줍니다. 영업 사원이 고객별 주문 실적을 물으면 AI가 그 DB에서 답을 찾아옵니다.
보안과 감사의 다른 글

엔지니어링 노트 · 보안과 감사
외부 모델 대화에는 내부 모델 전용 DB의 이름도 싣지 않는다
고객사 망 안에 띄운 모델과 외부 API 모델을 한 설치에 함께 두면, 쓸 모델을 정해 두는 것만으로는 사내 데이터가 밖으로 나가는 일을 막지 못합니다.

엔지니어링 노트 · 보안과 감사
도구 결과에 나온 주소도 모델이 먼저 썼다면 열지 않는다
웹 페이지를 읽거나 주소에서 파일을 받는 도구는 대화에 먼저 나온 주소만 엽니다(「권한은 코드로 막고, 판단은 모델에 맡긴다」).

엔지니어링 노트 · 보안과 감사
답변에서 지운 전화번호도 모델은 이미 읽었다, 도구 결과에서 가린다
「지난주에 환불을 세 번 넘게 요청한 고객을 찾아 문의 내용을 정리해 줘」라는 질문은 숫자 하나로 끝나지 않습니다.