DataBase

[MariaDB] 보안 취약점 점검 #6 사용자 계정 생성

빤따스뤽 2025. 2. 26. 11:03

인프라 아키텍쳐

테스트를 위한 인프라 구성입니다.

 

실무에서는 더 복잡한 구성이고 노란 박스안의 개인정보 처리자 영역이 대부분 서비스 서버와 다른 네트워크에 존재 하겠지만 여기서는 IP 대역으로 아래와 같이 구분해서 별도의 네트워크라고 간주하겠습니다.

 

~192.168.45.99 : IDC 

       192.168.45.20~192.168.45.29 : Public Network (Web, API, App)

       192.168.45.30~192.168.45.39 : Private Network (DB)

 

192.168.45.100~ : 업무공간

 

계정별로 접근 가능한 IP 대역을 부여해 보기 위해 위처럼 가상으로 구분하겠습니다.

 

 

 

 

사용자 계정 생성

사용자 계정을 생성하기 전에 앞서 정리했던 보안 시나리오를 다시 한번 상기해 보겠습니다.

https://opensrc.tistory.com/257

 

[MariaDB] 보안 취약점 점검 #3 보안 시나리오

이전 페이지에서 필요한 플러그인 설치는 완료 했습니다.https://opensrc.tistory.com/256 [MariaDB] 보안취약점 점검 #2 비밀번호 알고리즘 개선 - 플러그인 설치MariaDB에서 제공하는 주요 비밀번호 보안 관

opensrc.tistory.com

 

비밀번호

  • 최소 숫자 2자리 포함
  • 최소 대/소문자 2자리 포함
  • 최소 특수문자 2자리 포함
  • 비밀번호 최소 길이 10자리 이상
  • 유추하기 쉬운 비밀번호 사용 금지
  • 사용한 비밀번호 직전 3회 사용 금지
  • 비밀번호 유효기간 90일
  • 안전한 비밀번호 알고리즘 사용 (md5, sha1 같은 취약한 알고리즘 불가)

 

계정 및 접근 관리

  • 개인별 계정 및 IP 할당
  • 개인 계정 별 권한 차등 부여

미리 생성한 webservice db 에 대해 dba(192.168.45.102)는 모든 권한을, 실제 어플리케이션에서 접속 해서 서비스를 하는 webservice(192.168.45.21, 192.168.45.22) 에게는 읽기 쓰기, 수정, 삭제, 실행, developer1(195.168.45.102, 매니저) 계정에 읽기, 쓰기, 수정, 삭제, 실행, developer2(195.168.45.103, 쥬니어) 에게는 읽기, 실행 권한만 부여해보도록 하겠습니다.

 

계정 생성 하기전에 이전에 설치한 비밀번호 보안 플러그인이 잘 동작하는지도 확인해 보겠습니다.

password_reuse_check plugin

MariaDB [mysql]> create user 'developer'@'192.168.45.103' identified via ed25519 using  password ('보안삭제') password expire interval 90 day;
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements (password_reuse_check)
MariaDB [mysql]>

실제 사용할 계정을 만들었다가 drop 하고 같은 비밀번호로 생성시도 했을때 비밀번호 재사용 정책 위반으로 에러가 발생했습니다

 

simple_password_check

MariaDB [mysql]> create user 'developer'@'192.168.45.103' identified via ed25519 using  password ('Bass') password expire interval 90 day;
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements (simple_password_check)
MariaDB [mysql]>

간단한 문자열 역시 에러가 발생합니다.

 

cracklib_password_check

MariaDB [mysql]> create user 'developer'@'192.168.45.103' identified via ed25519 using  password ('Developer!@QWER1234') password expire interval 90 day;
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements (cracklib_password_check)
MariaDB [mysql]>

예측 가능한 단어로 이뤄진 비밀번호는 복잡해도 사용할 수 없습니다.

 

모두 정상 작동 하고 있습니다.

 

 

사용자 생성

MariaDB [mysql]> select host, user, plugin from user;
+-----------+-------------+-----------------------+
| Host      | User        | plugin                |
+-----------+-------------+-----------------------+
| localhost | mariadb.sys | mysql_native_password |
| localhost | root        | ed25519               |
| localhost | mysql       | mysql_native_password |
+-----------+-------------+-----------------------+
3 rows in set (0.004 sec)

MariaDB [mysql]> create user 'dba'@'192.168.45.102' identified via ed25519 using password('보안삭제') password expire interval 90 day;
Query OK, 0 rows affected (0.023 sec)

MariaDB [mysql]> create user 'developer1'@'192.168.45.102' identified via ed25519 using password('보안삭제') password expire interval 90 day;
Query OK, 0 rows affected (0.021 sec)

MariaDB [mysql]> create user 'developer2'@'192.168.45.103' identified via ed25519 using password('보안삭제') password expire interval 90 day;
Query OK, 0 rows affected (0.021 sec)

MariaDB [mysql]> create user 'webservice'@'192.168.45.20/255.255.255.252' identified via ed25519 using password('보안삭제') password expire interval 90 day;
Query OK, 0 rows affected (0.045 sec)

MariaDB [mysql]> select user, host, plugin from user;
+-------------+-------------------------------+-----------------------+
| User        | Host                          | plugin                |
+-------------+-------------------------------+-----------------------+
| mariadb.sys | localhost                     | mysql_native_password |
| root        | localhost                     | ed25519               |
| mysql       | localhost                     | mysql_native_password |
| dba         | 192.168.45.102                | ed25519               |
| developer1  | 192.168.45.102                | ed25519               |
| webservice  | 192.168.45.20/255.255.255.252 | ed25519               |
| developer2  | 192.168.45.103                | ed25519               |
+-------------+-------------------------------+-----------------------+
7 rows in set (0.005 sec)

MariaDB [mysql]>

 

MySQL 계열 DB에서는 사용자ID가 같아도 Host가 다르면 다른 계정으로 인식한다는 점을 숙지해야 합니다.

Host는 단독 IP로 지정을 할 수 도 있지만, 와일드카드 %를 이용하여 범위를 지정 할 수 있습니다.

developer@% 이면 IP 제한 없이 어디에서든 접속 가능합니다.

developer@10.10.% 이면 10.10.xxx.xxx 네트워크 B클래스 어디서든 접속 가능합니다.

네트워크 마스크 비트를 이용한 developer@192.168.45.0/24 이런식으로 지정은 불가하고 

developer@192.168.45.0/255.255.255.0 이런식으로 서브넷 마스크를 이용 할 수 있습니다.

192.168.45.20/255.255.255.252 는 192.168.45.20 ~ 23까지 4개의 IP를 허용 합니다.

 

권한 부여

dba 계정에 데이터베이스 webservice 의 모든 권한을 부여해보겠습니다.

MariaDB [mysql]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| webservice         |
+--------------------+
5 rows in set (0.002 sec)

MariaDB [mysql]> grant all privileges on webservice.* to 'dba'@'192.168.45.102';
Query OK, 0 rows affected (0.004 sec)

MariaDB [mysql]> show grants for 'dba'@'192.168.45.102';
+--------------------------------------------------------------------------------------+
| Grants for dba@192.168.45.102                                                        |
+--------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `dba`@`192.168.45.102` IDENTIFIED VIA ed25519 USING '보안삭제' |
| GRANT ALL PRIVILEGES ON `webservice`.* TO `dba`@`192.168.45.102`                     |
+--------------------------------------------------------------------------------------+
2 rows in set (0.001 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]>

 

webservice 계정에 조회, 입력, 수정, 삭제, 실행, 보기 권한을 부여해보겠습니다.

MariaDB [mysql]> grant select, insert, update, delete, execute, show view on webservice.* to 'webservice'@'192.168.45.20/255.255.255.252';
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]> show grants for 'webservice'@'192.168.45.20/255.255.255.252';
+----------------------------------------------------------------------------------------------------------------------------+
| Grants for webservice@192.168.45.20/255.255.255.252                                                                        |
+----------------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `webservice`@`192.168.45.20/255.255.255.252` IDENTIFIED VIA ed25519 USING '보안삭제'                 |
| GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE, SHOW VIEW ON `webservice`.* TO `webservice`@`192.168.45.20/255.255.255.252` |
+----------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.001 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]>

 

devleper1 계정에  조회, 입력, 수정, 삭제, 실행, 보기 developer2 계정에 조회, 실행, 보기 권한을 부여하겠습니다.

MariaDB [mysql]> grant select, insert, update, delete, execute, show view on webservice.* to 'developer1'@'192.168.45.102';
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]> grant select, execute, show view on webservice.* to 'developer2'@'192.168.45.103';
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]> show grants for 'developer1'@'192.168.45.102';
+-------------------------------------------------------------------------------------------------------------+
| Grants for developer1@192.168.45.102                                                                        |
+-------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `developer1`@`192.168.45.102` IDENTIFIED VIA ed25519 USING '보안삭제'                 |
| GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE, SHOW VIEW ON `webservice`.* TO `developer1`@`192.168.45.102` |
+-------------------------------------------------------------------------------------------------------------+
2 rows in set (0.001 sec)

MariaDB [mysql]> show grants for 'developer2'@'192.168.45.103';
+---------------------------------------------------------------------------------------------+
| Grants for developer2@192.168.45.103                                                        |
+---------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `developer2`@`192.168.45.103` IDENTIFIED VIA ed25519 USING '보안삭제' |
| GRANT SELECT, EXECUTE, SHOW VIEW ON `webservice`.* TO `developer2`@`192.168.45.103`         |
+---------------------------------------------------------------------------------------------+
2 rows in set (0.001 sec)

MariaDB [mysql]>

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.003 sec)

MariaDB [mysql]>

 

사용자에게 권한이 없는 명령을 실행하면 아래와 같이 command denied to user ... 에러가 발생 합니다.

 

참고1 : 권한을 부여 할 때 개별 테이블, 컬럼까지도 상세하게 줄 수 있습니다.

참고2 : 테이블 별로 권한이 복잡해 지는 경우 사용자 관리에 공수가 많이 들 것 같아서 MariaDB에서 role을 생성하고 그 role에 권한을 부여하는 방법을 테스트 해보았지만 원하는 대로 동작하지 않았습니다. https://opensrc.tistory.com/236

잘 아시는 분은 조언 부탁드립니다.

참고3 : 외부에서 DB클라이언트를 통해 접속시 Socket Error (115) 에 나오는 분은 bind_address를 확인해 보세요

더보기

외부에서 DBeaver 접속 했을 때 아래와 같은 오류가 발생 했습니다.

Socket fail to connect to 192.168.45.31. Connection refused: getsockopt
  Connection refused: getsockopt

 

다른 ubunut 에서 접속 하면 아래와 같은 에러가 발생 했습니다.

 

$ mysql -h192.168.45.31 -udeveloper1 -p
Enter password:
ERROR 2002 (HY000): Can't connect to server on '192.168.45.31' (115)
$

 

외부에서 연결이 안되는 경우는 대부분 방화벽 문제가 많습니다.

$ sudo ufw status
Status: inactive
$ netstat -nltp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:6011          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 ::1:6010                :::*                    LISTEN      -
tcp6       0      0 ::1:6011                :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
$

방화벽은 비활성화 되어 있고 MariaDB가 127.0.0.1:3306 루프백 IP로 바인딩 되어 있네요 이게 문제 인것 같습니다.

 

오류 메시지로 구글링 하면 대부분 해결책을 찾을 수 있습니다.

https://stackoverflow.com/questions/64320136/error-2002-hy000-cant-connect-to-mysql-server-on-192-168-1-15-115

 

두가지를 확인하라고 하네요

SHOW VARIABLES LIKE 'skip_networking'; (result should be off)
SHOW VARIABLES LIKE 'bind_address'; (should not be 127.0.0.1)

 

MariaDB [(none)]> show variables like 'skip_networking';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| skip_networking | OFF   |
+-----------------+-------+
1 row in set (0.005 sec)

MariaDB [(none)]> show variables like 'bind_address';
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| bind_address  | 127.0.0.1 |
+---------------+-----------+
1 row in set (0.005 sec)

MariaDB [(none)]>

 

역시 bind_address 가 문제 였습니다.

my.cnf 에 추가하고 재가동 하면 되는데,

제가 설치한 ubuntu 에는 /etc/mysql/mariadb.conf.d/50-server.cnf 파일에 IP를 변경해 주면 됩니다.

$ sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
... 생략...
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address            = 127.0.0.1
bind-address            = 192.168.45.31
... 생략
$

 

다음 편에서는 감사로그 다루는 방법을 알아 보겠습니다.

 

끝.