데이터베이스를 지키는 여러가지 방법: 누비랩은 모두가 admin 이었다고?

Seongho.Park
Nuvilab 누비랩
Published in
22 min readMay 31, 2023

목차

  1. 들어가며
  2. 데이터베이스 보안
  3. 유저 생성과 권한 부여
    - 유저 생성
    - 패스워드 만료 정책
    - 패스워드 재사용 정책
    - 패스워드 인증 플러그인 사용하기
    - 유저 권한 부여
  4. 더 나은 방법은? 역할 생성
    - 역할 생성하기
    - 역할에 권한 부여하기
    - 개인적으로 제안하는 역할과 권한
    - 역할 권한 생성시 유의사항
    - 역할의 권한 조회하기
    - 권한 삭제하기
    - 역할과 관련된 문제점 관리하기
  5. 예상치 못한 문제 : 저장 프로시저, 함수
  6. 마치며

들어가며

안녕하세요! 누비랩에 Product Tech팀에서 프론트엔드&백엔드 개발을 하고 있는 박성호 입니다.

누비랩에서 나름의 DBA 역할로 데이터베이스를 관리하고 있습니다.

제가 누비랩에 처음 합류하기 시작하고부터 누비랩에 데이터베이스가 관리 측면에서 어떻게 변화하고 있는지, 그리고 데이터베이스를 어떻게 안전하게 지키고 사용자 접근 제어를 할 수 있는지 소개해드리려고 합니다.

데이터베이스에 접근하는 사람이 많아질수록 더욱 철저한 관리가 필요해지는 시점이 존재합니다. 누비랩에서도 기획자, 개발자 등이 직접 데이터베이스에 접근에서 데이터를 뽑아내고, 새롭게 기획하여 문제를 해결하고, 데이터를 기반하여 의사결정이 이루어집니다.

그만큼 많은 구성원이 데이터베이스에 접근할 수밖에 없는 상황입니다. 이때, 과거 누비랩은 모두가 관리자계정 하나로 데이터베이스에 접근했었습니다. 하지만 인원이 증가하고, 구성원이 퇴사하는 과정에서 데이터베이스를 지키고 보호해야 하는 의무가 생겼고, 어떤 체계적인 방법으로 데이터베이스의 보안과 구성원의 퍼포먼스를 해치지 않게 세팅할 수 있을까? 고민했던 것 같습니다.

같이 어떤 방법으로 해결했는지 살펴보시죠.

데이터베이스 보안

데이터베이스 보안 가이드, 일반적인 세팅

우선 데이터베이스를 보호하는 일반적인 방법에 대해서 알아보겠습니다.

큰 틀에서는 다음과 같은 방법으로 데이터베이스를 보호합니다.

  1. 보안 프로토콜 사용
  2. 방화벽 및 인바운드 규칙 설정, TCP/UDP 포트 변경
  3. 허용된 VPC 또는 VPN 내부에서만 접속
  4. 사용자 접근 제어
  5. 데이터 암호화 또는 해싱
  6. 지속적인 감사 및 모니터링, 로깅, 백업

위 모든 과정을 충분하게 해낸다고 해도 100% 방어할 수 없는 것이 보안분야 인것 같습니다. 하지만 충분한 방어와 지속적인 관리로 데이터베이스를 지킬 수 있습니다.

우선 보안 프로토콜은 SSL과 같은 암호화된 계층에서 통신이 이루어지는 것 입니다. 데이터베이스 올리는 클라우드가 종단간 암호화가 되어있는지 또 암호화 되어 전송되는지 확인해야합니다. 이 방법으로 중간에 탈취, 유실시 발생할 수 있는 문제점을 해결할 수 있습니다.

또한 MySQL은 3306, PostgreSQL은 5432 포트를 기본적으로 사용합니다. 비표준 포트를 사용하면, 표준 포트를 사용하는 무차별적인 스크립트 공격을 예방할 수 있습니다.

엔드포인트와 포트 접속 계정이 유출되었다 하더라도 인증된 VPC 또는 VPN 내부에서 접속 가능하도록 설정한다면 해커는 쉽게 데이터베이스에 접근할 수 없습니다.

그리고, 데이터를 암호화 또는 해싱하여 저장하는 방법으로 데이터베이스의 보안을 향상시킬 수 있습니다. 가령 주민등록번호, 계좌정보, 여권본호 등과 같은 개인정보는 국가에서 특정 대칭키 알고리즘 또는 단방향 알고리즘을 사용하도록 권장하고 있습니다. 자세한 정보는 한국인터넷진흥원과 개인정보보호위원회에서 제공한 “개인정보의 암호화 조치 안내서” 2020년 개정판을 살펴보는 것이 좋습니다.

데이터베이스를 보호하는 것 만큼 중요한 것이 모니터링하고 백업인 것 같습니다. 최근에는 프로메테우스나 InfluxDB 같은 시계열 데이터베이스를 활용하여 데이터베이스 상태를 로깅하고 모니터링 하는 추세입니다. 또한 물리적으로 분리된 컴퓨팅 환경에 데이터베이스 스냅샷을 지속적으로 남기는 백업 작업으로 데이터 손실을 최소화 할 수 있습니다.

마지막으로, 접속 계정별로 권한에 따라 접근할 수 있는 데이터베이스(또는 스키마)와 테이블을 제한하는 방법이 있습니다. 이 방법을 통해 데이터베이스에 접속해야하는 많은 구성원 관리를 어떻게 했는지 아래에서 같이 알아봅시다.

유저 생성과 권한 부여

보안 방법 중 유저 및 권한 부여, 기대효과

누비랩에서는 많은 내부 구성원이 문제 해결을 위해 또는 의사결정을 위해 데이터를 보아야 하는 상황이 필연적으로 다가왔습니다. “사용자 접근 제어 측면”에서 누비랩은 어떤 방법으로 문제를 해결해 나갔는지 같이 살펴보겠습니다.

우선 모두가 admin 계정을 사용하고 있다는 큰 문제가 있었습니다.

이는 특정 쿼리가 시스템의 부하를 일으켰을 때 어떤 사용자가 해당 쿼리를 작성했는지 알 수 없어 수정 요청을 드릴 수 없었습니다. 실제로 과거 데이터베이스의 CPU가 100% 치닫았던 때가 있었는데요. 쿼리를 보며 누구의 쿼리인지 유추해야만 했었습니다.

또한 성장기에 있던 누비랩이 학생 인턴이 많았던 시절, 퇴사 과정에서 아이디 비밀번호 유출의 우려가 있었고, 루트 권한으로 회사의 모든 데이터를 유출 할 수 있는 심각한 문제였습니다.

이러한 문제를 해결하고자 “사용자 접근 제어“를 통해서 데이터베이스 보안과 내부 직원의 퍼포먼스를 해치지 않도록 노력했습니다.

우선 내부 직원이 데이터베이스를 접근하기 위한 계정을 모두 생성해드렸습니다. 해당 직원이 데이터베이스 내에서 적절한 쿼리를 할 수 있는 권한을 부여하였고, 특정 호스트를 계정에 붙임으로 써, 회사 밖 다른 네트워크에서의 접속을 제한할 수 있었습니다. (* 물론 방화벽으로 네트워크를 차단할 수 있지만 아래와 같은 방법으로 데이터베이스에서 계정 접속을 막을 수 있습니다.)

구체적으로 MySQL 언어와 함께 알아볼까요?

유저 생성

CREATE USER IF NOT EXISTS '유저이름'@'호스트' IDENTIFIED BY '비밀번호';

호스트(host)에는 이후에 나올 역할과 마찬가지로 아래의 세가지 옵션이 들어갈 수 있습니다.

  • localhost : 로컬호스트로만 계정 접속가능
  • % : 모든 IP에서 접속가능
  • Ip address : 특정 IP에서만 접속 가능

유저를 생성했다면 어떤 유저들이 존재하는지 확인할 수 있어야 겠죠. mysql.user 테이블에서 확인할 수 있습니다.

SELECT * from mysql.user;

계정을 만들고 비밀번호를 만료시키는 정책을 쓰면 데이터베이스를 사용하는 사용자로 하여금 비밀번호를 변경시킬 수 있습니다.

패스워드 만료 정책

-- default_password_lifetime 조회하기
show global variables like 'default_password_lifetime';

-- default_password_lifetime 설정하기
set global default_password_lifetime = 180;

-- 만료되지 않도록 설정하기
set global default_password_lifetime = 0;

위의 방법으로 설정된 시스템 변수는 기존에 생성된 계정에서는 적용되지 않는 문제가 있습니다. 기존 생성된 계정에서도 특정 일 수가 지나면 패스워드가 만료되도록 설정하려면, 애초에 처음 생성할 때 expire interval 옵션을 넣어주거나, alter user로 변경해주어야 합니다.

-- 생성 시, Expire interval 설정
CREATE USER 'some.user'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY;

-- alter user로 Expire interval 설정
ALTER USER 'some.user'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY;

-- 사용자 생성 시, 만료되지 않도록 변경
CREATE USER 'some.user'@'localhost' PASSWORD EXPIRE NEVER;

-- 만료되지 않도록 변경
ALTER USER 'some.user'@'localhost' PASSWORD EXPIRE NEVER;

-- 설정이 되었는지 확인
SELECT host, user, password_expired, password_lifetime FROM mysql.user;

패스워드 재사용 정책

또한 동일한 비밀번호를 사용할 수 없도록 강제하는 패스워드 재사용 정책을 세울 수 있습니다. 패스워드 재사용 정책에는 몇번을 변경했을 때 패스워드를 재사용 가능한지를 나타내는 password_history 얼만큼의 시간이 경과됐을 때 패스워드를 다시 재사용 가능한지를 나타내는 password_reuse_interval 옵션이 있습니다.

-- 시스템 변수 조회
SHOW VARIABLES LIKE 'password_history';
SHOW VARIABLES LIKE 'password_reuse_interval';

-- set global variables
SET GLOBAL password_history = 2;
SET GLOBAL password_reuse_interval = 365;

-- 유저에게 적용 되었는지 확인
SELECT host, user, password_reuse_time, password_reuse_history FROM mysql.user;

위에서 시스템 변수를 변경했지만 기존에 존재했던 유저들은 변경되지 않음을 볼 수 있습니다. 기존에 있던 모든 유저들을 적용시키려면 마찬가지로 ALTER USER 문을 사용해야합니다.

-- 생성시에 유저 설정
CREATE USER 'some.user'@'localhost' PASSWORD HISTORY 5 REUSE INTERVAL 365 DAY;

-- 기존에 있던 유저들 변경
ALTER USER 'some.user'@'localhost' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY;

-- 생성, 변경시 global 설정에 따르도록 변경
CREATE USER 'some.user'@'localhost' PASSWORD HISTORY DEFAULT REUSE INTERVAL DEFAULT;
ALTER USER 'some.user'@'localhost' PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT;

패스워드 인증 플러그인 사용하기

SHA2 Password로 인증 플러그인과 비밀번호를 사용하도록 설정할 수 있습니다.

ALTER USER 'some.user'@'host' IDENTIFIED WITH caching_sha2_password BY 'new_password';

위의 모든 정책을 종합해서 아래의 옵션을 사용하게 되면, 유저는 180일 마다 새 비밀번호를 선택해야하고, 로그인 실패 추척하여 5개의 연속적인 비빌번호 오류가 발생하게 되면 2일동안 임시로 계정 잠금이 발생하도록 합니다.

ALTER USER 'some.user'@'host'
IDENTIFIED WITH caching_sha2_password BY 'new_password'
PASSWORD EXPIRE INTERVAL 180 DAY
FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 2
PASSWORD HISTORY 3;

유저 권한 부여

유저를 생성하였다면 유저에게 특정 쿼리문을 실행할 수 있는 권한을 부여해보겠습니다.

-- Format
GRANT
`privilege`
ON [object_type] priv_level
TO user_or_role [, user_or_role] ...
[WITH GRANT OPTION]
[AS user
[WITH ROLE DEFAULT
| NONE
| ALL
| ALL EXCEPT role [, role ] ...
| role [, role ] ...
]
]

-- Example
-- GRANT 권한종류 ON 대상 TO 계정명
GRANT ALL ON app_db.* TO `admin`@`10.0.0.1`;
GRANT SELECT ON app_db.* TO `reader`@`10.0.0.1`;
GRANT INSERT, UPDATE, DELETE ON app_db.* TO `developer`@`10.0.0.1`;
FLUSH PRIVILEGES;

GRANT문으로 계정에게 권한을 부여할 수 있습니다. 위에 예제를 살펴보면 admin 계정에는 app_db 데이터베이스(또는 스키마)의 하위 폴더를 접근하는 모든 권한을 부여하였고, reader 유저에는 테이블의 레코드 조회만 할 수 있는 권한을 developer 에는 테이블의 레코드를 입력, 수정, 삭제 할 수 있는 권한을 주었습니다.

반대로 REVOKE 문을 사용하면 부여된 권한을 취소할 수 있습니다.

revoke select on app_db.* from `reader`@`10.0.0.1`;

이렇게 유저를 생성하여 권한을 부여하고 권한 부여를 취소하는 방법으로 데이터베이스를 접근 제어할 수 있습니다. 또한 특정 데이터베이스(스키마) 만을 접근 할 수 있도록 강제함으로 써 보안을 높일 수 있었습니다.

누비랩에서는 이렇게 모두가 고객의 개인정보를 볼 수 있는 것이 아닌, 특정 권한을 갖은 관리자만 권한 내의 정보를 확인, 수정, 관리 할 수 있도록 설계했습니다.

더 나은 방법은? 역할 생성

역할을 유저에게 계승. 장점 및 단점과 기대효과

하지만 생성된 유저에게 수작업으로 권한을 일일이 부여하는 방법을 구성원이 많아지면 많아질수록 한계에 다다르게 됩니다. 그래서 구성원이 많아지면 어떻게 관리하면 좋을 까 고민하던 중, AWS에서 특정 역할(Role)을 생성하고 역할을 사용자에게 부여하는 방법으로 AWS 계정 관리를 한다는 것을 알게 되었습니다.

이것을 데이터베이스에서도 할 수 있을 것 같다는 생각을 했었고, 데이터베이스 내 여러개의 권한을 묶어서 역할(Role)을 만들고 역할을 유저에게 부여하는 방법을 계획했습니다.

역할 생성하기

역할(Role, 이하 역할이라고 하겠습니다.)을 생성하기 위해서는 당연하게도 루트(super)권한을 필요로 합니다. 정확하게 말한다면 Global Create Role 또는 Create User 권한이 있어야 합니다.

-- Format
CREATE ROLE [IF NOT EXISTS] role [, role ] ...

-- Example.
-- Assume that db_name is app_db.
CREATE ROLE 'app_admin_role', 'app_developer_role', 'app_read_role', 'app_write_role';
FLUSH PRIVILEGES;

역할은 특정 데이터베이스(또는 스키마)에 접근할 수 있고 특정 권한을 갖고 있는 잠금(lock)된 계정입니다. 따라서 역할은 유저를 조회할 때, 역할도 보여지게 됩니다. 하지만 역할은 유저와는 다르게 잠겨 있어, 로그인 할 수 없습니다. 따라서 유저를 조회할 때 역할이 어떤것인지를 구분하기 쉽게 suffix 또는 prefix로 역할이라는 것을 명시해주는 것이 좋습니다.

역할에 권한 부여하기

역할을 생성했다면 해당 역할에 적절한 권한을 부여한 다음, 역할을 유저에게 할당하게 된다. 이때 역할sql에 적절한 권한을 부여하는 방법에 대해 알아보겠습니다.

-- format
GRANT
priv_type [(column_list)]
[, priv_type [(column_list)]] ...
ON [object_type] priv_level
TO user_or_role [, user_or_role] ...
[WITH GRANT OPTION]
[AS user
[WITH ROLE
DEFAULT
| NONE
| ALL
| ALL EXCEPT role [, role ] ...
| role [, role ] ...
]
]

-- Example
-- GRANT 권한종류 ON 대상 TO 계정명
GRANT ALL ON app_db.* TO 'role_app_developer';
GRANT SELECT ON app_db.* TO 'role_app_read';
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'role_app_write';
FLUSH PRIVILEGES;

위에 예제를 살펴보면 role_app_developer 역할에는 app_db 데이터베이스의 하위 폴더를 접근하는 모든 권한을 부여하였고, role_app_read 역할에는 테이블의 레코드 조회만 할 수 있는 권한을 role_app_write_role 에는 테이블의 레코드를 입력, 수정, 삭제 할 수 있는 권한을 주었습니다.

개인적으로 제안하는 역할과 권한

MSSQL에서는 MS에서 제안하는 고정 데이터베이스의 역할이 존재합니다. 이 역할에 할당된 권한은 수정 불가능 합니다.

출처 : Microsoft 데이터베이스 수준 역할

저는 아래와 같이 각각의 역할에 권한을 부여해보았습니다. 사람마다 생각하는 권한별로 역할을 더 다양하고 세세하게 쪼갤수 있습니다. 각 역할에 대한 권한을 이해하려면 MySQL Privileges 공식문서를 참고 바랍니다.

role_admin - 관리자 역할

  • all privileges

role_advanced_developer - 책임 개발자 역할

  • SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON

role_developer - 개발자 역할

  • SELECT, INSERT, UPDATE, DELETE, CREATE, EXECUTE, INDEX ON

role_manager 또는 role_reader - 비개발자, 뷰어 역할

  • SELECT

role_application - 어플리케이션 용 역할

  • SELECT, INSERT, UPDATE, DELETE, EXECUTE ON

역할 권한 생성시 유의사항

역할에 특정 권한을 주었지만, 부득이한 사정에 의해 권한을 변경해야한다고 했을 때, 일반적으로 GRANT, INSERT, DELETE, UPDATE 의 쿼리문을 통해 수정하게 될 것입니다. 이때 FLUSH PRIVILEGES명령어를 꼭 사용해주어야 합니다.

이때 변경하려는 유저는 여러 권한 중 꼭 Reload 권한을 갖고 있어야 합니다.

FLUSH PRIVILEGES

FLUSH PRIVILEGES는 현재 사용중인 MySQL 캐시를 지우고 새로운 설정을 적용하기 위해 사용합니다.

데이터베이스의 테이블은 MySQL의 환경 설정이 아니므로 FLUSH PRIVILEGES 가 필요하지 않습니다. 하지만 Role, User, 패스워드 등이 수정되었을 때는 FLUSH PRIVILEGES 가 필요합니다.

역할의 권한 조회하기

현재 사용자에 대한 권한 및 역할을 표시하는 것이 아닌, 특정 사용자의 역할을 조회를 하기 위해서는 Mysql 시스템 스키마에 대한 SELECT 권한을 필요로 합니다.

SHOW GRANTS; 
SHOW GRANTS FOR 유저이름; -- 특정 유저의 역할 및 권한 조회
SHOW GRANTS FOR CURRENT_USER; -- 현재 유저의 역할 및 권환 조회
SHOW GRANTS FOR CURRENT_USER();

하지만 위에처럼 보게되면 각 부여된 역할에 부여된 권한을 확인하기 어렵습니다. 역할 권한도 표시하면 아래처럼 USING 절을 추가하면 역할에 포함된 권한도 볼 수 있습니다.

SHOW GRANTS FOR 'some.user'@'host' USING 'app_read_role';
SHOW GRANTS FOR 'some.user'@'host' USING 'app_read_role', 'app_write_role';

권한 삭제하기

DROP ROLE IF EXISTS role [, role] ...

Global DROP Role, Create Role 또는 Create user 권한을 갖은 사용자가 위의 문장으로 역할을 삭제할 수 있습니다. 특히 Create user 권한이 있는 사용자는 위의 명령문으로 잠긴 계정이거나, 잠금이 해제된 계정을 삭제할 수도 있지만, Drop Role 권한만 있는 사용자는 잠긴 계정을 삭제할 때만 위 명령문을 쓸 수 있습니다.

역할과 관련된 문제점 관리하기

역할을 생성 후, 유저에게 권한을 부여하였어도 유저로 접속하면 스키마가 보이지 않는 현상이 있습니다. 이땐 역할이 활동 상태인지 비활동 상태인지 확인해야합니다.

사용자 세션에서 사용자에게 부여하는 역할이 비활동 상태에 있다면 사용자는 최종적으로 권한을 부여받을 수 없습니다.왜냐하면 주어진 역할 세션에서 활성 상태 일때만 사용자는 이 권한을 갖을 수 있기 때문입니다.

주어진 사용자를 활성 상태로 변경하기

역할을 유저에게 부여된 후, 해당 사용자에 대하여 활성화가 되어야 정상적으로 사용이 가능합니다.

주어진 사용자가 활성 상태인지 확인하려면 CURRENT_ROLE() 함수를 사용하면 됩니다.

-- 주어진 사용자의 계정으로 로그인

SELECT CURRENT_ROLE();
-- output: 현재_부여받은_Role@'%'
-- output: NONE

만약 위와같이 NONE으로, 활성상태가 이루어지지 않았다면, 아래의 명령어를 통해서 활성 상태로 변경할 수 있습니다.

SET DEFALUT ROLE role_name TO 'some.user'@'%';

그리고 다시 접속하여 권한을 확인하면, 권한이 생긴 것을 확인 할 수 있습니다.

항상 역할을 자동으로 활성화 하기

시스템 변수 activate_all_roles_on_loginmandatory_roles의 옵션에 따라 기본 역할을 자동으로 설정해주고 활성 상태로 변경 할 수 있습니다.

mandatory_roles : 이 시스템 변수 값에 등록된 역할을 사용자를 추가 할 때마다 자동으로 이 역할을 기본적으로 할당해 줍니다.

activate_all_roles_on_login : 이 시스템 변수의 값이 OFF이라면 매번 활성 상태로 바꾸어 주어야합니다. 하지만 ON이라면 역할을 유저에게 부여할 때마다 자동으로 활성 상태로 바꾸어줍니다.

시스템 변수 mandatory_roles 조회하기

SHOW VARIABLES LIKE 'mandatory_roles';

mandatory_roles 변경하기

SET GLOBAL mandatory_roles = 'role_name';

시스템 변수 activate_all_roles_on_login를 조회하기

SHOW VARIABLES LIKE 'activate_all_roles_on_login';

activate_all_roles_on_login을 변경하기

SET GLOBAL activate_all_roles_on_login = ON;

이렇게 역할을 생성하고 부여하는 방법으로 DBA는 권한을 효율적으로 유저에게 부여하고 취소할 수 있게 되었습니다.

권한 부족 에러

아래의 예시를 보면 ADMIN, ROLE_ADMIN 또는 SUPER 권한이 필요하다고 말하고 있습니다. 이때 해결 방법으로는 위의 역할을 줄 수 있는 권한을 갖고 있는 사용자가 부여해 줄 수 있습니다.

-- Error Code: 1227. Access denied; you need (at least one of) the WITH ADMIN, ROLE_ADMIN, SUPER privilege(s) for this operation

GRANT ROLE_ADMIN ON *.* TO 'some.user'@'%';

또한, 위의 시스템 변수를 수정하려고 할 때, 시스템 변수를 수정할 수 있는 권한이 부족하다는 에러를 맞이할 수 있습니다. 이때 해결 방법은 마찬가지로 위의 역할을 줄 수 있는 권한을 갖고 있는 사용자가 부여해 줄 수 있습니다.

-- Error Code: 1227. Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation

GRANT SYSTEM_VARIABELS_ADMIN ON *.* TO 'some.user'@'%';

( AWS RDS 사용자라면 RDS의 마스터 계정이 받은 권한 내에서 부여 할 수 있습니다.)*

예상치 못한 문제 : 저장 프로시저, 함수

파생 문제: 저장프로시저

누비랩에서 새롭게 역할과 계정을 배부하고 데이터베이스 보안을 강화했다고 생각했을 때, 하나의 문제가 발생했습니다. 바로 기존에 관리자 계정으로 생성했던 저장 프로시저(Stored Procedure)를 호출할 수 없던 문제였습니다.

같이 기존의 저장 프로시저를 볼까요?

기존에 프로시저는 아래와 같이 저장되어 있었습니다.

CREATE DEFINER=`admin`@`%` PROCEDURE `some_procedure`(
... some parameter
)
BEGIN
... some logic
END

Definer가 관리자 계정으로 되어 있어, 해당 프로시저를 수정할 수 있는 사람은 admin 뿐이었습니다. 또한 프로시저를 실행하려면 내부 명령어를 실행 할 수 있는 권한과 프로시저를 정의한 사람의 권한이 필요했습니다.

해당 프로시저를 수정하기 위해서는 다시 변경된 계정으로 프로시저를 재생성 또는 Definer를 수정해야겠지만, 우선 당장 프로시저를 실행해야 했기에 기존에 관리자 계정으로 저장 프로시저에 SQL SECURITY INVOKER 옵션을 주어 해결할 수 있었습니다.

CREATE DEFINER=`admin`@`%` PROCEDURE `some_procedure`(
... some parameter
)
SQL SECURITY INVOKER

BEGIN
... some logic
END

본인이 생성한 프로시저를 다른 유저가(해당 유저의 권한으로) 실행하도록 허락하는 옵션이 SQL SECURITY INVOKER 옵션입니다.

저장 프로시저를 수정할 때는 ALTER PROCEDURE 문을 사용하여 수정하지만 실제적으로 MySQL에서는 저장프로시저를 삭제하고 다시 생성하는 방법으로 동작하게 됩니다. 따라서 저장 프로시저를 수정하려면 주의해야합니다.

마치며

앞으로 얻을 수 있을 기대효과 및 전반적인 내용정리.

데이터베이스를 지키는 여러가지 방법 중 특히 “사용자 접근 제어”를 누비랩에서 어떤 방법으로 했는 지 알아보았습니다. 물론 그 과정이 순탄하지 많은 않았지만, 가장 큰 문제점이였던 ‘모두가 관리자 권한’에서 ‘누구나 적절한 권한’으로 데이터베이스를 접근 할 수 있게 되었습니다. 이렇게 데이터베이스를 훨씬 더 안전하게 지킬 수 있었고, 문제가 발생했을 때 원인을 빠르게 찾고 문제를 원활하게 해결할 수 있었습니다.

--

--