SQLCipher로 SQLite3 암/복호화

Jeongkuk Seo
sjk5766
Published in
18 min readSep 29, 2018

SQLite3 DB를 암/복호화 하는 방법Node에서 암호화 된 DB에 접근해 데이터를 만지는 방법을 정리하겠습니다. (삽질이 많이 들어가 있습니다.)

회사에서 개발중인 제품의 내부 자산 보호를 위해 사용하던 SQLite DB를 암호화 할 필요가 있었습니다. npm sqlite3 페이지에서 암호화 하는 링크가 있었습니다. (구글에 npm sqlite3 검색하면 아래 페이지가 검색됩니다.)

아래 링크는, 위 화면에서 노란색으로 표시한 SQLCipher extension 링크를 클릭하면 나오는 페이지입니다.

libsqlcipher.so 생성

여기서 만들 libsqlcipher 라이브러리는 SQLite DB를 암/복호화하는데 사용되는 라이브러리입니다.

우선 아래 명령어로 sqlcipher 소스를 받습니다.

git clone https://github.com/sqlcipher/sqlcipher.git

다음 명령어를 실행시키기 전에 crypto 라이브러리 위치를 찾아야 합니다. 검색하니 제 환경에는 /usr/lib64 디렉토리에 있었습니다. libcrypto.so 파일이 없어 심볼릭 링크로 libcrypto.so 파일을 생성 했습니다.

// 크립토 라이브러리 위치 확인
$ sudo find /usr -name "libcrypt*"
/usr/lib64/libcrypto.so.10
/usr/lib64/libcrypto.so.1.0.1e
// 심볼릭 링크 생성
$cd /usr/lib64
$sudo ln -s libcrypto.so.1.0.1e libcrypto.so
// LDFLAGS 값을 환경에 맞게 변경하여 configure 명령어 실행
./configure — enable-tempstore=yes CFLAGS=”-DSQLITE_HAS_CODEC” LDFLAGS=”-L/usr/lib64 -lcrypto”

make 명령어를 실행하니 에러가 발생합니다. tclsh 명령어를 찾을 수 없다네요.

설치합시다.

yum install tcl
yum install tcl-devel

다시 make를 입력합니다. 에러가 뜹니다. openssl/rand.h 파일을 찾을 수 없다고 합니다. find / -name rand.h 명령어로 검색해보니 파일이 정말 없네요. openssl 을 설치했습니다.

sudo wget https://www.openssl.org/source/openssl-1.0.2e.tar.gz
sudo tar -zxvf openssl-1.0.2e.tar.gz
cd openssl-1.0.2e
sudo ./config --prefix=/usr --openssldir=/usr/local/openssl shared
sudo make
sudo make install

다시 configure 명령어와 make 명령어를 입력합니다.

./configure — enable-tempstore=yes CFLAGS=”-DSQLITE_HAS_CODEC” LDFLAGS=”-L/usr/lib64 -lcrypto”make

메시지에 에러가 없는 걸로 보아 성공한 것 같습니다. .libs 폴더로 들어가면 아래와 같이 libsqlcipher 라이브러리들이 생성되어 있습니다.

링크 파일 말고 원본 libsqlcipher.so.0.8.6 파일을 /usr/lib64로 옮깁니다. 시스템 환경에 따라 /usr/local/lib 등으로 옮기시면 됩니다. 제 경우엔 library들이 /usr/lib64 경로에 있었습니다. 원본 라이브러리에 링크를 생성합니다.

sudo ln -s libsqlcipher.so.0.8.6 libsqlcipher.so

libsqlcipher를 사용할 수 있도록 리눅스 라이브러리 PATH를 설정합니다.

export LD_LIBRARY_PATH=/usr/lib64

sqlcipher로 암호화 된 DB 생성하기

위에서 libsqlcipher.so 파일을 생성했습니다. .libs 폴더를 다시 보시면 sqlcipher 라는 실행 프로그램이 있습니다.

sqlcipher를 암호화 된 DB를 만들 작업 디렉토리로 옮깁니다.

[centos@.libs]$ pwd
/usr/sqlcipher/.libs
[centos@.libs]$ cp sqlcipher /home/centos/sql-encrypt/
[centos@.libs]$ cd /home/centos/sql-encrypt/

아래는 npm init 명령으로 작업을 해두고 sqlcipher를 옮긴 제 환경입니다. 여기서 작업을 하겠습니다.

sqlcipher로 SQLite DB를 엽니다. 에러가 납니다.

[centos@ip-172–31–19–210 sql-encrypt]$ ./sqlcipher test.dbb
./sqlcipher: error while loading shared libraries: libsqlcipher.so.0: cannot ope n shared object file: No such file or directory

sqlcipher가 내부적으로 libsqlcipher.so.0 파일을 참조하나 봅니다. 라이브러리가 있는 폴더로 가서 심볼릭 링크를 생성합니다.

[centos@sql-encrypt]$ cd /usr/lib64
[centos@lib64]$ sudo ln -s libsqlcipher.so.0.8.6 libsqlcipher.so.0

다시 작업하던 디렉토리로 돌아와 동일한 명령어를 입력합니다. test라는 table을 만들고 종료합니다. 이렇게 만들어진 DB는 아직 암호화 되기 전이라서 sqlite3 명령으로 스키마 확인이 가능합니다.

[centos@ip-172–31–19–210 sql-encrypt]$ ./sqlcipher test.dbb
SQLCipher version 3.20.1 2017–08–24 16:21:36
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite> create table test(id int);
sqlite> .quit
[centos@ip-172–31–19–210 sql-encrypt]$ sqlite3 test.dbb “.s”
CREATE TABLE test(id int);

이번엔 DB 암호화를 해보겠습니다. 동일하게 sqlcipher로 test.dbb를 만들고 아래와 같이 입력합니다. pragma key=’암호화에 설정할 key’; 이 명령을 입력하고 test 테이블을 생성합니다. 다시 sqlite3로 db를 열어보면 암호화 되어 있다고 표시 됩니다.

[centos@ip-172–31–19–210 sql-encrypt]$ rm test.dbb
[centos@ip-172–31–19–210 sql-encrypt]$ ./sqlcipher test.dbb
SQLCipher version 3.20.1 2017–08–24 16:21:36
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite> pragma key=’testKey’;
sqlite> create table test(id int);
sqlite> .quit
[centos@ip-172–31–19–210 sql-encrypt]$ sqlite3 test.dbb “.s”
Error: file is encrypted or is not a database

위 처럼 만들어진 암호화 된 DB는 기존 Linux 명령어로 제공하는 sqlite3 명령어로는 열 수가 없습니다. 데이터를 삽입하기 위해 다시 sqlicipher로 DB를 열고 key를 설정 한 뒤 데이터를 넣어보겠습니다. test.dbb는 이미 암호화 되어 있으므로 pragma key로 복호화를 하지 않으면 insert가 실패합니다.

[centos@ip-172–31–19–210 sql-encrypt]$ ./sqlcipher test.dbb
SQLCipher version 3.20.1 2017–08–24 16:21:36
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite> insert into test values(1); // insert 시 실패
Error: file is not a database
sqlite> pragma key=’testKey’; // 암호화 시 설정한 key를 주고 복호화
sqlite> insert into test values(1);
sqlite> insert into test values(2);
sqlite> select * from test;
1
2
sqlite> .quit

SQLCipher로 암/복호화 과정을 정리해 보겠습니다.

  1. libsqlicipher.so 파일을 생성하고 리눅스가 사용하는 라이브러리 폴더에 옮긴 후, 다른 프로그램이 접근할 수 있도록 LD_LIBRARY_PATH를 설정
  2. libsqlicipher.so 파일과 같이 만들어진 sqlcipher로 SQLite3 DB를 만들고 pragma key로 암/복호화 할 수 있는 key를 설정
  3. 만들어진 암호화 된 DB에 작업을 수행하려면 암호화 시 사용한 key를 pragma key 명령어로 set해줘야 사용가능합니다.

아래부터는 Node에서 암호화 된 DB에 접근해 작업하는 방법을 다룹니다.

두 단계로 나뉘어져 있는데, 우선은 SQLcipher를 지원하는 sqlite3 모듈을 설치한 뒤, 소스 단에서 DB에 접근하는 법을 보겠습니다.

npm으로 sqlite3 설치

SQLite DB를 암호화 하기 위해 SQLCipher를 사용하는데, 이를 위해 npm 설치에 옵션을 추가해야 합니다.

npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/

AWS에 CentOS 구축한 곳에 테스트를 했는데 아래와 같은 에러가 뜹니다.

g++이 설치되어 있지 않다는 군요. 아래 명령어로 설치 하겠습니다.

sudo yum install -y gcc-c++

다시 위의 npm install 명령을 입력합니다. 근데 또 에러가 납니다.

이건, 설치 된 g++ 의 버전이 낮은 문제입니다. 버전이 4.4.7 이네요.

올려 보겠습니다. g++ 버전 업그레이드는 아래 링크를 참조하였고, 제 환경에서 그대로 따라하면 되지 않아 저 역시 정리 좀 하겠습니다.(https://sarc.io/index.php/miscellaneous/771-c-g-yum)

위 링크에는 아래와 같이 작업을 했는데 제 환경에서는 yum repo 정보를 추가하면 에러가 납니다. 아무래도 AWS에서 설치한 CentOS 설정 떄문 인 것 같습니다.

sudo curl http://linuxsoft.cern.ch/cern/scl/slc6-scl.repo > /etc/yum.repos.d/slc6-scl.repo

저는 아래와 같이 에러가 발생했습니다.

bash: /etc/yum.repos.d/slc6-scl.repo: Permission denied

구글링 하여 다음과 같이 변경했습니다.

sudo /bin/su -c 'curl http://linuxsoft.cern.ch/cern/scl/slc6-scl.repo > /etc/yum.repos.d/slc6-scl.repo'

vi로 /etc/yum.repos.d/slc6-scl.repo 파일을 열어보면 아래와 같이 정보들이 들어가 있습니다. 여기서 gpgcheck는 원래 값이 1이였습니다. 제가 0으로 변경했는데, 모든 gpgcheck 값을 0으로 변경하셔야 다음에 yum install에서 성공합니다.

[slc6-scl]
name=Scientific Linux CERN (SLC6) — SCL addons
baseurl=http://linuxsoft.cern.ch/cern/scl/slc6X/$basearch/yum/scl/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-cern
#uncomment next line if installing on a distribution different than SLC6
#gpgkey=http://linuxsoft.cern.ch/cern/scl/RPM-GPG-KEY-cern
gpgcheck=0
enabled=1
protect=1
.
.
.
.

값을 0으로 변경해서 정상적으로 저장했다면 아래처럼 패키지를 설치합니다.

yum install devtoolset-3

패키지 설치가 완료되면 아래 명령을 입력합니다.

scl enable devtoolset-3 bash

g++ 버전을 확인합니다. 4.9.1로 업그레이드 됨을 확인했습니다.

먼 길을 돌아왔네요. 다시 npm install 명령으로 sqlite 를 설치합니다. 스크롤 올리지 않게 다시 쓰겠습니다.

npm install sqlite3 --build-from-source --sqlite_libname=sqlcipher --sqlite=/usr/

될 거라 생각했지만 에러가 뜹니다. sqlite3.h 파일을 찾을 수 없다고 합니다.

리눅스에서 find 해보니 제가 작업했던 sqlcipher 폴더에 sqlite3.h 파일이 있었습니다.

[centos@sql-encrypt]$ find / -name sqlite3.h
/usr/sqlcipher/sqlite3.h

아래 링크에서 찾은 대로 빌드에 필요한 명령어를 설정했습니다.

CPPFLAGS, CXXFLAGS 에 sqlite3.h 파일이 있는 곳을 지정해줍니다. 다시 명령어를 입력하면 실패 메시지 없이 성공합니다.

$ export CPPFLAGS=”-I/usr/include -I/usr/sqlcipher”
$ export CXXFLAGS=”-I/usr/include -I/usr/sqlcipher”
$ npm install sqlite3 — build-from-source — sqlite_libname=sqlcipher — sqlite=/usr/

node_modules 하위에 sqlite3가 설치 된 것을 확인했습니다.

[centos@ip-172–31–19–210 sql-encrypt]$ cd node_modules/sqlite3/
[centos@ip-172–31–19–210 sqlite3]$ ll
total 56
-rw-rw-r — . 1 centos centos 1352 Jun 26 14:57 binding.gyp
drwxrwxr-x. 3 centos centos 4096 Sep 29 11:31 build
-rw-rw-r — . 1 centos centos 6080 Jul 9 02:08 CHANGELOG.md
-rw-rw-r — . 1 centos centos 2814 Jun 23 14:22 CONTRIBUTING.md
drwxrwxr-x. 2 centos centos 4096 Sep 29 11:31 deps
drwxrwxr-x. 3 centos centos 4096 Sep 29 11:31 lib
-rw-rw-r — . 1 centos centos 1460 Jun 23 14:22 LICENSE
-rw-rw-r — . 1 centos centos 3277 Sep 29 11:31 package.json
-rw-rw-r — . 1 centos centos 9576 Jun 26 15:01 README.md
-rw-rw-r — . 1 centos centos 43 Jun 23 14:22 sqlite3.js
drwxrwxr-x. 2 centos centos 4096 Sep 29 11:31 src

node에서 암호화 된 DB 다루기

이젠 Node 소스단에서 SQLite3 모듈을 사용해볼 차롑니다. 아래 소스를 분석 해 보겠습니다. 3번 line에서 test.dbb를 열고 4번 line에서 test 테이블을 생성하고 있습니다. 에러가 없다면 7번 line에서 test 테이블을 조회하고 있습니다.

실행 결과 입니다. test 테이블만 생성하고 데이터를 넣지 않았기에 빈 값으로 나옵니다. 정상입니다.

[centos@ip-172–31–19–210 sql-encrypt]$ node sql.js
[]

이 처럼, npm으로 SQLite3 모듈을 암호화 지원되도록 설치했지만 암호화 되지 않은 DB 역시 처리할 수 있습니다. (당연한 소리를 하나요 ㅎㅎ)

자, 그럼 암호화 된 DB를 생성하겠습니다. 기존의 test.dbb는 삭제해주세요. DB를 암호화 하기 위해 추가된 유일한 코드를 4번 line에 있습니다. db.run 함수를 호출 할 때, pragma key=’testKey’를 추가했습니다.

결과는 마찬가지로 test 테이블에 데이터가 없으므로 아래와 같습니다.

[centos@ip-172–31–19–210 sql-encrypt]$ node sql.js
[]

그럼 정말로 DB가 암호화 되었는지 확인해 보겠습니다. Node에서 생성한 test.dbb에 sqlite3로 접근하니 암호화 되었다고 나옵니다. 위에서 다뤘던 sqlicipher 툴로 test.dbb를 열어 select 하면 역시 암호화 되었다고 나옵니다. pragma key로, Node에서 설정한 암호화 key를 주고 select 하면 데이터가 없으므로 빈 값이 나오고 에러는 나지 않는걸 볼 수 있습니다.

[centos@ip-172–31–19–210 sql-encrypt]$ sqlite3 test.dbb “.s”
Error: file is encrypted or is not a database
[centos@ip-172–31–19–210 sql-encrypt]$
[centos@ip-172–31–19–210 sql-encrypt]$ ./sqlcipher test.dbb
SQLCipher version 3.20.1 2017–08–24 16:21:36
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
sqlite> select * from test;
Error: file is not a database
sqlite>
sqlite> pragma key=’testKey’;
sqlite> select * from test;
sqlite>

Node에서는 암호화 된 DB에 접근할 때 DB를 열고 pragma key 구문으로 암호화 된 key값만 전달해주면 별 다른 처리없이 정상적으로 사용할 수 있습니다.

--

--