2016년 11월 22일 화요일

MySQL의 삭제된 레코드 복구 실습


[Tech Report MySQL Forensics] 저장방식에 대한 ‘이해’가 데이터 복구의 ‘시작’이다



  • AhnLab


  • 2014-03-03

지난 2013년 12월호부터 월간 안을 통해 세 차례에 걸쳐 InnoDB와 MyISAM의 데이터 파일 구조 분석과 삭제된 레코드의 복구 방안에 대해 알아보았다. 이번에는 예제를 통해 삭제된 데이터에 대한 접근과 삭제된 데이터를 복구해가는 실제 과정을 살펴볼 예정이다. 이에 앞서 InnoDB와 MyISAM의 구조와 관련해 중요한 부분을 다시 한 번 살펴보고 각 저장방식의 구조적인 특징을 바탕으로 삭제된 레코드를 실제로 복구하는 과정을 알아본다. 테스트 환경을 구성하여 삭제된 레코드에 대해 실제로 복구 과정을 수행함으로써 MySQL이 DB 파일에 데이터를 어떻게 저장하고 사용하는지 데이터 저장의 흐름을 파악할 수 있기 때문에 분석가에게는 꼭 필요한 과정이라 할 수 있다.

 

<연재 목차>

1부_MySQL의 현황 및 포렌식적 의미(2013년 12월 호)

2부_MySQL InnoDB Type의 데이터 파일 구조 분석과 삭제된 레코드의 복구 방안(2014년 1월 호)

3부_MySQL MyISAM Type의 데이터 파일 구조 분석과 삭제된 레코드의 복구 방안(2014년 2월 호)

4부_MySQL의 삭제된 레코드 복구 실습(이번 호)

 

MySQL을 사용하는 대부분의 시스템은 InnoDB 또는 MyISAM 저장방식을 사용하여 데이터를 저장한다. 각각의 저장방식은 구조적으로 많은 차이가 있으나 데이터를 저장하는 전체적인 흐름은 비슷하다. 따라서 InnoDB 또는 MyISAM 저장방식 중 한가지 저장방식만 이해하고 있으면 대부분의 MySQL 저장방식의 메커니즘을 파악할 수 있다. 저장방식에 대한 메커니즘과 데이터 저장의 흐름에 대한 이해가 있어야만 DB로부터 데이터를 추출할 때 실제 데이터가 저장된 위치에 쉽게 접근할 수 있다. 특히 삭제된 레코드의 복구는 실제 데이터가 존재하는 위치로의 접근을 통해 이루어지기 때문에 저장방식에 대한 메커니즘 파악은 중요한 의미를 갖는다. 이미 월간 안을 통해 여러 차례 디지털 포렌식의 관점에서 삭제된 레코드 복구의 중요성에 대해 충분히 설명했기 때문에 이 글에서는 다시 부연하지 않기로 하겠다.

 

InnoDB의 구조적 특징 및 복구



InnoDB는 MySQL 설치 시 설정된 데이터 저장경로에 ibdata 파일을 생성하여 데이터를 저장하고 관리한다. ibdata 파일은 크게 시스템 테이블 영역과 트랜잭션 영역, 데이터 영역으로 나뉘며 각각의 영역은 16,384 바이트 크기의 블록 단위로 구성되어 있다. 시스템 테이블 영역은 데이터베이스 관련 정보와 테이블 스키마 등의 정보를 갖게 되고 데이터 영역은 데이터가 실제로 저장되는 영역이다. ibdata 파일 내의 모든 블록은 파일 헤더(FilHeader)와 페이지 헤더(PageHeader), 그리고 각 블록이 포함하고 있는 레코드(Record)와 레코드 속성 헤더를 포함한 형태로 구성되어 있다.

 

 

[표 1] 파일 헤더(FilHeader)의 주요 항목(출처: MySQL, http://dev.mysql.com/doc/internals/)

 

 

 

[표 2] 페이지 헤더(PageHeader)의 주요 항목(출처: MySQL, http://dev.mysql.com/doc/internals/)

 

 

 

[표 3] 리던던트 포맷(Redundant Format)의 주요 속성 정보

(출처: MySQL, http://dev.mysql.com/doc/internals/)

 

 

[표 1]과 [표 2]는 주요 파일 헤더와 페이지 헤더로, 삭제된 레코드를 복구하는데 꼭 필요한 항목이다. [표 3]은 각 블록마다 여러 개로 존재할 수 있는 레코드들에 대한 헤더 값으로, 각 레코드들을 연결하는 역할을 한다. 

 




[그림 1] 시스템 테이블 영역의 블록 일부

 

 

[그림 1]은 시스템 테이블에 저장된 블록의 일부다. [그림 1]을 통해 이 블록은 FIL_PAGE_SPACE 값을 통하여 0x3AEEF9EE 값의 ID를 가지고 있음을 확인할 수 있다. FIL_PAGE_PREV와 FIL_PAGE_NEXT는 이 블록과 연결되어 있는 블록이 있는지를 나타내며, 0xFFFFFFFF 값을 갖고 있을 경우에는 연결되는 페이지가 없음을 의미한다. 따라서 [그림 1]의 블록은 현재 앞뒤로 연결되어 있는 블록이 없다.




블록과 블록이 연결되는 이유는 하나의 블록에 많은 데이터가 저장되어 더 이상 저장할 공간이 없는 경우 새로운 블록을 연결함으로써 데이터를 이어서 저장하기 위함이다. 이때 기존 블록은 FIL_PAGE_NEXT에 새로운 블록의 블록 번호를 입력하고, 새로운 블록은 FIL_PAGE_PREV에 기존 블록의 번호를 입력하여 저장한다.




각 블록은 FIL_PAGE_OFFSET에 현재 블록이 0번째부터 몇 번째에 위치하는지에 대한 고유한 값을 갖고 있으며 FIL_PAGE_PREV와 FIL_PAGE_NEXT는 연결된 블록 번호를 저장한다. FIL_PAGE_TYPE은 현재 블록의 타입을 정의하며, 0x45BF를 갖고 있으면 실제 데이터를 가지고 있는 Leaf 노드에 해당한다. 삭제된 데이터를 복구할 때는 바로 이 0x45BF를 갖고 있는 블록이 필요하다.




PAGE_FREE는 현재 블록에서 첫 번째로 삭제된 레코드의 위치를 나타낸다. [그림 1]의 PAGE_FREE는 0x0000 이므로 현재 삭제된 레코드가 없음을 알 수 있다.

 




[그림 2] 레코드와 레코드의 연결

 

 

[그림 2]와 같이 파일 헤더와 페이지 헤더가 모두 나타나면 innoDB의 특징인 스타트 마커(Start Maker) “인피멈(infimum)”이 나타난다. 인피멈은 그 자체로 레코드이면서 모든 블록의 첫 번째 레코드가 된다. 즉, 인피멈 또한 레코드이기 때문에 앞의 6 바이트는 리던던트(Redundant) 방식의 레코드 헤더가 된다. 참고로, MySQL의 버전과 상관없이 시스템 테이블의 모든 레코드는 리던던트 방식을 취한다. [그림 2]에 나타난 바와 같이 이 파일의 인피멈 레코드 헤더는 0x01 00 00 03 00 8D 이다.




첫 번째 숫자인 0을 2진수로 표현하면 0000이며, 이 중 3번째 0은 현재 레코드의 삭제 여부를 나타낸다([표 3] 참조). 0은 정상 레코드임을 나타내기 때문에 현재 레코드는 정상 레코드이다. 레코드 헤더의 마지막 2 바이트에 해당하는 0x00 8D는 다음에 연결된 레코드의 오프셋을 나타낸다. 따라서 0x00 8D위치로 이동하면 “SYS_FOREIGN”이라는 다음 레코드를 만날 수 있다.



이와 같이 모든 레코드는 레코드 헤더의 속성을 통해 모두 연결되어 있으며, 계속해서 연결을 찾아가면 블록에 존재하는 모든 레코드에 접근할 수 있다. 단, 정상 레코드들은 모두 정상 레코드와 연결되며 삭제된 레코드들은 오직 삭제된 레코드와 연결된다. 모든 연결이 끝나면 정상 레코드는 “서프리멈(supremum)”이라는 엔드 마커(End Maker)로 이동하게 된 후 레코드 탐색을 종료한다.

 

[표 4] 콤팩트 포맷의 주요 속성 정보(출처: MySQL, http://dev.mysql.com/doc/internals/)

 

월간 안 지난 호에서 설명한 바와 같이 MySQL 버전 5.0 이상부터는 데이터 영역의 레코드 헤더가 콤팩트 포맷으로 나타난다. 리던던트 포맷과는 다소의 차이가 있기 때문에 삭제된 레코드의 복구 시 이점을 유의할 필요가 있다.



 


결국 삭제된 레코드를 복구하기 위해서는 PAGE_FREE를 이용하여 제일 처음에 삭제된 레코드의 위치에 접근한 후 리던던트 포맷 또는 콤팩트 포맷의 속성 정보를 통해 다음에 삭제된 레코드를 계속 찾아내야 한다.

 

 

MyISAM의 구조적 특징 및 복구

 


MyISAM은 테이블의 구조 정보와 인덱스 정보, 테이블 내부에 저장될 데이터를 각각 정해진 파일에 분할하여 저장한다. 사용자 또는 시스템이 데이터베이스를 생성하는 경우, 설정된 저장경로에 데이터 베이스명의 디렉토리를 생성하며 테이블을 생성하면 테이블명의 MYD 파일과 MYI, FRM 파일을 동시 생성하여 데이터를 저장 및 관리한다. (월간 안 2월 호)



MYD 파일은 크게 Fixed 포맷과 Dynamic 포맷 두 가지로 구분되어 저장된다. 저장할 테이블이 가변길이를 위한 데이터 타입을 포함하는 경우 Dynamic 포맷으로 저장되고, 저장할 테이블이 고정길이 데이터 타입만을 포함하는 경우 Fixed 포맷으로 저장된다.

 


[표 5] 저장 방식에 따른 데이터 타입

 

이와 같이 테이블이 포함하는 데이터의 타입에 따라 포맷이 구분되기 때문에 테이블 정보를 포함하고 있는 FRM 파일의 분석을 통해 복구하고자 하는 테이블의 포맷을 확인해야 한다. 테이블의 포맷을 확인해 각 포맷의 특징에 맞게 복구를 진행해야 하기 때문이다. 레코드는 모두 MYD 파일에 저장되기 때문에 이 글에서는 MYD 파일에서의 복구를 예제로 실습을 진행해본다.

 

1. Fixed 포맷

 




[그림 3] Fixed 포맷의 MYD 파일

 

 

[그림 3]은 Fixed 포맷의 MYD 파일로, Fixed 포맷이기 때문에 하나의 레코드가 동일한 길이로 반복되고 있음을 알 수 있다. [그림 3]의 표시된 부분과 같이 레코드 헤더는 하나의 레코드가 시작되기 전 1바이트의 크기로 반복되어 나타난다. 레코드 헤더를 통해 현재 레코드의 상태를 확인할 수 있다.



[그림 3]의 첫 번째 레코드 헤더 값은 0xF1이며, 0xF1의 2진수 값(1111 0001) 중 마지막 비트인 1이 현재 레코드의 상태를 나타낸다. MyISAM은 innoDB와는 달리 마지막 비트가 1인 경우 현재 레코드가 정상 상태임을 의미하고 반대로 0인 경우에는 삭제된 레코드임을 의미한다.

 




[그림 4] 삭제된 레코드의 데이터 손실

 

[그림 4]의 경우, 두 번째 레코드 헤더 값 0x00을 통해 해당 레코드가 삭제되어 있음을 알 수 있다. MyISAM 저장방식은 레코드가 삭제된 경우 레코드 헤더 값 이후에 다음으로 삭제된 레코드의 오프셋 위치를 추가로 저장한다. 기존에 저장되어 있던 원본 데이터가 다음 삭제된 레코드와 관련된 데이터로 덮어 쓰여짐에 따라 일부 데이터의 손실이 발생한다. 또한 레코드를 삭제한 후 새로운 레코드가 추가 되었을 경우 삭제된 레코드 영역에 새로운 레코드가 덮어 쓰여지기 때문에 손상되었던 데이터 외에 일부 데이터도 복구할 수 없는 경우가 발생할 수 있다. MyISAM 저장방식의 경우 완전한 복구는 사실상 불가능하다는 점을 인지하고 있어야 한다.

 

 



[표 6] Fixed 포맷의 헤더 구조

 

MyISAM 저장 방식 Fixed 포맷의 헤더는 [표 6]과 같이 테이블이 갖고 있는 필드의 개수에 따라 헤더의 길이가 변경될 수 있다. 따라서 해당 테이블에 대한 정보를 명확하게 파악한 뒤 복구를 시작해야 한다.

 

2. Dynamic 포맷

 

[그림 5] Dynamic 포맷의 MYD 파일

 

[표 7] Dynamic 포맷의 헤더 구조

 

[그림 5]는 Dynamic 포맷으로 저장된 MYD 파일로, 총 4개의 레코드가 저장되어 있음을 알 수 있다. 각각의 레코드는 6 바이트의 헤더 값을 갖고 있다. 헤더 값에 대한 정의는 [표 7]을 참고하여 파악할 수 있다. Fixed 포맷과 마찬가지로 Dynamic 포맷 역시 테이블이 포함하는 필드의 개수 및 데이터의 길이에 따라 헤더 값의 길이가 변할 수 있다. 따라서 데이터 복구 시 테이블 정보를 잘 파악하여 이용해야 한다.



[그림 5]의 경우, 두 번째 레코드가 삭제된 레코드임을 알 수 있다. 레코드가 삭제되었을 경우에는 처음 4바이트에는 삭제된 레코드의 크기 값이 저장되어 있다. 또한 다음 8바이트에는 이전에 삭제된 레코드의 오프셋이 나오고 그 다음 8바이트에는 다음에 삭제된 레코드의 오프셋이 나온다. Dynamic 포맷도 Fixed 포맷과 마찬가지로 레코드가 삭제되면 20바이트 정도의 크기를 다른 값으로 덮어 쓰기 때문에 완전한 복구는 불가능하다.

 

실습을 통해 데이터 복구 방법론을 확보할 수 있어



월간 안 연재를 통해 MySQL의 이론적인 내용을 살펴본 것에 이어 이번에는 삭제된 데이터를 실제로 어떻게 복구할 수 있는지에 대한 방법에 대해 알아보았다. 대부분의 데이터베이스는 MySQL과 비슷한 방식의 메커니즘을 통하여 데이터를 저장하고 관리하기 때문에 실습을 통해 복구 과정을 숙지하면 다른 데이터베이스의 경우에도 구조의 파악만으로도 쉽게 데이터 복구를 위한 방법론을 완성할 수 있다. 이러한 복구 방안을 자동화하여 보다 빠르고 정확한 복구를 가능하게 한다면 수많은 다양한 데이터베이스들의 삭제된 레코드의 복구가 가능할 것이다. 특히 디지털 포렌식 관점에서는 다양한 데이터베이스의 복구 연구와 함께 데이터베이스의 삭제된 레코드를 통합적으로 복구할 수 있는 방안에 대해서 끊임없이 연구할 필요가 있겠다.@










  • AhnLab 로고



  • 클라우드분석팀 남궁재웅 연구원




이 정보에 대한 저작권은 AhnLab에 있으며 무단 사용 및 도용을 금합니다.

단, 개인이 비상업적인 목적으로 일부 내용을 사용하는 것은 허용하고 있으나, 이 경우 반드시 출처가 AhnLab임을 밝혀야 합니다.

기업이 이 정보를 사용할 때에는 반드시 AhnLab 의 허가를 받아야 하며, 허가 없이 정보를 이용할 경우 저작권 침해로 간주되어 법적인 제재를 받을 수 있습니다. 자세한 내용은 컨텐츠 이용약관을 참고하시기 바랍니다.

정보 이용 문의 : contents@ahnlab.com


홈페이지 jQuery 라이브러리에서 CVE-2019-11358 취약점 패치 여부 확인 방법

현재 홈페이지에서 사용 중인 jQuery 라이브러리가 CVE-2019-11358 취약점 패치를 적용했는지 확인하는 방법은 다음과 같습니다. 1. jQuery 버전 확인 홈페이지 소스 코드를 확인하여 jQuery 라이브러리 버전을 직접 확인합니다. 웹 ...