MySQL 사용자 이름과 암호를 디 컴파일로부터 보호하려면 어떻게해야합니까?


87

Java .class파일은 상당히 쉽게 디 컴파일 될 수 있습니다. 코드에서 로그인 데이터를 사용해야하는 경우 데이터베이스를 어떻게 보호 할 수 있습니까?


나는 당신의 질문을 다시 언급해도 상관 없기를 바랍니다. 나는 "username"과 "password"를 제거하고 "reverse-engineering"과 "decompiling"을 추가했습니다. 나는 그들이 원본보다 더 서술 적이라고 생각한다. 그런데 기본에 대한 훌륭한 질문입니다!
William Brendel

6
Java를 사용하고 있다는 사실은 여기서 실제로 관련이 없습니다. 어떤 언어로든 하드 코딩 된 암호를 갖는 것은 거의 같은 방식으로 문제가됩니다 ( "strings thebinary"는 C 프로그램에서도 문자열 상수를 보여줍니다).
Joachim Sauer

@saua : 맞습니다.하지만 누군가 Java에서 사용자 이름과 암호를 분리하는 방법에 대한 샘플 코드를 게시 할 것입니다. 시간이 있으면 직접 할 수도 있습니다.
William Brendel

1
많은 답변이 앱을 실행하는 사용자가 정상인 동안 권한이없는 사용자로부터 사용자 이름 / 비밀번호를 숨기려고한다고 생각합니다. 나는 당신이 모든 사람 으로부터 암호를 숨기고 싶다고 믿습니다 . 질문에서 이것을 명확히하십시오.
pek

1
예를 들어 자격 증명을 사용하여 응용 프로그램 이외의 사람이 로그인 할 수없는 서버에 연결한다고 가정 해보십시오.
pek

답변:


124

암호를 코드에 하드 코딩하지 마십시오. 이것은 최근 가장 위험한 프로그래밍 실수 25 가지 에서 제기되었습니다 .

비밀 계정과 암호를 소프트웨어에 하드 코딩하는 것은 숙련 된 리버스 엔지니어에게 매우 편리합니다. 암호가 모든 소프트웨어에서 동일하면 암호가 불가피하게 알려질 때 모든 고객이 취약 해집니다. 그리고 하드 코딩 되었기 때문에 고치는 것은 큰 고통입니다.

암호를 포함한 구성 정보를 응용 프로그램이 시작될 때 읽는 별도의 파일에 저장해야합니다. 이것이 디 컴파일의 결과로 암호가 유출되는 것을 방지하는 유일한 방법입니다 (시작하기 위해 바이너리로 컴파일하지 마십시오).

이 일반적인 실수에 대한 자세한 내용은 CWE-259 기사를 참조하십시오 . 이 기사에는 문제에 대한보다 철저한 정의, 예 및 기타 많은 정보가 포함되어 있습니다.

Java에서이를 수행하는 가장 쉬운 방법 중 하나는 Preferences 클래스를 사용하는 것입니다. 모든 종류의 프로그램 설정을 저장하도록 설계되었으며 일부는 사용자 이름과 암호를 포함 할 수 있습니다.

import java.util.prefs.Preferences;

public class DemoApplication {
  Preferences preferences = 
      Preferences.userNodeForPackage(DemoApplication.class);

  public void setCredentials(String username, String password) {
    preferences.put("db_username", username);
    preferences.put("db_password", password);
  }

  public String getUsername() {
    return preferences.get("db_username", null);
  }

  public String getPassword() {
    return preferences.get("db_password", null);
  }

  // your code here
}

위의 코드 setCredentials에서 사용자 이름과 암호를 묻는 대화 상자를 표시 한 후 메서드를 호출 할 수 있습니다. 데이터베이스에 연결해야하는 경우 getUsernamegetPassword메서드를 사용 하여 저장된 값을 검색 할 수 있습니다 . 로그인 자격 증명은 바이너리에 하드 코딩되지 않으므로 디 컴파일로 인해 보안 위험이 발생하지 않습니다.

중요 참고 : 환경 설정 파일은 일반 텍스트 XML 파일입니다. 권한이없는 사용자가 원시 파일 (UNIX 권한, Windows 권한 등)을 보지 못하도록 적절한 조치를 취해야합니다. Linux에서는 적어도 이것은 문제가되지 않습니다.을 호출 Preferences.userNodeForPackage하면 현재 사용자의 홈 디렉토리에 XML 파일이 생성되므로 다른 사용자가 읽을 수 없습니다. Windows에서는 상황이 다를 수 있습니다.

더 중요한 참고 사항 : 이 답변에 대한 의견과이 상황에 대한 올바른 아키텍처가 무엇인지에 대한 다른 의견에서 많은 논의가있었습니다. 원래 질문은 응용 프로그램이 사용되는 컨텍스트를 실제로 언급하지 않으므로 생각할 수있는 두 가지 상황에 대해 이야기하겠습니다. 첫 번째는 프로그램을 사용하는 사람이 이미 데이터베이스 자격 증명을 알고 있고 알 권한이있는 경우입니다. 두 번째는 개발자 인 귀하가 프로그램을 사용하는 사람으로부터 데이터베이스 자격 증명을 비밀로 유지하려는 경우입니다.

첫 번째 경우 : 사용자는 데이터베이스 로그인 자격 증명을 알 수있는 권한이 있습니다.

이 경우 위에서 언급 한 솔루션이 작동합니다. Java Preference클래스는 사용자 이름과 암호를 일반 텍스트로 저장하지만 기본 설정 파일은 인증 된 사용자 만 읽을 수 있습니다. 사용자는 기본 설정 XML 파일을 열고 로그인 자격 증명을 읽을 수 있지만 사용자가 시작할 자격 증명을 알고 있었기 때문에 보안 위험이 없습니다.

두 번째 사례 : 사용자에게 로그인 자격 증명을 숨기려고

이것은 더 복잡한 경우입니다. 사용자는 로그인 자격 증명을 몰라도 데이터베이스에 액세스해야합니다. 이 경우 응용 프로그램을 실행하는 사용자는 데이터베이스에 직접 액세스 할 수 있으므로 프로그램이 로그인 자격 증명을 미리 알아야합니다. 위에서 언급 한 솔루션은이 경우에 적합하지 않습니다. 데이터베이스 로그인 자격 증명을 기본 설정 파일에 저장할 수 있지만 사용자는 소유자가되므로 해당 파일을 읽을 수 있습니다. 사실,이 경우를 안전하게 사용하는 좋은 방법은 없습니다.

올바른 사례 : 다중 계층 아키텍처 사용

이를 수행하는 올바른 방법은 데이터베이스 서버와 클라이언트 애플리케이션 사이에 개별 사용자를 인증하고 제한된 작업 집합을 수행 할 수있는 중간 계층을 갖는 것입니다. 각 사용자는 자신의 로그인 자격 증명을 가지지 만 데이터베이스 서버에 대해서는 그렇지 않습니다. 자격 증명은 중간 계층 (비즈니스 논리 계층)에 대한 액세스를 허용하며 각 사용자마다 다릅니다.

모든 사용자는 보안 위험없이 기본 설정 파일에 로컬로 저장할 수있는 고유 한 사용자 이름과 암호를 갖게됩니다. 이를 3 계층 아키텍처 라고합니다 (계층은 데이터베이스 서버, 비즈니스 논리 서버 및 클라이언트 응용 프로그램 임). 더 복잡하지만 실제로 이런 종류의 작업을 수행하는 가장 안전한 방법입니다.

기본 작업 순서는 다음과 같습니다.

  1. 클라이언트는 사용자의 개인 사용자 이름 / 암호를 사용하여 비즈니스 논리 계층으로 인증합니다. 사용자 이름과 암호는 사용자에게 알려져 있으며 어떤 방식 으로든 데이터베이스 로그인 자격 증명과 관련이 없습니다.
  2. 인증이 성공하면 클라이언트는 데이터베이스에서 일부 정보를 요청하는 비즈니스 논리 계층에 요청을 보냅니다. 예를 들어, 제품 재고. 클라이언트의 요청은 SQL 쿼리가 아닙니다. .NET과 같은 원격 프로 시저 호출 getInventoryList입니다.
  3. 비즈니스 논리 계층은 데이터베이스에 연결하고 요청 된 정보를 검색합니다. 비즈니스 로직 계층은 사용자의 요청에 따라 보안 SQL 쿼리를 구성하는 역할을합니다. SQL 인젝션 공격을 방지하려면 SQL 쿼리에 대한 모든 매개 변수를 삭제해야합니다.
  4. 비즈니스 논리 계층은 인벤토리 목록을 클라이언트 응용 프로그램으로 다시 보냅니다.
  5. 클라이언트는 사용자에게 인벤토리 목록을 표시합니다.

전체 프로세스 에서 클라이언트 애플리케이션은 데이터베이스에 직접 연결되지 않습니다 . 비즈니스 논리 계층은 인증 된 사용자로부터 요청을 수신하고 인벤토리 목록에 대한 클라이언트의 요청을 처리 한 다음 SQL 쿼리 만 실행합니다.


4
이것은 누군가가 사용자 이름 / 비밀번호를 얻는 것을 정확히 어떻게 방지합니까? 그러면 파일에서 읽을 수 없습니까?
Joe Phillips

내 대답에서 말했듯이 파일 권한이 올바르게 설정되면 프로그램을 실행하는 사용자 만 해당 기본 설정 파일에 대한 읽기 액세스 권한을 갖습니다. UNIX 환경에서는이 작업이 자동으로 수행됩니다. Windows에는 추가 단계가 필요할 수 있습니다 (Windows를 많이 사용하지 않기 때문에 확실하지 않습니다).
William Brendel

앱을 실행하는 사용자가 유지하려는 사용자가 아니라는 생각이 듭니다. 이 경우 암호화해야합니다.
Michael Haren

네, 마이클이 맞습니다. 기본적으로 아이디어는 이미 사용자 이름 / 비밀번호를 알고 있으므로 숨길 필요가 없다는 것입니다. 그러나 파일 권한을 통해 다른 사용자에게는 숨겨집니다.
William Brendel

7
사용자에게 DB 편집 응용 프로그램을 배포 (예)하고 데이터베이스 사용자 이름과 암호를 알지 못하도록하려는 경우 솔루션을 잘못 설계 한 것이므로 클라이언트 소프트웨어가 서버와 통신해야합니다. 예 : 웹 서비스) DB 작업을 수행합니다.
JeeBee

15

애플리케이션이 읽을 파일에 비밀번호를 입력하십시오. 소스 파일에 비밀번호를 삽입하지 마십시오. 기간.

Ruby에는 이러한 용도로 사용되는 DBI :: DBRC 라는 잘 알려지지 않은 모듈이 있습니다. Java가 동등하다는 것은 의심의 여지가 없습니다. 어쨌든 쓰는 것은 어렵지 않습니다.


7
이렇게하면 나중에 암호를 변경하는 것이 약간 더 쉬워 지지만 기본적인 보안 문제는 해결되지 않습니다.
Brian Knoblauch

네 그렇습니다. William Brendel의 답변도 참조하십시오.
Keltia

1
Keltia와 제가 지적한 방법은이 문제를 처리하는 데 허용되는 방법입니다. 컴파일 된 코드에서 로그인 자격 증명을 분리하는 것은 가장 기본적인 소프트웨어 보안 관행 중 하나입니다. 로그인 자격 증명을 별도의 파일에 저장하는 것이이를 달성하는 효과적인 방법입니다.
William Brendel

또한 구성 정보가 일반 텍스트 파일에 있다는 사실은 운영 체제 제한으로 취소해야합니다. 예를 들어, UNIX에서 일반 텍스트 파일은 프로그램을 실행하는 사용자가 소유하고 0600 권한이 있어야 소유자 만 읽을 수 있습니다.
William Brendel

4
좋습니다. 프로그램을 실행하는 사용자 만 파일을 읽을 수 있습니다. 큰. 그것은 아무것도 해결하지 못합니다. :-) 나는 우리가에서 암호의 비밀을 유지하려는 사용자는 ... 응용 프로그램처럼 쉽게 읽을 수
브라이언 Knoblauch에게

3

웹 애플리케이션을 작성하고 있습니까? 그렇다면 JNDI를 사용하여 애플리케이션 외부에서 구성하십시오. 개요는 여기에서 볼 수 있습니다 .

JNDI는 애플리케이션이 네트워크를 통해 원격 서비스를 찾고 액세스 할 수있는 일관된 방법을 제공합니다. 원격 서비스는 메시징 서비스 또는 애플리케이션 별 서비스를 포함한 모든 엔터프라이즈 서비스 일 수 있지만 물론 JDBC 애플리케이션은 주로 데이터베이스 서비스에 관심이 있습니다. DataSource 객체가 생성되고 JNDI 이름 지정 서비스에 등록되면 애플리케이션은 JNDI API를 사용하여 해당 DataSource 객체에 액세스 할 수 있으며,이를 사용하여 해당 데이터 소스에 연결할 수 있습니다.


제공된 링크가 나쁘다
James Oravec

2

당신이 무엇을하든 민감한 정보는 어딘가에 파일에 저장됩니다. 당신의 목표는 가능한 한 얻기 어렵게 만드는 것입니다. 이를 달성 할 수있는 정도는 프로젝트, 요구 사항 및 회사 지갑의 두께에 따라 다릅니다.

가장 좋은 방법은 암호를 어디에도 저장하지 않는 것입니다. 해시 함수를 사용하여 암호 해시를 생성하고 저장하면됩니다.

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

해시 알고리즘은 단방향 함수입니다. 그들은 모든 양의 데이터를 되돌릴 수없는 고정 길이 "지문"으로 변환합니다. 또한 입력이 조금이라도 변경되면 결과 해시가 완전히 달라진다는 속성이 있습니다 (위의 예 참조). 이는 암호 파일 자체가 손상 되더라도 암호를 보호하는 형식으로 암호를 저장하고 싶지만 동시에 사용자의 암호가 올바른지 확인할 수 있기 때문에 암호를 보호하는 데 유용합니다.

관련없는 참고 사항 : 인터넷의 예전에는 '비밀번호 찾기'링크를 클릭하면 웹 사이트에서 일반 텍스트 비밀번호를 이메일로 보내 왔습니다. 그들은 아마도 그것들을 데이터베이스 어딘가에 저장하고 있었을 것입니다. 해커가 데이터베이스에 액세스하면 모든 암호에 액세스 할 수 있습니다. 많은 사용자가 여러 웹 사이트에서 동일한 암호를 사용하기 때문에 이것은 큰 보안 문제였습니다. 다행히 요즘은 이것이 일반적인 관행이 아닙니다.

이제 질문이 있습니다. 암호를 저장하는 가장 좋은 방법은 무엇입니까? 이 (인증 및 사용자 관리 서비스 stormpath의) 솔루션은 매우 이상적인 솔루션 이라고 생각 합니다 .

  1. 사용자가 자격 증명을 입력하면 암호 해시에 대해 검증됩니다.
  2. 암호가 아닌 암호 해시가 생성되고 저장됩니다.
  3. 해시가 여러 번 수행됨
  4. 무작위로 생성 된 솔트를 사용하여 해시가 생성됩니다.
  5. 해시는 개인 키로 암호화됩니다.
  6. 개인 키는 해시와 물리적으로 다른 위치에 저장됩니다.
  7. 개인 키는 시간 기반 방식으로 업데이트됩니다.
  8. 암호화 된 해시는 청크로 분할됩니다.
  9. 이러한 청크는 물리적으로 별도의 위치에 저장됩니다.

분명히 당신은 구글이나 은행이 아니기 때문에 이것은 당신에게 과도한 솔루션입니다. 그러나 그러면 프로젝트에 얼마나 많은 보안이 필요한지, 얼마나 많은 시간과 돈이 있습니까?

권장되지는 않지만 많은 애플리케이션의 경우 하드 코딩 된 암호를 코드에 저장하는 것이 충분한 해결책이 될 수 있습니다. 그러나 위 목록에서 몇 가지 추가 보안 단계를 쉽게 추가하면 애플리케이션을 훨씬 더 안전하게 만들 수 있습니다.

예를 들어 1 단계가 프로젝트에 적합한 솔루션이 아니라고 가정 해 보겠습니다. 사용자가 매번 암호를 입력하는 것을 원하지 않거나 사용자가 암호를 알기를 원하거나 필요로하지 않습니다. 여전히 민감한 정보가 어딘가에 있고이를 보호하고 싶습니다. 간단한 응용 프로그램이 있거나 파일을 저장할 서버가 없거나 프로젝트에 너무 번거 롭습니다. 애플리케이션은 파일을 안전하게 저장할 수없는 환경에서 실행됩니다. 이것은 최악의 경우 중 하나이지만 여전히 몇 가지 추가 보안 조치를 사용하면 훨씬 더 안전한 솔루션을 얻을 수 있습니다. 예를 들어 중요한 정보를 파일에 저장하고 파일을 암호화 할 수 있습니다. 암호화 개인 키를 코드에 하드 코딩 할 수 있습니다. 코드를 난독 화하여 누군가가 크래킹하는 것을 조금 더 어렵게 만들 수 있습니다.이 링크 . (나는 이것이 100 % 안전하지 않다는 것을 다시 한 번 경고하고 싶습니다. 올바른 지식과 도구를 가진 똑똑한 해커가 이것을 해킹 할 수 있습니다. 그러나 귀하의 요구 사항과 필요에 따라 이것은 귀하에게 충분한 해결책이 될 수 있습니다).



0

MD5는 암호화 알고리즘이 아니라 해시 알고리즘입니다. 간단히 말해서 해시 된 후 다시 가져올 수없고 비교할 수만 있습니다. db 사용자 이름과 비밀번호가 아닌 사용자 인증 정보를 저장할 때 이상적으로 사용해야합니다. db 사용자 이름 및 pwd는 최소한의 작업을 수행하기 위해 암호화되고 구성 파일에 보관되어야합니다.


가능한 모든 문자열 조합을 생성하고 해당 MD5 ​​해시를 저장하는 사람들에 대해 들었습니다. 따라서 누군가의 MD5 해시를 찾으면 저장 한 해시를 찾고 해당 문자열을 얻습니다.
Nav
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.