모니터링 화면에는 회원 수와 최근 가입 시점이 필요했습니다. 그 화면을 만드는 SQL에는 이메일이나 인증 토큰이 필요 없었지만, 모니터링 계정의 데이터베이스 전체 조회 권한으로는 그런 값도 읽을 수 있었습니다. 화면 쿼리에서 열을 빼는 것과 계정이 그 열을 읽을 수 없는 것은 다른 문제였습니다.
전체 조회 권한을 열 단위로 축소
MySQL은 계정에 부여된 조회 권한을 합칩니다. 데이터베이스 전체에 SELECT가 남아 있는 상태에서 특정 열만 다시 GRANT하면, 이전 권한으로 여전히 모든 열을 읽을 수 있습니다. 먼저 넓은 권한을 회수한 뒤, 모니터링에 필요한 테이블과 열을 다시 부여했습니다. 회원 테이블에서는 9개 열만 열었습니다. MySQL의 열 단위 GRANT 문서
대시보드 쿼리도 필요한 열을 명시하도록 맞췄습니다. 허용된 열만 조회할 수 있는 계정에서 SELECT *는 보호된 열을 함께 요청합니다.
같은 계정으로 허용·거부 검증
테스트 DB에서 모니터링 계정으로 직접 조회했습니다. 회원 수와 최근 회원 목록에 필요한 쿼리는 실행됐습니다. 이어 읽히면 안 되는 쿼리 세 건을 확인했습니다.
| 조회 | 관측한 결과 |
|---|---|
| 회원 이메일 열 | 열 접근 거부 1143 |
| 회원 테이블 전체 열 | 접근 거부 1142 |
| 인증 토큰 테이블 | 접근 거부 1142 |
Grafana에서도 같은 읽기 권한으로 확인
운영에서 넓은 권한을 회수하고 다시 부여한 뒤, 두 모니터링 계정의 실제 권한을 SHOW GRANTS로 확인했습니다. 보호된 회원 열과 인증 테이블은 운영 계정으로도 조회가 거부됐습니다. Grafana 데이터 소스는 권한을 줄인 계정으로 운영 현황 패널 11개, 채점 품질 패널 9개와 대시보드 변수 3개의 쿼리를 실행했습니다. Grafana가 선택한 시간 범위를 SQL 조건으로 바꾸는 쿼리도 이 확인에 포함됐습니다. 당시 데이터가 없어 화면의 값은 0 또는 ‘데이터 없음’이었고, 패널에 쿼리 오류가 표시되지 않았습니다.
이렇게 확인하면 두 가지 실수를 따로 잡을 수 있습니다. 대시보드가 여전히 필요한 데이터를 요청할 수 있는지 보고, 화면에서 쓰지 않는 민감한 열은 같은 계정으로 직접 요청해도 읽히지 않는지 봅니다.

댓글
댓글과 반응은 GitHub 계정으로 로그인해 남길 수 있어요.