제2의 비엔지니어 인생관을 꿈꾸며

Posted
Filed under MSSQL
● 데이터의 암호화  vs  난독화 

사용자로 부터 입력된 데이터는 DB 에 저장되고, 입력된 데이터는  누구나 볼 수 있는 문자(영문,한글포함) 숫자 형태로 저장되는게 일반적이고, 당연하다.

업무적인 처리(보안)을 위해 간혹 개인정보 또는 누구도 알수없는 정보로 저장되기위해서 암호화를 강행 하곤한다, (사실 암호환 한다는자체가 '정보' 로서의 의미 '상실' 이다 ) 

단지, 입력받은 변수의 값을 pwdcompare 함수로, 이미 암호화된 원본값과  비교하여 같은지에대한 구분이 , 최대한 우리가 얻을수 있는 정보(?) 인 셈이다.

비밀번호를  한발 양보 하여  암호화 처리된다고 하자, 우리도 어차피 관심밖의 데이터이다, 하지만  전화번호 , 주소  그 이상의 통계 또는 마케팅용등의 활용 가치가있는데이터라면, 이야기는 달라진다.

해킹을 안당하거나 데이터가 유출되지 말아야하는게 최선의 방법이지만, 적은 내부에도 있다. 나만보고 남은 못보는 방법 이라면 , 그나마 여러가지 방법중 난독화가 최선의 방법중 하나 아닐까 생각한다.

난독화란 말그대로 , 읽기 어렵게 할 뿐  암호화처럼 못읽게하는게 아니기때문에 완벽한 보안이 될수는 없다.
아래  장점과 단점에대해서 고민해보자,

 장점은 ①나만 보고, 다른사람은 보기 힘드게 한다는것이고(못보는것이 아니다) ② 난독화의 알고리즘을 내가 마음대로 만들어내어 적용할수있다. 단점은, 해독이 가능하기때문에, 누구든 그 패턴을 알면  해독이 가능해진다.(나도 볼 수 있다면 남도 볼 수 있다.) 단, 나는 봐야하는데이터 이기때문에 그만큼 복잡하게 해둘필요는있다 예를들면 전화번호 12자리 12byte의 난독화를 위해 아래 예제는 50byte를 소진했으나, 더 랜덤하고 더 복잡하게하여 100byte 200byte 를 사용한다면 그만큼 더 복잡해질수 있는 잇점이있다.



난독화 시나리오

'알고리즘' 이라고 이름 붙이기엔 너무 '단순해서' 시나리오로 붙여봤다, 포스팅을위해 심플하게 만들었으니 이해하자.

난독화 데이터길이는 총 50byte,  입력된 전화번호 신 11자리 (구 10자리) 를... 각각  랜덤위치로 위치시키되,전화번호는 자릿수는 1자리 즉, 1byte로 표기한다. 010이면 왔으면 '0' , '1' ,'0'  각각 1byte 씩 3byte 가 되겟다,  랜덤 자릿수는 2자리로 표기한다. 랜덤자리수 5가 나왔으면,  05로 기록한다. 다시 전화번호로 돌아와서, 10자리일경우 11자리로 만들고 마지막데이터를 더미로 채워 총 11자리로 만든다. 이때 전화번호 10자리의 경우 마지막숫자는없으므로, 자릿수를 '00' 으로 기록하기로 했다. 만약  난독화를 해독중 00 의 자릿수가나오면 그건 10자리 전화번호가 되겟다.

아래는 총 데이터 길이 구조이다. 


총 데이터 길이 : 50byte
---------------------------------------------------------
전화번호 11자리 :  11자리  = 11byte* 1byte = 11byte
위치정보 index  22자리 : 11개 * 2byte = 22byte
더미(가짜)전화번호 17자리 : 17개 * 1byte  = 17byte
------------------------------------------------------------
11byte + 22byte + 17byte = 50Byte


Create  function fn_EncTelTest(@gTel varchar(20))
        returns varchar(50)
as  Begin

-- 함수에 필요한 각종 변수 선언
declare @tel varchar(20) -- 전화번호 변수
declare @tel_len tinyint   -- 전화번호 길이변수
declare @tel_tmp tinyint -- 전화번호 1자 1자 자를때 @tel_len 만큼 잘랐는지 사용할 임시변수
declare @rnd_num tinyint -- 전화번호가 들어갈 자리 번째 랜덤수
declare @telNo char(1) -- 전화번호를 1자 1자 잘라서 가지고있을 변수
declare @telOrd varchar(50) -- 전화번호 1자 1자가 위치정보를 가지고있는 index 번호
declare @telInfo varchar(50) -- 전화번호 + 위치번호 + 더미데이터를 포함한 총 난독화된 정보 보유 
declare @dummyOrd varchar(50) -- 전화번호 + 위치번호를 제외한 더미데이터 위치 정보순서
declare @dummyNo tinyint -- 더미위치에 들어갈 가짜 숫자 
declare @chkOK tinyint -- 번호검증및 중복난수 체크
declare @chkOrd varchar(50) -- @telOrd를 @chkOrd 에 넣고 중복된 자리 index 가 없는지 조사할 임시 체크 index
declare @chkNum varchar(2)  -- @chkOrd 를 1개자리 잘라서 가지고있을 변수
-- 벨리데이션된 핸드폰 번호가 넘어왔따는 가정하에 공백과 하이픈 제거  숫자로만 10 or 11 로정리
set @tel = @gTel
set @tel = replace(replace(@tel , ' ' , '' ) , '-' , '')
set @telOrd = ''

-- 번호 검증 및 중복된 난수없이 모든게 완료되었다면 @chkOK 는 1 이다.
-- 50byte 로 전화번호와 전화번호가 있는 자리수를 
set @chkOK = 1  -- 1:OK 0:NO
set @tel_tmp = 1
set @tel_len = len(@tel)
set @telInfo = ''
-- 핸드폰번호라 10자리  또는 11자리 이어야 난독문자열생성
-- 단 ,10자리 이건 11자리이건 11자리 취급하여 자릿수를 만들고 10자리일겨웅 마지막 숫자를 더미로 생성
-- 010-000-0000 10자리 / 또는 010-0000-0000 11자리  '-' 제외한 자릿수
-- 정상적인 핸드폰 번호가 넘어왔는지 자릿수로 비교한다.
-- 참고로 function 안에서 mssql 에 random 함수를 사용할수 없다. v_rand_view 테이블 생성 
/*
       CREATE VIEW dbo.v_rand_view
       As
              Select Rand() as random_num
*/
IF len(@tel) = 10 or len(@tel) = 11 Begin

     -- 전화번호 자릿수만큼 돌면서 필요한 랜덤 위치를 생성한다.     
       while @tel_tmp <= @tel_len
       Begin

              SELECT  @rnd_num = convert(tinyint , (random_num*28) + 1)  FROM dbo.v_rand_view

           -- 총 50byte 에서 전화번호  22byte 를 빼면 28이니 난수가 28보다 크면안된다.
           -- 나머지는 전화번호 자릿수를 위해 index 로 사용된다.
       
      IF @rnd_num > 28   
                     set @rnd_num = 28
             IF @rnd_num  <=  0
                      set @rnd_num = 1

 
            Set @chkOrd = @telOrd

             -- 1개라도 만든 자릿수가 @chkOrd 에 있다면 지금 만들어진 index 자릿수가 기존에 있는 숫자아 
             --  같은지 비교해서 같은 자릿수에 중복으로 들어가면 전화번호가 사라지니,
             -- 1번이라도 쓴적이없는 index 가 돌도록 무한루프 중복된 index 가 없어야 종료
       
       While rtrim(ltrim((@chkOrd))) <> ''
               Begin
                     IF rtrim(ltrim((@chkOrd))) <> '' Begin
                            set @chkNum = replace(left(@chkOrd , 3) , ':' , '')
        
                     IF convert(tinyint , @chkNum) = @rnd_num  begin
                                      set @chkOk = 0
                                      break
                               End 
                               Else 
                               Begin
                                      set @chkOk = 1 
                                      set @chkOrd = substring(@chkOrd ,4,100)
                               End  
                    End  
               End  -- 안쪽 While 끝
          -- 중복된 검증된 수가 없다면, 랜덤수 멤버자격완료 2자리수로 만들어준다.
          -- 중복체크시 편하게하기위해 구분값을 : 로 임시로 활용한다.(차후에 모두제거)
        
               IF @chkOk =1 begin
                       Set @telOrd = @telOrd + 
                               Case when @tel_tmp > 1
                                      then ':'
                                      else ''
                                End
                                      +
                               Case when len(@rnd_num) = 1 
                                      then '0' + convert(char(1),@rnd_num)
                                      else convert(char(2) , @rnd_num)
                                End
                               Set @tel_tmp = @tel_tmp + 1   
                 End -- IF 끝
           End -- 최상 While 끝
   -- @telOrd 22자리 + : 10자리 = 32자리
  --  @telOrd 에 전화번호가 들어갈 자리 index 를 가지고있다.
   
    Set @telOrd = replace(@telOrd , ':' , '')

       IF len(@telOrd) < 22
              Set @telOrd = @telOrd + replicate( '0' , 22 - len(@telOrd) )

   
    Set @dummyOrd = @telOrd
       -- 랜덤생성된 자릿수에 전화번호를 채워 넣는다.
       -- 실제 DB에저장 데이터를 생성한다. 
       -- 0 이 아닌 숫자로 채워넣을것
       -- 전화번호 22byte 를 넣을 가짜위치 더미 28byte 생성
       Set @tel_len = 0

       while @tel_len < 28
       begin
              SELECT  @rnd_num = convert(tinyint , (random_num * 9) + 0)  FROM dbo.v_rand_view
              set @telInfo = @telInfo + convert(char(1) , @rnd_num)
              set @tel_len = @tel_len + 1
       End


     -- 실제 입력받은 전화번호를 하나하나 자르면서 length 가 0이될때까지 
     --  현재 실제위치가 들어갈 index 는 Set @dummyOrd = @telOrd 에  있다. (변수명이 좀 혼동된다.ㅠ)
       While len(@tel) > 0
       Begin
              Set @dummyNo = convert(tinyint , left(@dummyOrd , 2))
              Set @dummyOrd = substring(@dummyOrd ,3 , 100) -- 앞단 2byte 는 썼으니 버리고 나머지를 취한다.
              Set @telNo = left(@tel,1)  -- 전화번호를 1개씩 가져온다
              Set @tel = substring(@tel , 2, 100)

             --  @dummyOrd 위치에 전화번호 1개씩 위치 시킨다. 랜덤위치
              Set @telInfo = substring(@telInfo , 0 , @dummyNo ) + @telNo+substring(@telInfo , @dummyNo+1 , 200)
       End

     -- 최종적으로 앞단 28byte의 index 와 뒷단에 전화번호 22byte 를 위치시킨다.
       set @telInfo = @telOrd  +  @telInfo
End -- 최상위 IF 문 끝 
return @telInfo -- 50byte 짜리 난독화된 데이터 리턴
End -- 함수끝

간단하게 난독화 해보았다. 앞에 22자리는 전화번호가 위치한 각각 의 index 번호이고, 나머지 22 byte ~ 50byte 까지는 진자 전화번호와 더미 데이터가 섞여있다. 누군가가 유출된 난독화된 데이터를 보고 앞에 숫자가 왠지 전화번호의 위치일꺼라는것은 충분히 유추가능하다. 그러므로 전화번호의 위치를 앞에 나란히 11자리 넣기보다.

위치 index가 위치할 위치를 변경하거나 50byte 가아닌 100byte 200byte 로 늘려 위치정보 + 전화번호 + 더미데이터로 범벅해 놓는방법으로 활용해보자 위는 샘플이어서 간단하게 해보았다.
아래는 해독 시나리오 이다.


해독화 시나리오

해독화는 간단하다 위에서 앞 22자리가 전화번호의 위치데이터 이다, 앞에 22자리를 자른후 while 문을 돌면서 위치의 숫자만 가져오면간단하다. 23 ~ 50 번째까지는 전화번호와 dummy 번호가 섞여잇다. 

 
Alter function fn_DecTest (@telCode char(50))
 
returns varchar(15)
 
as Begin
 
-- 난독화 시킨 번호를 입력 받는다.
set @telCode = rtrim(ltrim(@telCode)) 
 
declare @gTel varchar(50)
declare @sTel_Info varchar(28)
declare @sTel_Number varchar(13)
declare @sDec  char(2)
declare @sDecPosition tinyint
declare @sDecOrd  char(22)
 
set @sTel_Number = ''
if len(@telCode) = 50 Begin
 
set @gTel = @telCode

-- 데이터의 앞 22byte 가 위치 정보를 가지고있다.
set @sDecOrd = left(@gTel,22)

               -- 나머지 23 ~ 50byte 까지는 실제 전화번호와 dummy 데이터라 섞여있다.
set @sTel_Info = substring(@gTel , 23, 28)
 
-- 위치정보를 가지고있는 @sDecOrd 를 앞에서 2개씩 잘라가면서 위치에 있는 번호를
-- 리턴할 전화번호 @sTel_Number 변수에 넣는다.

while len(@sDecOrd) > 0 
Begin
set @sDec = left(@sDecOrd , 2)
set @sDecOrd = substring(@sDecOrd , 3 , 50)
 
if @sDec > 0 Begin
set @sDecPosition = convert(tinyint , @sDec)
set @sTel_Number = @sTel_Number + substring(@sTel_Info, @sDecPosition,1)
End
 
End
              
               -- 추출된 전화번호를 자릿수에 맞게 '-' 하이픈 으로 구분하여 보기 좋게 리턴해준다.
set @sTel_Number = rtrim(ltrim(@sTel_Number))
 
IF len(@sTel_Number) = 10
set @sTel_Number = left(@sTel_Number ,3) + '-' +
                                                           substring(@sTel_Number , 4 , 3) + '-' + 
                                                            right(@sTel_Number,4)
 
IF len(@sTel_Number) = 11
set @sTel_Number = left(@sTel_Number ,3) + '-' +
                                                           substring(@sTel_Number , 4 , 4) + '-' +
                                                           right(@sTel_Number,4)
 
End
 
 
return @sTel_Number
 
End

단순해보이지만, 나름 보안으로 적용하기에는 가성비 효과가 좋다.
포스팅을 위해 작성된 쿼리 원문과 결과를  첨부하는것으로 이번 포스팅을 마치겟다.
사용자 삽입 이미지



























fn_EncTelTest.sql <다운로드>


fn_DecTelTest.sql <다운로드>
2018/11/29 17:40 2018/11/29 17:40