자바에서 키 보안을 위해 강제 메모리반환하는 방법이 있나요?
글쓴이: 일분일초 / 작성시간: 수, 2007/01/31 - 3:15오후
안녕하세요. 자바에대해 질문 하나만 할까 합니다.
아시다시피 자바에서는 메모리반환을 가비지콜렉터가 자동으로 알아서 해줍니다.
그런데 보안 프로그램 작성중 키값을 전역변수에 담아 놓고 사용한 후
강제로 메모리에 담긴 값을 없애고 싶습니다.
저는 이런 방법이 없는걸로 아는데 주변에 어떤분이 있다고 하시는군요....
과연 이런 방법이 있는지 궁금합니다...
이런 질문을 하게된 이유는...
가비지 콜렉터의 메모리 반환 시점이 자바 쓰레드 종료시점과 미세하게 틀릴 수 있기 때문에
그 약간의 시간차로 인한 메모리에 담겨있는 키가 노출되지 않나 하는 생각에 의해서였습니다....
위에서 적은 방법이 존재하지 않는다고 하더라도 이런 시간차에 의한 키의 노출이 일어날 여지가 있을지도 궁금합니다...
고수님들의 답변 부탁드립니다.
좋은 하루 되세요^^;;
Forums:
System.gc()를 부르면
System.gc()를 부르면 강제로 가비지컬랙터를 호출하기는 합니다.
System.gc()에서 return되었을 때 이미 object들이 사라지는지 아니면 조금 뒤에 사라지는지(sync or async) 잘 모르겠군요. 일반적으로 사라지는게 정상인데 spec상으로는 다르게 해석될 수 있을 지도 모르겠습니다.
Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
설명 봐서는 sync하게 동작하는 것 같은 느낌이긴 한데 몇몇 단어가 좀 아리송하군요. suggest라던가 effort라는 말이 왠지 반드시 해야만 한다라는 의미는 아니니 엄밀하게는 아무것도 안해도 된다로 해석될수도.. -_-;;
그리고 가비지컬랙트 되었더라도 memory를 강제로 JVM밖에서 dump하면 값을 얻어낼 수 있을 것으로 생각됩니다. 가비지컬랙트에 의존하기 보다 해당 메모리에 의미없는 값을 overwrite해서 정보를 보호하는 것이 더 현명한 방법일 듯 합니다.
Taeho Oh ( ohhara@postech.edu , ohhara@plus.or.kr ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Digital Media Professionals Inc. http://www.dmprof.com
Taeho Oh ( ohhara@postech.edu ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Alticast Corp. http://www.alticast.com
답글 감사합니다. 그리고...
해당 메모리에 의미 없는 값을 overwrite한다는건
해당 전역변수를 null처리 한다는 것인지요. null처리를 해도 메모리에는 남아있다고 알고 있어서요...
해당 전역변수를
해당 전역변수를 null처리 한다는 것은 object의 reference를 null로 세팅한다는 의미인 것으로 생각이 됩니다만 제가 말하고자 했던 의도는 object의 reference가 아닌 object안에 들어있는 data를 직접 의미없는 값으로 overwrite하는 것이었습니다.
하지만 java programming의 철학(?)이 object안의 data를 수정 못하게 하고 수정이 필요하면 new를 해서 새로 만들어서 쓰고 이전 것은 가비지컬랙트 되도록 유도하는 경향이 있어서 해당 object의 구성에 따라 제가 제시한 방법의 사용이 다소 어려울 가능성이 있을 듯도 합니다.
Taeho Oh ( ohhara@postech.edu , ohhara@plus.or.kr ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Digital Media Professionals Inc. http://www.dmprof.com
Taeho Oh ( ohhara@postech.edu ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Alticast Corp. http://www.alticast.com
감사합니다.. 마지막으로 하나만 더 질문할께요...^^;;;;
그렇다면 자바에서도 object자체를 overwrite할 수 있는 방법이 있다는거군요.
오옷!! 그 방법이 너무너무 궁금합니다... 제한적으로 쓸 수 밖에 없는 방법이라도 말이죠...
방법이 있을까요? int,
방법이 있을까요? int, double 같은 primitive type 이 아니라면 overwrite 할 방법이 없는 것으로 압니다만...
String 이라면 char 배열을 사용해서 흉내낼 수 있겠지만... char 배열의 내용을 가지고 String 을 한번이라도 만들게 되면 말짱 도루묵이지요. 처음부터 끝까지 char 배열을 사용해야만 overwrite해서 원하시는 목적을 달성할 수 있을겁니다.
간단히..
우선 String은 사용하시면 않됩니다.
JVM 내부에서 캐싱되기 때문에 유저레벨의 관리가 불가능한 객체입니다.
키등의 대부분의 데이터는 byte 배열로 표현이 되고,
이 바이트 배열을 dumm data로 덥어쓰시면 되됩니다.
이것은 대부분의 primitive 배열에 적용이 되는 겁니다.
두번째로 NIO의 버퍼를 사용하시는 방법이 있습니다.
NIO의 버퍼는 메모리 영역을 직접 관리할수 있는 방법을 제시하기 때문에 여기에 키 정보를 넣어두었다가 더미데이터로 덥어쓰면 됩니다.
System.gc()를
System.gc()를 호출하더라도 실제로 garbage collection이 수행된다는 보장은 없습니다. 저 메소드를 호출하는 것은 지금쯤이 gc를 하기에 좋은 시점이라는 것을 vm에게 알려주는 역할을 합니다.
자바가 이래서 맘에
자바가 이래서 맘에 안듭니다.
혼자서 헛짓꺼리해버리고 유저가 원하는것을 할수가 없거든요..
또다른의문..Non Primitive Type인 Object내부에 키값으로 byte[] 타입을 선언하여 사용했을 때
Object를 null처리하면, Object 내부에 있는 byte[]값은 메모리에서 완전히 초기화되는건가요?
1. 그렇다. Object 는 null값을 가지고, Object 내부의 byte[]값(메모리에 입력된 값)까지 모두 없어진다.
2. 아니다. Object 는 null값을 가지지만 Object 내부의 byte[]값(메모리에 입력된 값)은 메모리에 남는다.
혹시 2번이라면... Object를 null처리하는게 아니라 Object 내부의 byte[]값만 null처리하면 이 byte[]값은 메모리에서 사라지는건가요?
객체를 null처리하시면..
메모리에서 데이터가 남아있을수 있습니다.
이건 C에서 malloc/free를 사용해도 마찬가지입니다.
malloc등, 거의 모든 메모리 관리자는 메모리를 해제할때 해당 메모리의 내용을 지우지는 않습니다.
대신, "이영역의 메모리는 앞으로 다른곳에서 사용가능한 영역"이라고 표시만 할 뿐입니다.
그래서 malloc으로 메모리 영역을 얻어온후에 루프돌면서 0으로 초기화 하는 과정을 거치는 거죠.
자바도 마찬가지입니다. 가베지콜렉팅이 되어도 해당 영역의 메모리의 내용이 지워지는게 아닙니다.
따라서 보안상 중요한 정보를 메모리에서 완전히 제거하고 싶으시다면, 더미데이터로 덥어쓰는 과정은 필수적이라고 보시면 될겁니다.
null 처리한다고
null 처리한다고 메모리에서 사라지는 것은 아닙니다. 가비지컬렉션 대상이 될 뿐이지요. 언제 사라질지는 장담할 수 없습니다. System.gc를 호출해도 마찬가지입니다. System.gc 에 대한 API 문서의 일부분입니다.
"When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects."
effort 일뿐입니다. 위에 분들 말씀대로 primitive type 의 배열을 만들고 overwrite 하는게 가장 좋은 방법입니다.
배열도 객체의 성격(referece)을 갖습니다.
배열의 원소를 초기화(0x00, 0xff)시켜주면 됩니다.
그 말씀은
byte[] key = "123456".getBytes();
byte[] dummy = new byte[6]
이런 형태일때
for(int i=0 ; i=key.length ; i++){
key[i] = 0x00;
}
=> 이 형태는 overwrite가 되고
key = dummy;
=> 이 형태는 overwrite가 되지 않는다는 건지요....
위에도 언급이
위에도 언급이 있지만 java.lang.String은 overwrite를 할 수 없습니다.
"123456" 이것은 java.lang.String이기 때문에 overwrite가 불가능합니다.
Taeho Oh ( ohhara@postech.edu , ohhara@plus.or.kr ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Digital Media Professionals Inc. http://www.dmprof.com
Taeho Oh ( ohhara@postech.edu ) http://ohhara.sarang.net
Postech ( Pohang University of Science and Technology ) http://www.postech.edu
Alticast Corp. http://www.alticast.com
네.. 예를 드느라구요...
"123456"은 신경쓰지 않고
byte[] key 가 새로운 메모리에 셋팅이 되었을텐데요..
이 key만 놓고 봤을때 overwrite가 위와같이 2가지 방법으로 해서
각각 overwrite가 되는지 안되는지가 궁금합니다...
다시 써본다면,
byte[] key = new byte[6]
btye[] dummy = new byte[6]
key[0] = 0x00;key[1] = 0x01;key[2] = 0x02;key[3] = 0x03;key[4] = 0x04;key[5] = 0x05;
dummy [0] = 0x00;dummy [1] = 0x00;dummy [2] = 0x00;dummy [3] = 0x00;dummy [4] = 0x00;dummy [5] = 0x00;
=> 이 경우에
for(int i=0 ; i key[0] = 0x00;
}
=> 이건 overwrite가 되고....(게시판이 이상허네요.. for문 부분이 짤리네요... 위에 글 참조해주세요...ㅜㅜ)
key = dummy;
=> 이건 overwrite 안되는지 궁급합니다.
배열 자료형이 객체라서 안됩니다.
>> key = dummy;
>> => 이건 overwrite 안되는지 궁급합니다.
자바 자료형에서 basic 자료형과 object 자료형 변수에
담기는 값이 reference인지 아니면 순수한 값인지를 이해하시면 됩니다.
참고로, 테스트 코드를 만들어 보시면 쉽게 이해하실 수 있을것 같습니다.
(No subject)
테스트를 해봤습니다.
byte[] key10 = new byte[10];
byte[] key11 = null; /* null을 사용하는 것에 유의해 주세요. */
byte[] key21 = null; /* null을 사용하는 것에 유의해 주세요. */
key11 = new byte [11];
key21 = new byte [21];
System.out.println("초기값 : ");
System.out.println("key10"+key10);
System.out.println("key11"+key11);
key10 = key11;
System.out.println("");
System.out.println("key10 = key11 실행후 : ");
System.out.println("key10"+key10);
System.out.println("key11"+key11);
System.out.println("");
System.out.println("key21[0]에 새로운 값 입력 : ");
key21[0] = 0x01;
System.out.println("key21"+key21);
key21[0] = 0x02;
System.out.println("key21"+key21);
==========================================================================
결과
==========================================================================
초기값 :
key10[B@535b58
key11[B@922804
key10 = key11 실행후 :
key10[B@922804
key11[B@922804
key21[0]에 새로운 값 입력 :
key21[B@1815859
key21[B@1815859
==========================================================================
테스트 후 저의 추측
==========================================================================
byte 배열은 참조형인걸 알 수 있고,
key10 =key11 실행하여 key10의 값을 변경해도
이전 key10의 메모리 주소 B@535b58 에 이전 값이 남아있을거로 생각되어집니다.
그리고
key21[0]의 값을 16진수를 넣어 변경해보아도 메모리 주소는 변경되지 않았습니다.
그러므로 key21[0]을 변경하면 이전값인 0x01이란 값은 메모리상에서 완전히 사라지고 그 메모리주소에 0x02가 overwrite되었을 것으로 생각됩니다.
==========================================================================
댓글 달기