팁/튜토리얼

요새 쿠팡같이 여러 웹 서비스 업체에서 DB 데이터가 탈취되는 사례들이 많이 보고되고 있습니다.

 

내부자를 통해서 유출되는 케이스도 있지만, 이 커뮤니티를 이용하시는 분들은 대부분 내부자보다는 외부 침입으로 인한 해킹 위협이 가장 확률이 높을 것입니다.

 

이메일이나 실명같이 일반적인 계정 데이터도 노출되면 위험하지만, 만일 휴대폰 인증을 통한 CI 데이터나 주민번호같이 매우 민감한 정보를 담고있는 경우, DB 접근을 제한한다고 해도 해킹을 통해 단번에 노출될 위험성이 존재합니다.

 

 

 

그렇다면 이 문제를 어떤 식으로 해결할 수 있을까요?

 

방식은 다양하겠지만, 가장 간단하게 해결할 수 있는건 DB에 데이터를 암호화 해서 저장하면 됩니다.

 

예시를 들자면 010-1234-5678 이라는 데이터를 eBduaelwnd... 과 같이 알 수 없는 문자열로 치환해서 DB에 넣는 것이죠.

 

이 경우 라이믹스에서 데이터를 저장하고 불러올때만 치환해주는 작업을 진행하면 되고, 모종의 취약점을 통해 DB 정보가 탈취되더라도 해커는 eBduaelwnd... 와 같이 암호화된 정보만 볼 수 있게 되는 것이죠.

-> 방식에 따라서 DB 엔진 자체에서 암호화하는 방식도 있습니다. 다만 별도의 솔루션을 사용해야해서 설명하지는 않겠습니다.

 

라이믹스 Rhymix\Framework\Security 클래스에 기본적인 encrypt, decrypt 함수가 마련되어있으며, 해당 함수를 활용해서 암호화할 수 있습니다.

 

+) 찾아보니 옛날에 기진곰님이 제작해두신 암호화 모듈도 있는 것 같습니다. (최신 라이믹스에서도 동작하는지는 테스트를 안해봐서 모르겠네요?)

 

 

 

다만 위 방식도 서버에 키를 저장한다는 단점이 존재합니다.

 

모종의 이유로 서버에 저장한 키까지 털려버린다면? 골치 아파지죠..

 

이런 문제때문에 보안업계에서는 하드웨어 보안 모듈 (HSM)이라는 장비를 사용합니다.

-> 윈도우 11 설치할때 필수적으로 요구했던 TPM 이라는 장비가 HSM의 일종에 속합니다.

 

별도의 장치에 키를 저장해두고, API를 통해서 eBduaelwnd... 와 같이 암호화된 데이터를 넘기면, HSM 장비에서 복호화 후 010-1234-5678 이라는 데이터를 넘겨준다고 보시면 됩니다.

 

사이트 운영자가 암호화 키에 대해 신경쓰지 않아도 되고, 서버가 탈취되더라도 HSM 장치에 접근할 수 없다면 (혹은 HSM 장비에서 키를 폐기한다면) 데이터를 복구할 수단이 사라지게 됩니다.

 

 

 

돈 많고 보안에 신경을 써야하는 IT 사업체에서는 비싼 HSM 장비를 구매해서 물리 서버에 부착해 사용하면 됩니다.

 

다만 영세한 업체(혹은 커뮤니티)는 더 싼 방법을 찾아서 보안을 강화하고 싶을지도 모릅니다.

혹은 클라우드를 사용하고 있어서 물리 HSM 장비를 사용하지 못할 수도 있습니다.

 

이 경우 저는 동일한 기능을 제공하는 AWS KMS를 사용합니다.

>> 26년 1월 기준) 마스터키 하나당 월 $1에 이용 가능하며, 1만 건의 암/복호화 호출 당 $0.03이 청구됩니다.

 


 

AWS KMS를 사용하기 위해서는 AWS Cli를 서버에 세팅하고, composer를 통해 SDK를 설치해야 합니다.

>> https://aws.amazon.com/ko/cli/

>> https://docs.aws.amazon.com/ko_kr/sdk-for-php/v3/developer-guide/getting-started_index.html

 

다만 세팅 과정은 업데이트될 때마다 변경되고, 사용환경에 따라 세팅 방법이 달라 이 게시글에서 설명하지는 않겠습니다.

SDK 설치는 개발하시는 모듈 하위 폴더에서 composer require 명령어를 사용하시면 됩니다.

 

세팅이 완료되었다면 아래와 같이 사용할 수 있게 됩니다.

 


파일명 : modules/example/controllers/Admin.php

<?php

namespace Rhymix\Modules\Example\Controllers;

require '../vendor/autoload.php';

use Aws\Kms\KmsClient;
use Aws\Exception\AwsException;

class Admin extends Base
{
    private static $kmsClient = null;
    private const KEY_ID = 'arn:aws:kms:ap-northeast-2:123456789012:key/EXAMPLE-GUID-1234-5678-9012-EXAMPLEKEY';

    public function init()
    {
        $this->setTemplatePath($this->module_path . 'views/admin/');
        self::$kmsClient = new KmsClient([
            'version' => 'latest',
            'region'  => 'ap-northeast-2',
        ]);
    }

    public function dispExampleAdminConfig()
    {
        $plainText = "010-1234-5678"; // 암호화할 평문 데이터

        try {
            // -------------------------
            // Encrypt
            // -------------------------
            $encryptResult = self::$kmsClient->encrypt([
                'KeyId'     => self::KEY_ID,
                'Plaintext' => $plainText,
            ]);

            $cipherBlob = $encryptResult['CiphertextBlob'];
            $cipherTextBase64 = base64_encode($cipherBlob); // 암호화된 데이터: eBduaelwnd...

            // -------------------------
            // Decrypt
            // -------------------------
            $decryptResult = self::$kmsClient->decrypt([
                'CiphertextBlob' => base64_decode($cipherTextBase64),
            ]);

            $decryptedText = $decryptResult['Plaintext']; // 복호화된 데이터: 010-1234-5678
        } catch (AwsException $e) {
            return new BaseObject(-1, "KMS Error: " . $e->getAwsErrorMessage());
        }

        ...
    }

    ...
}

 

코드는 복잡해보여도, 내용을 이해하면 단순합니다.

$kms->encrypt를 통해서 암호화(010-1234-5678 -> eBduaelwnd...)를 합니다.

$kms->decrypt를 통해서 복호화(eBduaelwnd... -> 010-1234-5678)를 합니다.

 

다만 위 방식을 사용하면 특정 길이를 넘는 데이터를 암호화 할 수 없고, 호출시마다 비용이 부과되기 때문에 비용 부담이 많아질 수 있다는 단점이 있습니다.

 

해서 저는 row 단위로 저장하는 데이터는 row마다 데이터 암호화키를 만들어서, 해당 키로 실제 데이터를 암호화합니다.

>> https://docs.aws.amazon.com/ko_kr/kms/latest/developerguide/data-keys.html

 

 

아래는 제가 실제로 사용하는 예시입니다.

 

image (2).png

 

DB툴을 활용해 조회하면, 암호화된 휴대폰 인증정보는 의미가 없는 문자열로만 구성되어있는 것을 볼 수 있습니다.

-> name, phone, birthday 항목

 

위 항목에서 decryption_key라는 항목이 눈에 띄는데요, 해당 값은 AWS KMS에서 발급받은 Data Key의 암호화된 값입니다.

 

KMS API를 통해서 원본 Data Key를 받아와야만 아래 암호화된 데이터들을 복호화할 수 있습니다.

 

데이터 row별로 별도의 암호화 키를 사용하는 효과로, 보안성이 더 높다고 알고 있습니다.

 

>> KMS로 암호화한 키가 아닌, 그냥 복호화키를 넣어두면 데이터 암호화의 의미가 없으니 주의하세요.

 

 

image.png

 

코드에서도 캡슐화를 진행하여 민감정보에 접근하는 것을 최소화하였습니다.

 

암호화된 데이터에 접근하기 전에는 굳이 복호화하지 않으며, 해당 데이터에 접근할 때 복호화를 진행합니다.

 

 

 

IT 보안 업계에서 한동안 근무했었다보니, 여러가지 주워들은게 많아서 한번 글을 써보았습니다.

 

필요하다면 AI의 도움을 받아서 민감한 데이터부터 차근차근 암호화해보시는게 어떨까요?

 

더 이상 사용하지 않는 민감 데이터가 있는지도 점검해보는 계기가 되었음 합니다.

리버스 Lv. 8
모듈만드는 대학생입니다.
https://potatosoft.kr

댓글 2

  • Rhymix에 접목되어야 할 부분중 하나라고 늘 생각 하고 있습니다. 

    시간날때 저도 한번  ai 붙들고 해봐야겠네요.. 

  • @똑띠

    작성해주신 댓글을 보고 본문에도 보충을 해두었습니다.

     

    Rhymix\Framework\Security 클래스를 보시면 기본적인 encrypt, decrypt 메소드가 제공되고 있습니다.

    일반적인 상황에서는 해당 함수를 이용하셔서 암/복호화하셔도 무방할 듯 싶습니다.

    >> 기본 암호화 키는 files/config/config.php 파일에 저장되지만, 해당 키만 노출되지 않는다면 KMS(HSM)와 효과는 동일합니다.

    물리적으로 키가 도난 당하는 것을 방지하는 솔루션이 KMS(HSM)라고 이해해주세요.

     

    물론 본문에서 AWS KMS 활용을 소개한만큼, AI를 통해서 괜찮은 방법을 찾아 민감정보를 암호화해보시는 것도 좋습니다.