etc./StackOverFlow

char[]가 비밀번호에 대해 String보다 선호되는 이유는 무엇입니까?

청렴결백한 만능 재주꾼 2021. 10. 6. 03:01
반응형

질문자 :Ahamed


Swing에서 비밀번호 필드에는 getText() ( String 반환) 메서드 대신 getPassword() ( char[] 반환) 메서드가 있습니다. 마찬가지로 비밀번호를 처리 String 을 사용하지 말라는 제안을 받았습니다.

String 이 비밀번호와 관련하여 보안에 위협이 되는 이유는 무엇입니까? char[] 을 사용하는 것이 불편합니다.



문자열은 변경할 수 없습니다 . String 생성한 후 다른 프로세스가 메모리를 덤프 할 수 있는 경우 가비지 수집이 시작되기 전에 데이터를 제거할 수 있는 방법이 없습니다(리플렉션 제외).

어레이를 사용하면 작업을 마친 후 데이터를 명시적으로 지울 수 있습니다. 배열을 원하는 대로 덮어쓸 수 있으며 암호는 가비지 수집 전에도 시스템의 어디에도 표시되지 않습니다.

그래서 예,이 보안 문제 -하지만 심지어 사용하여 char[] 공격자 만 기회의 창을 줄이고, 그것은 단지 공격이 특정 유형입니다.

주석에서 언급했듯이 가비지 수집기에 의해 이동되는 배열은 메모리에 데이터의 잘못된 복사본을 남길 수 있습니다. 나는 이것이 구현에 따라 다르다고 생각합니다. 가비지 수집기는 이런 종류의 일을 피하기 위해 모든 메모리를 지울 수 있습니다. char[] 에 실제 문자가 공격 창으로 포함되는 시간이 있습니다.


Jon Skeet

여기에 있는 다른 제안이 타당해 보이지만 다른 좋은 이유가 있습니다. String 을 사용하면 실수로 비밀번호를 로그 , 모니터 또는 기타 안전하지 않은 장소에 인쇄할 가능성이 훨씬 높아집니다. char[] 는 덜 취약합니다.

이걸 고려하세요:

 public static void main(String[] args) { Object pw = "Password"; System.out.println("String: " + pw); pw = "Password".toCharArray(); System.out.println("Array: " + pw); }

인쇄물:

 String: Password Array: [C@5829428e

Konrad Garus

공식 문서를 인용하기 위해 Java Cryptography Architecture 가이드 char[]String 암호(암호 기반 암호화에 관한 것이지만 이는 더 일반적으로 암호에 관한 것임)에 대해 다음과 같이 말합니다.

java.lang.String 유형의 객체에서 비밀번호를 수집하고 저장하는 것이 논리적으로 보입니다. 그러나 여기에 주의해야 할 사항이 있습니다. String 유형의 Object s는 변경할 수 없습니다. 즉, 사용 후 String 의 내용을 변경(덮어쓰기)하거나 0으로 만들 수 있도록 정의된 메서드가 없습니다. 이 기능으로 인해 String 개체는 사용자 암호와 같은 보안에 민감한 정보를 저장하는 데 적합하지 않습니다. char 배열에 보안에 민감한 정보를 수집하고 저장해야 합니다.

Java 프로그래밍 언어 버전 4.0에 대한 보안 코딩 지침의 지침 2-2 에도 비슷한 내용이 나와 있습니다(원래 로깅 컨텍스트에 있음).

지침 2-2: 매우 민감한 정보는 기록하지 마십시오

사회 보장 번호(SSN) 및 암호와 같은 일부 정보는 매우 민감합니다. 이 정보는 관리자라도 필요 이상으로 오래 보관하거나 볼 수 있는 곳에 보관해서는 안 됩니다. 예를 들어, 로그 파일로 보내서는 안 되며 검색을 통해 그 존재를 감지할 수 없어야 합니다. 일부 일시적인 데이터는 char 배열과 같은 변경 가능한 데이터 구조에 보관될 수 있으며 사용 후 즉시 지워집니다. 데이터 구조를 지우면 객체가 메모리에서 프로그래머에게 투명하게 이동되기 때문에 일반적인 Java 런타임 시스템에서 효율성이 감소했습니다.

이 지침은 또한 그들이 다루고 있는 데이터에 대한 의미론적 지식이 없는 하위 수준 라이브러리의 구현 및 사용에 대한 의미를 가지고 있습니다. 예를 들어, 저수준 문자열 구문 분석 라이브러리는 작동하는 텍스트를 기록할 수 있습니다. 응용 프로그램은 라이브러리를 사용하여 SSN을 구문 분석할 수 있습니다. 이는 로그 파일에 대한 액세스 권한이 있는 관리자가 SSN을 사용할 수 있는 상황을 만듭니다.


Bruno

문자 배열( char[] )은 사용 후 각 문자를 0으로 설정하고 문자열을 그렇지 않음으로 설정하여 지울 수 있습니다. 누군가가 메모리 이미지를 볼 수 있다면 문자열을 사용하면 일반 텍스트로 암호를 볼 수 있지만 char[] 를 사용하면 0으로 데이터를 제거한 후 암호가 안전합니다.


alephx

어떤 사람들은 암호가 더 이상 필요하지 않으면 암호를 저장하는 데 사용된 메모리를 덮어써야 한다고 생각합니다. 이렇게 하면 공격자가 시스템에서 암호를 읽어야 하는 시간 창이 줄어들고 공격자가 이를 수행하기 위해 JVM 메모리를 가로채기에 충분한 액세스 권한이 이미 필요하다는 사실을 완전히 무시합니다. 많은 액세스 권한을 가진 공격자가 주요 이벤트를 포착하여 이것을 완전히 쓸모 없게 만들 수 있습니다(AFAIK, 내가 틀렸다면 수정해 주세요).

업데이트

댓글 덕분에 답변을 업데이트해야 합니다. 암호가 하드 드라이브에 저장될 수 있는 시간을 줄여주기 때문에 (매우) 사소한 보안 향상을 추가할 수 있는 두 가지 경우가 분명히 있습니다. 여전히 나는 대부분의 사용 사례에서 과도하다고 생각합니다.

  • 대상 시스템이 잘못 구성되어 있거나 그렇다고 가정해야 하며 코어 덤프에 대해 편집증적이어야 합니다(시스템이 관리자에 의해 관리되지 않는 경우 유효할 수 있음).
  • TrueCrypt (단종), VeraCrypt 또는 CipherShed 와 같은 것을 사용하여 공격자가 하드웨어에 액세스하여 데이터 유출을 방지하려면 소프트웨어가 지나치게 편집증적이어야 합니다.

가능하다면 코어 덤프와 스왑 파일을 비활성화하면 두 문제가 모두 해결됩니다. 그러나 관리자 권한이 필요하고 기능이 줄어들 수 있으며(사용할 메모리가 적음) 실행 중인 시스템에서 RAM을 가져오는 것은 여전히 유효한 문제입니다.


josefx

나는 이것이 유효한 제안이라고 생각하지 않지만 적어도 그 이유는 추측할 수 있습니다.

비밀번호를 사용한 후 신속하고 확실하게 메모리에 있는 모든 자취를 확실히 지울 수 있도록 하려는 동기가 있다고 생각합니다. char[] 를 사용하면 배열의 각 요소를 공백으로 덮어쓸 수 있습니다. 그런 식으로 String 의 내부 값을 편집할 수 없습니다.

그러나 그것만으로는 좋은 답이 아닙니다. char[] 또는 String 대한 참조가 이스케이프되지 않는지 확인하는 것이 어떻습니까? 그러면 보안 문제가 없습니다. 그러나 문제는 String intern() 될 수 있고 상수 풀 내에서 계속 살아 있을 수 있다는 것입니다. char[] 사용하면 이러한 가능성이 금지된다고 가정합니다.


Sean Owen

답은 이미 나왔지만 최근에 자바 표준 라이브러리에서 발견한 문제를 공유하고 싶습니다. char[] 로 교체하는 데 세심한 주의를 기울이지만(물론 좋은 일입니다) 다른 보안에 중요한 데이터는 메모리에서 삭제할 때 간과되는 것 같습니다.

예를 들어 PrivateKey 클래스를 생각하고 있습니다. 일부 작업을 수행하는 데 사용하여 PKCS#12 파일에서 개인 RSA 키를 로드하는 시나리오를 고려하십시오. 이제 이 경우 키 파일에 대한 물리적 액세스가 적절하게 제한되어 있는 한 암호를 스니핑하는 것만으로는 도움이 되지 않습니다. 공격자로서 암호 대신 키를 직접 얻는 것이 훨씬 나을 것입니다. 원하는 정보가 누출될 수 있는 매니폴드, 코어 덤프, 디버거 세션 또는 스왑 파일은 일부 예일 뿐입니다.

그리고 밝혀진 바와 같이 해당 정보를 구성하는 바이트를 지울 수 있는 API가 없기 때문에 메모리에서 PrivateKey 의 개인 정보를 지울 수 있는 방법은 없습니다.

이 문서 에서는 이러한 상황이 잠재적으로 악용될 수 있는 방법을 설명하므로 이는 나쁜 상황입니다.

예를 들어 OpenSSL 라이브러리는 개인 키가 해제되기 전에 중요한 메모리 섹션을 덮어씁니다. Java는 가비지 수집(garbage-collected)이므로 키를 사용한 직후 적용되는 Java 키에 대한 개인 정보를 지우고 무효화하는 명시적 방법이 필요합니다.


emboss

Jon Skeet이 말했듯이 리플렉션을 사용하는 것 외에는 방법이 없습니다.

그러나 리플렉션이 옵션인 경우 이 작업을 수행할 수 있습니다.

 public static void main(String[] args) { System.out.println("please enter a password"); // don't actually do this, this is an example only. Scanner in = new Scanner(System.in); String password = in.nextLine(); usePassword(password); clearString(password); System.out.println("password: '" + password + "'"); } private static void usePassword(String password) { } private static void clearString(String password) { try { Field value = String.class.getDeclaredField("value"); value.setAccessible(true); char[] chars = (char[]) value.get(password); Arrays.fill(chars, '*'); } catch (Exception e) { throw new AssertionError(e); } }

실행할 때

 please enter a password hello world password: '***********'

참고: 문자열의 char[]가 GC 주기의 일부로 복사된 경우 이전 복사본이 메모리 어딘가에 있을 가능성이 있습니다.

이 오래된 복사본은 힙 덤프에 나타나지 않지만 프로세스의 원시 메모리에 직접 액세스할 수 있는 경우 이를 볼 수 있습니다. 일반적으로 그러한 액세스 권한이 있는 사람은 피해야 합니다.


Peter Lawrey

이것이 모두 이유입니다. 비밀번호 로 String 대신 char[] 배열을 선택해야 합니다.

1. Java에서 문자열은 변경할 수 없으므로 암호를 일반 텍스트로 저장하면 가비지 수집기가 암호를 지울 때까지 메모리에서 사용할 수 있습니다. 문자열은 재사용성을 위해 문자열 풀에서 사용되므로 장기간 메모리에 남아 있어 보안 위협이 됩니다.

메모리 덤프에 액세스할 수 있는 사람은 누구나 암호를 일반 텍스트로 찾을 수 있기 때문에 항상 일반 텍스트보다 암호화된 암호를 사용해야 하는 또 다른 이유입니다. 문자열은 변경할 수 없으므로 문자열의 내용을 변경할 수 있는 방법은 없습니다. 변경하면 새 문자열이 생성되기 때문입니다. 반면에 char[]를 사용하는 경우 여전히 모든 요소를 공백이나 0으로 설정할 수 있습니다. 따라서 문자 배열에 암호를 저장하면 암호 도용의 보안 위험을 확실히 완화할 수 있습니다.

2. Java는 보안상의 이유로 암호를 일반 텍스트로 반환하는 더 이상 사용되지 않는 getText() 메서드 대신 char[]를 반환하는 JPasswordField의 getPassword() 메서드를 사용할 것을 권장합니다. Java 팀의 조언을 따르고 표준에 위배되지 않고 준수하는 것이 좋습니다.

3. String을 사용하면 항상 로그 파일이나 콘솔에 일반 텍스트를 인쇄할 위험이 있지만 배열을 사용하는 경우 배열의 내용을 인쇄하지 않고 대신 메모리 위치가 인쇄됩니다. 실제 이유는 아니지만 여전히 의미가 있습니다.

 String strPassword="Unknown"; char[] charPassword= new char[]{'U','n','k','w','o','n'}; System.out.println("String password: " + strPassword); System.out.println("Character password: " + charPassword); String password: Unknown Character password: [C@110b053

이 블로그 에서 참조했습니다. 이게 도움이 되길 바란다.


Human Being

편집: 1년 간의 보안 연구 후에 이 답변으로 돌아와서 실제로 일반 텍스트 암호를 비교하게 될 것이라는 다소 불행한 암시를 준다는 것을 깨달았습니다. 하지 마세요. 솔트와 합리적인 횟수의 반복으로 안전한 단방향 해시를 사용하십시오 . 라이브러리 사용을 고려하십시오. 이 항목은 제대로 하기 어렵습니다!

원래 답변: String.equals()가 단락 평가를 사용하므로 타이밍 공격에 취약하다는 사실은 어떻습니까? 가능성은 낮지만 이론적 으로 올바른 문자 시퀀스를 결정하기 위해 암호 비교 시간을 정할 수 있습니다.

 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; // Quits here if Strings are different lengths. if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; // Quits here at first different character. while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

타이밍 공격에 대한 추가 리소스:


Graph Theory

사용 후 수동으로 정리하지 않는 한 char 배열이 String에 대해 제공하는 것은 없으며 실제로 그렇게하는 사람을 보지 못했습니다. 그래서 나에게 char[] 대 String의 선호는 약간 과장되었습니다.

여기 에서 널리 사용되는 Spring Security 라이브러리를 살펴보고 스스로에게 물어보십시오. Spring Security 직원이 무능하거나 char[] 암호가 그다지 의미가 없습니다. 일부 악의적인 해커가 RAM의 메모리 덤프를 가져갈 때 정교한 방법을 사용하여 암호를 숨기더라도 모든 암호를 알아낼 수 있는지 확인하십시오.

그러나 Java는 항상 변경되며 Java 8의 문자열 중복 제거 기능 과 같은 일부 무서운 기능은 사용자 모르게 문자열 객체를 인턴할 수 있습니다. 그러나 그것은 다른 대화입니다.


Oleg Mikheev

문자열은 변경할 수 없으며 일단 생성되면 변경할 수 없습니다. 암호를 문자열로 생성하면 힙 또는 문자열 풀에 암호에 대한 잘못된 참조가 남습니다. 이제 누군가 Java 프로세스의 힙 덤프를 가져와서 주의 깊게 스캔하면 암호를 추측할 수 있습니다. 물론 이러한 사용되지 않는 문자열은 가비지 수집되지만 GC가 시작되는 시점에 따라 다릅니다.

반면 char[]는 인증이 완료되는 즉시 변경할 수 있으며 모든 M 또는 백슬래시와 같은 문자로 덮어쓸 수 있습니다. 이제 누군가가 힙 덤프를 사용하더라도 현재 사용하지 않는 암호를 얻지 못할 수도 있습니다. 이렇게 하면 개체 콘텐츠를 직접 지우는 것과 GC가 완료하기를 기다리는 것과 같은 의미에서 더 많은 제어가 가능합니다.


Geek

문자열은 변경할 수 없으며 문자열 풀로 이동합니다. 한 번 작성하면 덮어쓸 수 없습니다.

char[] 는 암호를 사용한 후에 덮어써야 하는 배열이며 다음과 같이 수행해야 합니다.

 char[] passw = request.getPassword().toCharArray() if (comparePasswords(dbPassword, passw) { allowUser = true; cleanPassword(passw); cleanPassword(dbPassword); passw=null; } private static void cleanPassword (char[] pass) { Arrays.fill(pass, '0'); }

공격자가 이를 사용할 수 있는 한 가지 시나리오는 crashdump입니다. JVM이 충돌하고 메모리 덤프를 생성할 때 암호를 볼 수 있습니다.

반드시 악의적인 외부 공격자는 아닙니다. 모니터링 목적으로 서버에 액세스할 수 있는 지원 사용자일 수 있습니다. 그는 크래시 덤프를 들여다보고 암호를 찾을 수 있었습니다.


ACV

짧고 간단한 대답은 char[] 는 변경 가능하지만 String 객체는 변경 가능하지 않기 때문입니다.

Strings 은 변경할 수 없는 객체입니다. 이것이 한 번 생성되면 수정할 수 없는 이유이며, 따라서 메모리에서 내용을 제거하는 유일한 방법은 가비지 수집을 하는 것입니다. 개체가 해제한 메모리를 덮어쓸 수 있는 경우에만 해당 데이터가 사라집니다.

이제 Java의 가비지 수집은 보장된 간격으로 발생하지 않습니다. 따라서 String 은 메모리에 오랫동안 유지될 수 있으며 이 시간 동안 프로세스가 충돌하면 문자열의 내용이 메모리 덤프나 일부 로그에 남을 수 있습니다.

문자형 배열을 사용하면 암호를 읽고 가능한 한 빨리 작업을 마친 다음 내용을 즉시 변경할 수 있습니다.


Pritam Banerjee

Java의 문자열은 변경할 수 없습니다. 따라서 문자열이 생성될 때마다 가비지 수집될 때까지 메모리에 남아 있습니다. 따라서 메모리에 액세스할 수 있는 사람은 누구나 문자열 값을 읽을 수 있습니다.
문자열의 값이 수정되면 결국 새 문자열이 생성됩니다. 따라서 원래 값과 수정된 값은 모두 가비지 수집될 때까지 메모리에 유지됩니다.

문자 배열을 사용하면 암호의 목적이 제공되면 배열의 내용을 수정하거나 지울 수 있습니다. 배열의 원래 내용은 수정된 후 가비지 수집이 시작되기 전에도 메모리에서 찾을 수 없습니다.

보안 문제 때문에 암호를 문자 배열로 저장하는 것이 좋습니다.


Saathvik

케이스 문자열:

 String password = "ill stay in StringPool after Death !!!"; // some long code goes // ...Now I want to remove traces of password password = null; password = ""; // above attempts wil change value of password // but the actual password can be traced from String pool through memory dump, if not garbage collected

케이스 CHAR 어레이:

 char[] passArray = {'p','a','s','s','w','o','r','d'}; // some long code goes // ...Now I want to remove traces of password for (int i=0; i<passArray.length;i++){ passArray[i] = 'x'; } // Now you ACTUALLY DESTROYED traces of password form memory

Aditya Rewari

이 목적을 위해 String을 사용해야 하는지 Char[]를 사용해야 하는지는 둘 다 장단점이 있기 때문에 논쟁의 여지가 있습니다. 그것은 사용자가 필요로 하는 것에 달려 있습니다.

Java의 문자열은 변경할 수 없으므로 일부 사용자가 문자열을 조작하려고 할 때마다 새 객체가 생성되고 기존 문자열은 영향을 받지 않습니다. 이것은 비밀번호를 String으로 저장하는 것에 대한 이점으로 볼 수 있지만, 객체는 사용 후에도 메모리에 남아 있습니다. 따라서 누군가가 개체의 메모리 위치를 알아낸 경우 해당 위치에 저장된 암호를 쉽게 추적할 수 있습니다.

Char[]는 변경 가능하지만 사용 후 프로그래머가 명시적으로 배열을 정리하거나 값을 재정의할 수 있다는 이점이 있습니다. 따라서 사용이 완료되면 청소되며 아무도 저장한 정보에 대해 알 수 없습니다.

위의 상황을 바탕으로 요구 사항에 따라 String으로 갈지 Char[]로 갈지 아이디어를 얻을 수 있습니다.


Neeraj

위의 많은 훌륭한 답변. 내가 가정하는 또 다른 요점이 있습니다(틀린 경우 수정해 주세요). 기본적으로 Java는 문자열을 저장하기 위해 UTF-16을 사용합니다. 문자 배열 사용 char[]array는 유니코드, 지역 문자 등의 활용을 용이하게 합니다. 이 기술을 사용하면 암호를 저장하기 위해 모든 문자 집합이 동등하게 존중될 수 있으며 이후에는 문자 집합 혼동으로 인해 특정 암호 문제가 시작되지 않습니다. 마지막으로 char 배열을 사용하여 암호 배열을 원하는 문자 집합 문자열로 변환할 수 있습니다.


Dibsyhex

출처 : http:www.stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords

반응형