MySQL 서버 보안 구성을 강화하기 위한 모범 사례
MySQL은 수많은 애플리케이션의 초석으로, 소규모 블로그부터 거대한 엔터프라이즈 시스템에 이르기까지 모든 것을 구동합니다. 그 인기는 견고성과 유연성에 대한 증거이지만, 악의적인 공격자들의 잦은 표적이 되기도 합니다. 잘못 구성되거나 강화되지 않은 MySQL 서버는 민감한 데이터를 노출하고 전체 인프라에 대한 무단 액세스 게이트웨이를 제공하는 중대한 취약점이 될 수 있습니다. MySQL 배포를 보호하는 것은 일회성 작업이 아니라 경계심과 보안 모범 사례 준수를 요구하는 지속적인 노력입니다.
이 문서는 필수 구성 팁과 보안 강화 조치에 대한 포괄적인 가이드를 제공합니다. 사용자 관리 및 네트워크 보호부터 구성 파일 조정 및 운영 체제 고려 사항에 이르기까지 다양한 보안 계층을 자세히 살펴보겠습니다. 이러한 권장 사항을 구현함으로써 MySQL 서버의 공격 표면을 크게 줄이고 일반적인 취약점과 무단 액세스 시도에 대한 보다 탄력적인 데이터베이스 환경을 구축할 수 있습니다.
1. 사용자 및 액세스 관리: 최소 권한의 원칙
효과적인 사용자 관리는 MySQL 보안의 기반입니다. 사용자에게 반드시 필요한 권한만 부여하는 것이 가장 중요합니다.
특정 애플리케이션을 위한 특정 사용자 생성
애플리케이션 연결에 root 사용자를 사용하는 것을 피하십시오. 대신 세분화된 권한을 가진 전용 사용자를 생성하십시오.
CREATE USER 'my_app_user'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON `your_database`.* TO 'my_app_user'@'localhost';
FLUSH PRIVILEGES;
강력한 암호 적용
강력하고 고유한 암호는 방어의 첫 번째 선입니다. 사용 가능한 경우 MySQL의 내장 암호 유효성 검사 플러그인을 활용하십시오.
- 복잡성: 대문자, 소문자, 숫자 및 기호를 혼합하십시오.
- 길이: 최소 12-16자를 목표로 하십시오.
- 고유성: 암호를 절대 재사용하지 마십시오.
- 순환: 정기적인 암호 변경 정책을 구현하십시오.
You can enable the validate_password component (MySQL 8.0+) or plugin (MySQL 5.7+):
INSTALL COMPONENT 'file://component_validate_password';
-- OR for older versions
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
-- Configure strength policy (e.g., MEDIUM for 8+ chars, mix case, numbers, special)
SET GLOBAL validate_password.policy = MEDIUM;
SET GLOBAL validate_password.length = 12;
기본 및 사용하지 않는 사용자 제거
MySQL은 기본 사용자(예: root@localhost)와 함께 제공됩니다. root@localhost는 필요하지만 강력한 암호를 사용해야 합니다. 중요하게도, mysql.session, mysql.sys 및 익명 사용자와 같은 기타 기본 사용자가 명시적으로 필요하지 않고 올바르게 구성되지 않은 경우 제거하거나 보호하십시오.
-- 익명 사용자를 식별하려면:
SELECT user, host FROM mysql.user WHERE user = '';
-- 익명 사용자를 삭제하려면 (찾은 경우):
DROP USER ''@'localhost';
-- test 데이터베이스를 제거하려면 (존재하는 경우):
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%';
FLUSH PRIVILEGES;
호스트 액세스 제한
사용자 계정을 특정 IP 주소 또는 호스트 이름에서만 연결되도록 제한하십시오. 반드시 필요하고 다른 강력한 보안 제어와 결합되지 않는 한 호스트에 대한 와일드카드(%) 사용을 피하십시오.
-- 사용자는 localhost에서만 연결할 수 있습니다
CREATE USER 'admin'@'localhost' IDENTIFIED BY 'AnotherStrongPass!';
-- 사용자는 특정 IP 주소에서만 연결할 수 있습니다
CREATE USER 'backup_user'@'192.168.1.100' IDENTIFIED BY 'BackupPass!';
GRANT OPTION 주의
The WITH GRANT OPTION 절은 사용자가 자신의 권한을 다른 사용자에게 부여할 수 있도록 합니다. 이는 신뢰할 수 없는 사용자에게 부여될 경우 상당한 보안 위험이 될 수 있습니다. 이 기능을 진정으로 필요로 하는 관리 계정의 경우에만 제한적으로 사용하십시오.
-- 권한 부여 능력이 있는 사용자 (극도의 주의 필요)
GRANT ALL PRIVILEGES ON *.* TO 'superadmin'@'localhost' IDENTIFIED BY 'SuperAdminPass!' WITH GRANT OPTION;
2. 네트워크 보안: 데이터베이스 격리
승인되지 않은 외부 액세스로부터 MySQL 서버를 방지하려면 네트워크 수준 제어가 중요합니다.
방화벽 구성
MySQL의 기본 포트(3306)에 대한 연결을 신뢰할 수 있는 IP 주소 또는 네트워크에서만 허용하십시오. 이 포트로 들어오는 다른 모든 연결을 차단하십시오.
예시 (Linux의 UFW):
sudo ufw enable
sudo ufw allow from 192.168.1.0/24 to any port 3306
sudo ufw deny 3306
sudo ufw status
예시 (CentOS/RHEL의 firewalld):
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --permanent --remove-port=3306/tcp --zone=public # 전역적으로 열려 있는지 확인
sudo firewall-cmd --reload
SSL/TLS를 사용한 연결 보안
클라이언트와 MySQL 서버 간의 모든 트래픽을 SSL/TLS를 사용하여 암호화하여 도청 및 중간자(MitM) 공격을 방지하십시오. 이는 특히 신뢰할 수 없는 네트워크를 통한 연결에 중요합니다.
SSL/TLS를 활성화하려면 일반적으로 SSL 인증서와 키를 생성한 다음 my.cnf를 구성해야 합니다.
# my.cnf
[mysqld]
ssl_ca=/etc/mysql/certs/ca.pem
ssl_cert=/etc/mysql/certs/server-cert.pem
ssl_key=/etc/mysql/certs/server-key.pem
클라이언트는 이후 SSL/TLS를 사용하여 연결하도록 구성해야 하며, 종종 사용자의 GRANT 문에 REQUIRE SSL이 포함됩니다.
CREATE USER 'ssl_user'@'%' IDENTIFIED BY 'SSLUserPass!';
GRANT SELECT ON `your_database`.* TO 'ssl_user'@'%' REQUIRE SSL;
FLUSH PRIVILEGES;
특정 IP 주소에 바인딩
기본적으로 MySQL은 사용 가능한 모든 네트워크 인터페이스(0.0.0.0)에서 수신 대기할 수 있습니다. 연결을 수락해야 하는 인터페이스(예: 로컬 애플리케이션의 경우 localhost 또는 내부 연결의 경우 개인 네트워크 IP)에서만 수신 대기하도록 제한하십시오.
# my.cnf
[mysqld]
bind-address = 127.0.0.1 # 로컬 연결 전용
# OR
bind-address = 192.168.1.10 # 특정 내부 IP의 경우
팁: 애플리케이션과 MySQL 서버가 동일한 머신에 있는 경우,
bind-address = 127.0.0.1(localhost)가 외부 연결을 완전히 방지하므로 가장 안전한 옵션입니다.
3. 구성 파일 강화 (my.cnf / my.ini)
MySQL 구성 파일(Linux에서는 my.cnf, Windows에서는 my.ini)은 보안을 강화하기 위한 수많은 매개변수를 제공합니다.
사용하지 않는 기능 비활성화
배포에 필요하지 않은 기능을 비활성화하여 공격 표면을 최소화하십시오.
local_infile = 0: 클라이언트가 서버 호스트의 임의의 파일을 로드하도록 서버에 요청하는 것을 방지합니다. 이는 데이터 유출의 일반적인 벡터입니다.
ini [mysqld] local_infile = 0skip-networking: 데이터베이스에 동일한 서버의 애플리케이션에 의해서만 액세스되는 경우 네트워킹을 완전히 비활성화합니다. 이렇게 하면 모든 연결이 Unix 소켓 또는 명명된 파이프를 사용하도록 강제됩니다.
ini [mysqld] skip-networkingsymbolic-links = 0: MySQL 데이터 디렉터리 외부의 파일 액세스를 악용할 수 있는 데이터베이스 테이블스페이스에 대한 기호 링크 사용을 방지합니다.
ini [mysqld] symbolic-links = 0secure_file_priv:LOAD DATA INFILE및SELECT ... INTO OUTFILE과 같은 함수에 의해 파일이 읽고 쓸 수 있는 디렉터리를 제한합니다.
```ini
[mysqld]
secure_file_priv = "/var/lib/mysql-files" # 특정 제한된 디렉터리로 설정
# OR
# secure_file_priv = "