DBMS 2

DA, SQL, DB보안 등 실무자를 위한 위한 DB기술 바이블!

일반적인 보안 이슈

DBMS 2
MySQL 가이드
데이터 베이스 관리
일반적인 보안 이슈
작성자
admin
작성일
2021-02-19 10:51
조회
1413

일반적인 보안 이슈

일반적인 보안 가이드라인
공격으로부터 MySQL 보안 설정하기
보안-관련 mysqld 옵션
LOAD DATA LOCAL 이 가지는 보안 이슈
일반 사용자 자격으로 MySQL 구동시키는 방법

이 섹션에서는 다른 사람의 악성적인 공격에 대응하게끔 MySQL을 보다 안정적으로 설치하는 방법에 대해서 설명을 하도록 한다. MySQL사용자 계정을 설정하고 데이터 베이스 접근을 제어하는 접근 제어 시스템에 관련된 사항은 Section 5.8, “MySQL 접근 권한 시스템”에서 다루기로 한다.


일반적인 보안 가이드라인

인터넷에 연결되어 있는 컴퓨터에서 MySQL을 사용하고 있는 사용자들이라면 이 섹션을 읽기 바란다.

MySQL은 사용자가 수행하고자 시도하는 모든 접속, 쿼리, 그리고 다른 실행 동작에 대해 접근 제어 리스트(ACL)에 근거를 한 보안 정책을 사용하고 있다. 또한 MySQL 클라이언트와 서버 사이의 SSl-암호화 접속도 지원을 한다. 여기에서 설명하는 많은 개념들은 MySQL에만 국한되지 않는다; 동일한 개념들이 거의 모든 어플리케이션에도 적용된다.

MySQL을 구동할 때에는 가능하면 항상 아래의 가이드 라인을 준수하기 바란다:


  • user 테이블에 대해서는 누구도 접근을 허용하지 말 것 (MySQL root 계정은 제외)! 이 테이블은 매우 중요한 것이다. MySQL에서는 패스워드가 암호화 된다. 테이블에 있는 패스워드를 알게 되면, 누구라도 쉽게 다른 사용자 계정에 접근할 수가 있다.
  • MySQL 접근 권한 시스템에 대해 공부한다. GRANT 및 REVOKE 명령문은 MySQL접근을 제어하는데 사용된다. 모든 호스트에 필요 이상의 권한을 부여하지 말도록 한다.
    체크 리스트:
    1. mysql -u root를 실행해 본다. 만약에 패스워드를 넣지 않고도 서버에 쉽게 접속이 된다면, 모든 사람들이 전체 권한을 가진 root 사용자 자격으로 여러분의 MySQL 서버에 접속할 수가 있다는 것을 의미한다! MySQL 설치 설명을 다시 한번 보고 root 패스워드 설정에 대한 사항을 숙지하기 바란다.
    2. 어떤 계정이 접속 하고 있는지를 보기 위해서는 SHOW GRANTS 명령문을 사용한다. 그 다음에는 필요 없는 권한을 삭제하기 위해서 REVOKE 명령문을 사용한다.
  • 여러분의 데이터 베이스에서 평범한 패스워드는 저장하지 말 것. 대신에, MD5(), SHA1(), 또는 다른 단 방향 해싱 (one-way hashing) 함수를 사용해서 해시 (hash) 값을 저장하도록 한다.
  • 방화벽을 설치한다. MySQL을 방화벽 뒤 또는 DMZ (demilitarized zone)에 놓는다.
    1. nmap과 같은 툴을 사용해서 인터넷에서 여러분의 포트를 스캔하여 본다. MySQL 은 디폴트로 3306 포트를 사용한다. 이 포트는 인가 받지 못한 호스트는 접근하지 못하게 한다. 여러분의 포트가 열리는지 아닌지를 검사 하는 다른 간단한 방법은 리모트 머신에서 아래의 명령어를 시도해 보는 것이다. 여기에서 server_host는 여러분의 MySQL 서버가 구동되고 있는 호스트 이름 또는 IP 번호가 된다:
      shell> telnet server_host 3306
      이것을 사용해서 접속이 되고 이상한 문자가 나오게 되면, 여러분의 포트는 열려 있는 것이며, 방화벽 또는 라우터를 사용해서 닫아야 한다. 만일 telnet이 행 (hang) 상태가 되거나 또는 접속이 거절되면, 포트는 블로킹 된 것이다.
  • 많은 어플리케이션 프로그래밍 인터페이스는 데이터 값에서 이스케이핑 (escaping) 특수 문자를 제공한다. 제대로만 사용한다면, 이것을 통해 어플리케이션 사용자가 여러분이 의도하는 것과는 다른 명령문을 어플리케이션이 만들지 못하도록 할 수 있다:
    1. MySQL C API: mysql_real_escape_string() API 호출을 사용한다.
    2. MySQL++: 쿼리 스트림을 위해 escape 와 quote 모디파이어 (modifier)를 사용한다.
    3. PHP: mysql_escape_string() 함수를 사용하는데, 이것은 MySQL C API에 있는 동일한 이름의 함수를 기초로 한다. (PHP 4.0.3 이전에는, addslashes()를 대신 사용함.) PHP 5에서는, mysqli 확장자를 사용할 수가 있는데, 이것은 MySQL 승인 프로토콜과 패스워드, 그리고 플레이스홀더 (placeholder)가 있는 프리페어드 명령문을 지원한다.
    4. Perl DBI: quote() 방식을 사용하거나 또는 플레이스홀더를 사용한다.
    5. Ruby DBI: 플레이스홀더를 사용한다.
    6. Java JDBC: PreparedStatement 오브젝트와 플레이스홀더를 사용한다.
    다른 프로그래밍 인터페이스에도 비슷한 기능들이 있다.
  • 인터넷으로 평이한 (암호화 하지 않은) 데이터를 전송하지 말 것. 대신에, SSL 또는 SSH와 같은 암호화 프로토콜을 사용한다. MySQL은 4.0 이후에 내부 SSL을 지원하고 있다. 다른 방법으로는 SSH 포트- 전달 방식을 사용해서 암호화 (압축) 터널을 생성해서 통신을 하는 것이다.
  • tcpdump 과 strings 유틸리티 사용법을 배운다. 대부분의 경우, 아래와 같은 명령어를 입력해서 MySQL 데이터 스트림의 암호화 상태를 검사할 수가 있다:
    shell> tcpdump -l -i eth0 -w - src or dst port 3306 | strings
    (위의 것은 리눅스 환경에서만 동작을 하는 명령문이며 다른 시스템에서는 약간 수정을 해야 한다.) 경고: 만약에 여러분이 일반 텍스트 데이터를 보지 못한다고 하더라도, 이것이 데이터가 실제로 암호화 되었다는 것을 항상 의미하는 것은 아니다.
.공격으로부터 MySQL 보안 설정하기

MySQL 서버에 접속을 할 때에는 패스워드를 사용하도록 한다. 클라이언트 접속 시퀀스 동안 패스워드를 처리하는 방식은 MySQL 4.1.1에서 많은 업그레이드가 되었다. 만약에 여러분이 아직도 4.1.1 이전 버전 형태의 패스워드를 사용한다면, 암호화 알고리즘이 새 버전의 알고리즘만큼 견고하지 못한 것이다. 명석한 공격자라면, 약간의 노력만 가지고도 클라이언트와 서버간의 통신을 가로채서 패스워드를 해석할 수가 있다.

다른 모든 정보는 텍스트 형태로 전송되는데, 이것은 접속을 지켜보는 사용자라면 누구라도 읽을 수가 있는 것이다. 만약에 클라이언트와 서버간의 접속이 신뢰도가 떨어지는 네트워크를 통해 이루어 진다면, 그리고 여러분이 이에 대해 걱정을 한다면, 압축 프로토콜을 사용해서 통신을 해석하는 것을 더욱 어렵게 만들 수도 있다. 또한 MySQL 내부 SSL을 사용해서 보안성을 보다 좋게 만들 수도 있다. Section 5.9.7, “보안 접속 사용하기”를 참조할 것.

MySQL 시스템의 보안성을 좋게 하기 위해서는, 여러분은 아래의 내용을 정확하게 숙지해야 한다:


  • 모든 MySQL 계정이 패스워드를 가지도록 한다.
    패스워드 설정 방법은 Section 5.9.5, “계정 패스워드 설정하기”를 참조한다.
  • 유닉스 root 사용자 권한으로는 절대로 MySQL 서버를 구동하지 말 것. 이것은 매우 위험한 일인데, 그 이유는 FILE 권한을 가진 사용자라면 누구라도 서버로 하여금 파일을 생성할 수 있게끔 할 수가 있기 때문이다 (예를 들면, ~root/.bashrc). 이것을 방지하기 위해서 mysqld는 --user=root 옵션을 사용해서 명확하게 지정을 하지 않는 한 root로 구동되는 것을 거부한다.
    대신에, 아무런 권한이 없는 일반 사용자만이 mysqld를 구동 시킬 수 있도록 했다. 여러분은 별도의 유닉스 계정 mysql를 생성해서 일을 처리하는 것이 보다 안전하다. 이 계정은 MySQL을 관리하는 용도로만 사용한다. 다른 유닉스 사용자로서 mysqld를 구동 시킬 때에는, 서버 옵션을 지정하는 my.cnf 옵션 파일의 [mysqld] 그룹에 사용자 이름을 지정하는 user 옵션을 추가한다. 예를 들면:
    [mysqld]
    user=mysql

    보다 자세한 사항은 Section 5.7.5, “일반 사용자 자격으로 MySQL 구동시키는 방법”을 참조할 것. mysqld를 root가 아닌 다른 유닉스 사용자로서 구동시킨다는 것은 여러분이 user 테이블에 있는 root 사용자 이름을 변경해야 한다는 것을 의미하는 것이 아니다. MySQL 계정에 대한 사용자 이름은 유닉스 계정에 대한 사용자 이름과는 아무 상관이 없다.
  • 테이블에 대한 심볼릭 링크를 허용하지 말 것. (이 기능은 --skip-symbolic-links 옵션을 사용해서 비활성화 시킬 수가 있다.) 이것은 mysqld를 root 자격으로 구동 시킬 때 특히 중요한 사항인데, 그 이유는 서버의 데이터 디렉토리에 쓰기 접근 권한을 가지는 사용자는 시스템의 모든 파일을 삭제할 수 있기 때문이다! Section 7.6.1.2, “유닉스에서 심볼릭 링크 사용하기”를 참조.
  • 데이터 디렉토리에서 읽기 또는 쓰기 권한을 가지고 있는 유닉스 사용자만이 mysqld를 구동 시킬 수 있도록 만든다.
  • 관리자가 아닌 사용자에게믐 PROCESS 또는 SUPER 권한을 주지 말 것. mysqladmin processlist와 SHOW PROCESSLIST는 현재 실행되고 있는 모든 명령문의 텍스트를 보여준다. 따라서 서버 프로세스 리스트를 볼 수 있는 사용자는 다른 사용자가 입력하는 UPDATE user SET password=PASSWORD('not_secure')와 같은 명령문도 볼 수가 있는 것이다. mysqld는 SUPER 권한을 가지고 있는 사용자를 위해 별도의 연결을 유지하는데, 그 이유는 일반적인 연결을 모두 사용하고 있더라도 MySQL root 사용자가 로그인하여 서버의 동작을 검사할 수 있도록 하기 위해서다.
    SUPER 권한은 클라이언트 접속을 종료시키고, 시스템 변수 값을 변경해서 서버 동작을 변경시키고, 리플리케이션 서버를 제어하기 위해 사용할 수가 있다.
  • 관리자가 아닌 사용자에게는 FILE 권한을 주지 말 것. 이 권한을 가지고 있는 사용자는 mysqld 데몬에 대한 권한을 사용해서 파일 시스템에 있는 어떤 파일에도 쓰기를 할 수가 있다.
  • 만약에 여러분의 DNS를 신뢰하지 못한다면, 그랜트 테이블에서 호스트 이름을 사용하지 말고 IP 번호를 사용하도록 한다. 또한, 와일드 카드가 있는 호스트 이름을 사용해서 그랜트 테이블 엔트리를 생성할 경우에는 세심한 주의를 한다.
  • 하나의 계정이 접속할 수 있는 숫자를 제한하고자 한다면, mysqld에 있는 max_user_connections 변수를 설정하면 된다. GRANT 명령문을 사용해도 하나의 계정에게 허용된 서버 사용 확장에 대한 자원 제어 옵션을 지정할 수가 있다. Section 13.5.1.3, “GRANT 신텍스”를 참조.
보안-관련 mysqld 옵션

아래의 mysqld 옵션들은 보안성에 영향을 주는 것들이다:


  • --allow-suspicious-udfs
    이 옵션은 메인 함수에 대해 xxx 심볼만 가지고 있는 사용자 정의 함수를 읽어올 수 있는지 없는지를 제어한다. 디폴트로는 오프 (off)이며, 최소 한 개의 보조 심볼을 가지고 있는 UDF만을 읽어올 수가 있다; 이것은 규칙에 맞는 UDF를 가지고 있는 파일이 아닌 공유 오브젝트 파일에서는 함수를 읽어오지 못하도록 만든다. 이 옵션은 5.0.3에 추가 되었다. Section 24.2.4.6, “사용자 정의 함수 보안 유의 사항”을 참고할 것.
  • --local-infile[={0|1}]
    만일 --local-infile=0를 사용해서 서버를 구동 시킨다면, 클라이언트는 LOAD DATA 에서 LOCAL을 사용할 수가 없게 된다. Section 5.7.4, “LOAD DATA LOCAL 보안 이슈”를 참조.
  • --old-passwords
    서버로 하여금 새로운 패스워드에 대해서 짧은 (4.1 이전 버전 스타일) 패스워드 해시를 만들도록 한다. 이것은 서버가 이전 버전의 클라이언트 프로그램을 지원해야 하는 경우에 유용하다. Section 5.8.9, “MySQL 4.1이후의 패스워드 해싱 (Hashing)”을 참조할 것.
  • --safe-show-database (더 이상 사용하지 않음)
    이전 버전의 MySQL의 경우, 이 옵션은 SHOW DATABASES 명령문이 사용자가 권한을 가지고 있는 데이터 베이스의 이름을 출력하게끔 한다. 5.0 이후에는 이 옵션은 더 이상 사용되지 않는다. Section 13.5.1.3, “GRANT 신텍스”를 참조할 것.
  • --safe-user-create
    만약에 이 옵션이 활성화되면, 사용자가 mysql.user 테이블 또는 테이블 내 컬럼에 대해서 INSERT 권한을 가지고 있지 않는 한 GRANT 명령문을 사용해서는 새로운 MySQL 사용자를 만들 수가 없게 된다. 만약에 사용자로 하여금 이러한 권한을 가지는 새로운 사용자를 생성할 수 있도록 하고자 한다면, 여러분은 그 사용자에게 아래의 권한을 승인해야 한다:
    GRANT INSERT(user) ON mysql.user TO 'user_name'@'host_name';
    이것은 사용자가 권한 컬럼을 직접 변경하지 못하도록 만들며, 대신에 GRANT 명령문을 사용해서 다른 사용자에게 권한을 주도록 만든다.
  • --secure-auth
    구형 (pre-4.1) 패스워드를 가지고 있는 계정에 대해서는 사용을 허가하지 않음. mysql 클라이언트도 --secure-auth 옵션을 가지는데, 이것은 만약에 서버가 클라이언트 계정에 대해 구형 포맷의 패스워드를 요구할 경우에는 접속을 하지 못하도록 하기 위함이다.
  • --skip-grant-tables
    이 옵션은 서버가 권한 테이블을 전혀 사용할 수 없도록 만든다. 이렇게 하면 누구라도 제한 없이 서버와 모든 데이터 베이스에 접근할 수 있게 된다. mysqladmin flush-privileges를 실행하거나, 시스템 쉘에서 mysqladmin reload 명령어를 실행하거나, 또는 MySQL FLUSH PRIVILEGES 명령문을 입력하면 구동 중인 서버가 그랜트 테이블을 사용해서 재 구동할 수 있게 된다. 이 옵션은 또한 사용자 정의 함수를 가져오지 못하도록 한다.
  • --skip-name-resolve
    호스트 이름을 해석하지 않는다. 그랜트 테이블에 있는 모든 Host 컬럼값은 반드시 IP 번호 또는 localhost 이어야 한다.
  • --skip-networking
    네트워크를 통한 TCP/IP 접속을 허용하지 않음. Mysqld에 대한 모든 접속은 유닉스 소켓 파일을 통해서 이루어져야 한다.
  • --skip-show-database
    이 옵션을 사용지고 있는 사용자에게만 허용이 되며, 이 명령문은 모든 데이터 베이스 이름을 출력한다. 이 옵션이 없으면, SHOW DATABASES는 모든 사용자가 사용할 수 있으나, 사용자가 SHOW DATABASES 권한을 가지고 있는 데이터 베이스 이름만 화면에 출력 한다. 모든 글로벌 권한은 데이터 베이스에 대한 권한이라는 것을 알기 바란다.
LOAD DATA LOCAL 이 가지는 보안 이슈

LOAD DATA 명령문은 서버 호스트에 있는 파일을 읽어거나, 또는 LOCAL 키워드가 지정이 되면 클라이언트에 있는 파일을 읽어 온다.

LOAD DATA 명령문에 대한 LOCAL 버전 지원에는 잠재적으로 두 가지의 보안 이슈가 존재 한다:


  • 클라이언트 호스트에서 서버 호스트로 파일을 전송하는 것은 MySQL 서버가 초기화 한다. 이론적으로는, 클라이언트가 LOAD DATA 명령문에서 지정하는 파일 대신에 서버가 선택한 파일을 클라이언트 프로그램이 전송하도록 서버를 구축할 수가 있다. 그러한 서버는 클라이언트 사용자가 읽을 수 있는 클라이언트 호스트의 모든 파일에 접속할 수가 있다.
  • 클라이언트가 웹 서버를 통해 접속하는 웹 환경에서는, 사용자는 LOAD DATA LOCAL을 사용해서 웹 서버 프로세스가 읽을 수 있는 모든 파일을 읽을 수가 있게 된다 (사용자가 SQL 서버에 대응하는 명령문을 구동 시킨다고 가정한다). 이러한 환경에서는, MySQL 서버와 관련된 클라이언트는 웹 서버가 되는 것이지, 웹 서버에 접속 한 사용자가 구동하는 리모트 프로그램이 되는 것은 아니다.

이러한 문제를 다루기 위해서, 우리는 LOAD DATA LOCAL 처리 방법을 다음과 같이 변경하였다:


  • 디폴트로, 바이너리 배포판에 있는 모든 MySQL 클라이언트와 라이브러리는 --enable-local-infile 옵션을 가지고 컴파일 되며, MySQL 3.23.48 및 이전 버전과 호환이 되도록 하였다.
  • 소스를 가지고 MySQL은 구축하였지만 --enable-local-infile 옵션을 사용해서 configure를 실행하지 않았다면, LOAD DATA LOCAL이 mysql_options(... MYSQL_OPT_LOCAL_INFILE, 0)를 실행할 수 있도록 명확하게 작성되지 않는 한 어떤 클라이언트도 LOAD DATA LOCAL을 사용할 수 없도록 만들었다. Section 22.2.3.48, “mysql_options()”을 참조.
  • 여러분은 mysqld를 --local-infile=0 옵션과 함께 시작 함으로서 서버 측면에서 모든 LOAD DATA LOCAL 명령어를 비활성화 시킬 수가 있다.
  • mysql 명령어-라인 클라이언트의 경우, LOAD DATA LOCAL은 --local-infile[=1] 옵션을 지정해서 활성화 시키거나, 또는, --local-infile=0 옵션을 가지고 비활성화 시킬 수가 있다. 비슷하게, mysqlimport의 경우, --local 또는 -L 옵션은 로컬 데이터 파일 로딩을 활성화 시킬 수가 있다. 어떤 경우에서건, 성공적인 로컬 로딩 동작은 서버가 이를 허용하도록 활성화 되어 있어야 한다.
  • 만약에 여러분이 Perl 스크립트 또는 옵션 파일에서 [client] 그룹을 읽는 다른 프로그램에서 LOAD DATA LOCAL을 사용한다면, local-infile=1 옵션을 그 그룹에 추가할 수가 있다. 하지만, local-infile을 이해하지 못하는 프로그램에서 문제가 발생하지 않도록 하기 위해서는, loose- 접두사를 사용해서 그것을 지정하도록 한다:
    [client]
    loose-local-infile=1

  • 만약에 서버 또는 클라이언트 중의 한 곳에서 LOAD DATA LOCAL INFILE이 비활성화 되었다면, 그러한 명령문을 입력하고자 하는 클라이언트에는 다음과 같은 에러 메시지를 발생한다:
    ERROR 1148: The used command is not allowed with this MySQL version

일반 사용자 자격으로 MySQL 구동시키는 방법

윈도우 시스템의 경우, 여러분은 일반 사용자 계정을 사용해서 MySQL 서버를 윈도우 서비스 형태로 구동 시킬 수가 있다.

유닉스 시스템의 경우에는, MySQL 서버 mysqld는 모든 사용자가 시작 및 구동을 시킬 수가 있다. 하지만, 보안상의 이유로, 여러분은 서버를 유닉스 root 사용자로 구동 시키지는 말도록 한다. mysqld를 권한이 없는 일반 유닉스 사용자 user_name가 구동할 수 있도록 변경하기 위해서는, 아래의 사항을 따르기 바란다:


  1. 서버가 구동 중이라면, 종료를 시킨다 (mysqladmin shutdown를 사용).
  2. 데이터 베이스 디렉토리 및 파일을 변경해서 user_name가 읽기 및 쓰기 권한을 가지도록 변경한다 (이 작업은 유닉스 root 사용자의 자격으로 해야 함):
    shell> chown -R user_name /path/to/mysql/datadir
    만약에 여러분이 이렇게 하지 않으면, user_name 자격으로 서버를 구동 시킬 때 서버는 데이터 베이스 또는 테이블에 접근을 하지 못하게 된다.
    만약에 MySQL 데이터 디렉토리 안에 있는 디렉토리 또는 파일이 심볼릭 링크라면, 그 링크를 따라야 하고 링크가 가리키는 디렉토리와 파일도 변경 해야 한다. chown -R은 여러분을 위한 심볼릭 링크를 따르지 않을 것이다.
  3. 사용자user_name로 서버를 시작한다. 만약에 여러분이 MySQL 3.22 또는 그 이후 버전을 사용한다면, mysqld를 유닉스 root 사용자로 시작하고 --user=user_name 옵션을 사용하는 것도 한가지 방법이다. mysqld 를 시작한 후에는 다른 연결을 받아들이기 전에 유닉스 사용자 user_name로 구동을 변경한다.
  4. 시스템 스타트업 시점에 지정한 사용자로 서버를 자동으로 시작하기 위해서는, user 옵션을 /etc/my.cnf 옵션 파일의 [mysqld] 그룹 또는 서버의 데이터 디렉토리에 있는 my.cnf 옵션 파일에 추가해서 사용자 이름 지정한다. 예를 들면:
    [mysqld]
    user=user_name

출처 : MySQL 코리아