소스 코드에서 인증에 사용되는 비밀번호 처리


79

기본 인증 / 기본 인증서를 사용하는 RESTful API에서 가져 오려고한다고 가정하면 해당 사용자 이름과 암호를 내 프로그램에 저장하는 가장 좋은 방법은 무엇입니까? 지금은 그냥 평문으로 앉아 있습니다.

UsernamePasswordCredentials creds = new UsernamePasswordCredentials("myName@myserver","myPassword1234");

보안에 더 신경을 쓰는 방법이 있습니까?

감사


4
대답은 다음 사항에 따라 다릅니다. 앱을 배포 하시겠습니까? 사용자 / 비밀번호는 애플리케이션 사용자를 기반으로합니까, 아니면 일종의 API 키입니까? 로컬 사용자 (일종의 DRM)로부터 사용자 / 암호를 보호 하시겠습니까?
parasietje 2012 년

실제로는 백엔드에서 실행되는 프로그램이지만 실제로는 스타일에 관한 것입니다. 일반 텍스트로 분류 된 정보를 보관하는 계정에 대한 사용자 이름 / 암호가 없어야합니다.
A_Elric

2
이 스레드 stackoverflow.com/questions/12198228/…을 살펴보면 일반적인 아이디어를 얻을 수 있습니다.
Shark

답변:


112

중요 사항:

인증 시스템을 전체적으로 설계하는 경우 암호가 암호화되어 있더라도 저장해서는 안됩니다. 해시를 저장하고 로그인 중에 제공된 암호가 동일한 해시와 일치하는지 확인합니다. 이렇게하면 데이터베이스의 보안 위반으로 인해 사용자의 암호가 노출되는 것을 방지 할 수 있습니다.

즉, 내적 사고 방식으로 프로세스를 보호하기위한 몇 가지 단계는 다음과 같습니다.


첫 번째 단계는 비밀번호 처리를에서 String로 변경해야합니다 character array.

그 이유는 a Stringimmutable객체이므로 객체가로 설정되어 있어도 데이터가 즉시 정리되지 않기 때문입니다 null. 대신 데이터가 가비지 수집 용으로 설정되며 악성 프로그램 String이 정리되기 전에 해당 (암호) 데이터에 액세스 할 수 있기 때문에 보안 문제가 발생 합니다.

이것이 Swing의 JPasswordFieldgetText() 메소드가 더 이상 사용되지 않는 주된 이유 이며 getPassword()문자 배열을 사용하는 이유 입니다.


두 번째 단계는 자격 증명을 암호화하고 인증 프로세스 중에 일시적으로 만 암호를 해독하는 것입니다. 또는 서버 측에서 해시하고 해당 해시를 저장하고 원래 암호를 "잊어 버립니다".

이것은 첫 번째 단계와 유사하게 취약성 시간을 가능한 한 짧게 만듭니다.

자격 증명은 하드 코딩되지 않고 대신 구성 또는 속성 파일 또는 데이터베이스와 같이 중앙 집중식으로 구성 가능하며 쉽게 유지 관리 할 수있는 방식으로 저장하는 것이 좋습니다.

파일을 저장하기 전에 자격 증명을 암호화해야하며, 추가로 파일 자체에 두 번째 암호화를 적용 할 수 있습니다 (자격 증명에 2 계층 암호화, 다른 파일 콘텐츠에 1 계층 암호화).

위에서 언급 한 두 가지 암호화 프로세스는 각각 자체적으로 다중 계층화 될 수 있습니다. 각 암호화는 개념적 예로 Triple Data Encryption Standard (AKA TDES 및 3DES) 의 개별 응용 프로그램이 될 수 있습니다 .


로컬 환경이 적절하게 보호 된 후 (하지만 결코 "안전"하지 않습니다!) 세 번째 단계는 TLS (Transport Layer Security) 또는 SSL (Secure Sockets Layer) 을 사용하여 전송 프로세스에 기본 보호를 적용하는 것 입니다.


네 번째 단계는 다른 보호 방법을 적용하는 것입니다.

예를 들어, "사용할"컴파일에 난독 화 기술을 적용하여 이브, 말로리 또는 다른 사람 이 프로그램을 획득 한 경우 (잠시라도) 보안 조치가 노출되는 것을 방지 합니다. 얘들 아) 그리고 디 컴파일.


업데이트 1 :

@ Damien.Bell의 요청에 따라 다음은 첫 번째 단계와 두 번째 단계를 다루는 예입니다.

    //These will be used as the source of the configuration file's stored attributes.
    private static final Map<String, String> COMMON_ATTRIBUTES = new HashMap<String, String>();
    private static final Map<String, char[]> SECURE_ATTRIBUTES = new HashMap<String, char[]>();
    //Ciphering (encryption and decryption) password/key.
    private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray();
    //Cipher salt.
    private static final byte[] SALT = {
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
    //Desktop dir:
    private static final File DESKTOP = new File(System.getProperty("user.home") + "/Desktop");
    //File names:
    private static final String NO_ENCRYPTION = "no_layers.txt";
    private static final String SINGLE_LAYER = "single_layer.txt";
    private static final String DOUBLE_LAYER = "double_layer.txt";

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws GeneralSecurityException, FileNotFoundException, IOException {
        //Set common attributes.
        COMMON_ATTRIBUTES.put("Gender", "Male");
        COMMON_ATTRIBUTES.put("Age", "21");
        COMMON_ATTRIBUTES.put("Name", "Hypot Hetical");
        COMMON_ATTRIBUTES.put("Nickname", "HH");

        /*
         * Set secure attributes.
         * NOTE: Ignore the use of Strings here, it's being used for convenience only.
         * In real implementations, JPasswordField.getPassword() would send the arrays directly.
         */
        SECURE_ATTRIBUTES.put("Username", "Hypothetical".toCharArray());
        SECURE_ATTRIBUTES.put("Password", "LetMePass_Word".toCharArray());

        /*
         * For demosntration purposes, I make the three encryption layer-levels I mention.
         * To leave no doubt the code works, I use real file IO.
         */
        //File without encryption.
        create_EncryptedFile(NO_ENCRYPTION, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 0);
        //File with encryption to secure attributes only.
        create_EncryptedFile(SINGLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 1);
        //File completely encrypted, including re-encryption of secure attributes.
        create_EncryptedFile(DOUBLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 2);

        /*
         * Show contents of all three encryption levels, from file.
         */
        System.out.println("NO ENCRYPTION: \n" + readFile_NoDecryption(NO_ENCRYPTION) + "\n\n\n");
        System.out.println("SINGLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(SINGLE_LAYER) + "\n\n\n");
        System.out.println("DOUBLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(DOUBLE_LAYER) + "\n\n\n");

        /*
         * Decryption is demonstrated with the Double-Layer encryption file.
         */
        //Descrypt first layer. (file content) (REMEMBER: Layers are in reverse order from writing).
        String decryptedContent = readFile_ApplyDecryption(DOUBLE_LAYER);
        System.out.println("READ: [first layer decrypted]\n" + decryptedContent + "\n\n\n");
        //Decrypt second layer (secure data).
        for (String line : decryptedContent.split("\n")) {
            String[] pair = line.split(": ", 2);
            if (pair[0].equalsIgnoreCase("Username") || pair[0].equalsIgnoreCase("Password")) {
                System.out.println("Decrypted: " + pair[0] + ": " + decrypt(pair[1]));
            }
        }
    }

    private static String encrypt(byte[] property) throws GeneralSecurityException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));

        //Encrypt and save to temporary storage.
        String encrypted = Base64.encodeBytes(pbeCipher.doFinal(property));

        //Cleanup data-sources - Leave no traces behind.
        for (int i = 0; i < property.length; i++) {
            property[i] = 0;
        }
        property = null;
        System.gc();

        //Return encryption result.
        return encrypted;
    }

    private static String encrypt(char[] property) throws GeneralSecurityException {
        //Prepare and encrypt.
        byte[] bytes = new byte[property.length];
        for (int i = 0; i < property.length; i++) {
            bytes[i] = (byte) property[i];
        }
        String encrypted = encrypt(bytes);

        /*
         * Cleanup property here. (child data-source 'bytes' is cleaned inside 'encrypt(byte[])').
         * It's not being done because the sources are being used multiple times for the different layer samples.
         */
//      for (int i = 0; i < property.length; i++) { //cleanup allocated data.
//          property[i] = 0;
//      }
//      property = null; //de-allocate data (set for GC).
//      System.gc(); //Attempt triggering garbage-collection.

        return encrypted;
    }

    private static String encrypt(String property) throws GeneralSecurityException {
        String encrypted = encrypt(property.getBytes());
        /*
         * Strings can't really have their allocated data cleaned before CG,
         * that's why secure data should be handled with char[] or byte[].
         * Still, don't forget to set for GC, even for data of sesser importancy;
         * You are making everything safer still, and freeing up memory as bonus.
         */
        property = null;
        return encrypted;
    }

    private static String decrypt(String property) throws GeneralSecurityException, IOException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
        return new String(pbeCipher.doFinal(Base64.decode(property)));
    }

    private static void create_EncryptedFile(
                    String fileName,
                    Map<String, String> commonAttributes,
                    Map<String, char[]> secureAttributes,
                    int layers)
                    throws GeneralSecurityException, FileNotFoundException, IOException {
        StringBuilder sb = new StringBuilder();
        for (String k : commonAttributes.keySet()) {
            sb.append(k).append(": ").append(commonAttributes.get(k)).append(System.lineSeparator());
        }
        //First encryption layer. Encrypts secure attribute values only.
        for (String k : secureAttributes.keySet()) {
            String encryptedValue;
            if (layers >= 1) {
                encryptedValue = encrypt(secureAttributes.get(k));
            } else {
                encryptedValue = new String(secureAttributes.get(k));
            }
            sb.append(k).append(": ").append(encryptedValue).append(System.lineSeparator());
        }

        //Prepare file and file-writing process.
        File f = new File(DESKTOP, fileName);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        } else if (f.exists()) {
            f.delete();
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter(f));
        //Second encryption layer. Encrypts whole file content including previously encrypted stuff.
        if (layers >= 2) {
            bw.append(encrypt(sb.toString().trim()));
        } else {
            bw.append(sb.toString().trim());
        }
        bw.flush();
        bw.close();
    }

    private static String readFile_NoDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
        File f = new File(DESKTOP, fileName);
        BufferedReader br = new BufferedReader(new FileReader(f));
        StringBuilder sb = new StringBuilder();
        while (br.ready()) {
            sb.append(br.readLine()).append(System.lineSeparator());
        }
        return sb.toString();
    }

    private static String readFile_ApplyDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
        File f = new File(DESKTOP, fileName);
        BufferedReader br = new BufferedReader(new FileReader(f));
        StringBuilder sb = new StringBuilder();
        while (br.ready()) {
            sb.append(br.readLine()).append(System.lineSeparator());
        }
        return decrypt(sb.toString());
    }

모든 보호 단계를 다루는 전체 예는 "어떻게 적용 할 것인가 " 가 아니라 " 단계가 무엇인지" 에 관한 것이기 때문에이 질문에 대해 합리적이라고 생각하는 것보다 훨씬 뛰어납니다 .

내 대답 (마지막으로 샘플링) 이 훨씬 더 커질 것이지만 여기에있는 다른 질문은 이미 해당 단계 의 "방법" 에 대한 것이며 훨씬 더 적절하며 구현에 대한 더 나은 설명과 샘플링을 제공합니다. 각 개별 단계.


3
[*]-@ Damien.Bell 귀하의 요청을 방치하지 않기 위해 첫 번째 (~) 및 두 번째 단계를 다루는 예제를 포함했습니다. --- 모든 단계가 아닌 이유는 보시다시피 작은 코드 조각으로 샘플링 할 수있는 것이 아닙니다. 그리고 네트워크 보호를위한 예는 부분적으로 의사 코딩 되더라도 로컬 범위보다 더 많은 것을 요구합니다. 또한 난독 화는 매우 광범위한 구현 방법을 가지고 있으며 개념 상 간단하지만 소스 코드 자체에 적용된다는 사실은 샘플로 설명하기 어렵다는 것을 의미합니다.
XenoRo

2
마지막으로 소스에서 ProGuard와 같은 난독 화 도구를 실행합니다. Java 바이트 코드는 분해 및 분석이 매우 쉽습니다. 난독 화는 보안 케이크의 장식이며 누군가가 코드를 리버스 엔지니어링하고 잠재적으로 보안 조치를 해킹하는 것을 훨씬 더 어렵게 만듭니다. 참조 : proguard.sourceforge.net/index.html#manual/introduction.html
Jarrod Smith

1
@ Woot4Moo-내가 이해하는 바에 따르면 그가 로컬이 아닌 엔티티 (서버) 에서 가져 오려고하기 때문에 범위는 인증 자 (서버)의 관점에서 인증 프로세스의 범위가 아니라 클라이언트의 것입니다. --- 따라서 클라이언트는 저장 및 전송시 자격 증명을 보호해야하지만 자격 증명을있는 그대로 보냅니다 . 전송 보안은 기본적으로 암호화, 해싱 및 메시지 다이제스트가있는 3 단계에서 처리됩니다. ___ 서버는 그러한 해시 비교를 적용 할 수있는 곳이며 클라이언트는 보안상의 이유로 서버-듀티 프로세스를 수행해서는 안됩니다.
XenoRo

1
@Roland 나는 작년에 Java로 아무것도 프로그래밍하지 않았기 때문에 CharBuffer특별히 내 머리 꼭대기에서 기억하지 못하지만 기본적으로 변경 불가능하지 않은 경우 (내부 데이터를 덮어 쓰거나 null0으로 가져올 수 있습니다. GC를 기다리는 중), 정리하는 것을 잊지 않는 한 사용할 수 있습니다.
XenoRo

1
내 소스 코드에 모든 복호화 및 암호화 정보 (솔트 포함)가있는 경우 이것이 어떻게 더 안전합니까?
Herr Derb

7

기본 인증을 사용하는 경우 base64로 인코딩 된 일반 텍스트로 자격 증명을 전달하지 않도록 SSL과 연결해야합니다. 누군가가 패킷을 스니핑하여 자격 증명을 쉽게 얻을 수 있도록하고 싶지는 않습니다. 또한 소스 코드에 자격 증명을 하드 코딩하지 마십시오. 구성 가능하게 만드십시오. 구성 파일에서 읽으십시오. 자격 증명을 구성 파일에 저장하기 전에 암호화해야하며 앱은 구성 파일에서 자격 증명을 읽은 후 암호를 해독해야합니다.


1
이를 프로그래밍 방식으로 수행하는 방법에 대한 몇 가지 예를 제공 할 수 있습니까?
A_Elric


1

일반적으로 자격 증명을 암호화하는 것은 좋은 조언이 아닙니다. 암호화 된 것은 해독 할 수 있습니다. 공통 가장 좋은 방법은 같은 암호를 저장하는 것입니다 소금에 절인 해시 .A 해시를 해독 할 수 없습니다. Rainbow Tables로 추측하는 무차별 대입을 물리 치기 위해 소금이 추가됩니다 . 모든 사용자 ID가 고유 한 임의의 솔트를 가지고있는 한 공격자는 가능한 모든 솔트 값에 대해 테이블 ​​세트를 생성해야하므로 우주의 수명 내에서이 공격을 빠르게 불가능하게 만들 수 있습니다. 이것이 웹 사이트에서 일반적으로 비밀번호를 잊어 버린 경우 비밀번호를 보낼 수 없지만 '재설정'만 할 수있는 이유입니다. 그들은 귀하의 암호가 저장되지 않고 해시 만 저장됩니다.

암호 해싱은 스스로 구현하기가 그리 어렵지 않지만 수많은 다른 사람들이 해싱을 해주었 기 때문에 해결하는 것은 일반적인 문제입니다. 사용하기 쉬운 jBcrypt를 발견 했습니다 .

암호의 무차별 대입 추측에 대한 추가 보호로서 잘못된 암호로 특정 횟수의 로그인 시도 후 사용자 ID 또는 원격 IP가 몇 초 동안 기다리도록하는 것이 일반적인 모범 사례입니다. 이것이 없으면 무차별 대입 공격자는 서버가 처리 할 수있는만큼의 초당 암호를 추측 할 수 있습니다. 10 초당 100 개의 암호를 추측 할 수있는 것과 백만 개를 추측 할 수있는 것에는 큰 차이가 있습니다.

소스 코드에 사용자 이름 / 비밀번호 조합을 포함 시켰다는 인상을 받았습니다. 즉, 비밀번호를 변경하려는 경우 서비스를 다시 컴파일하고 중지하고 다시 시작해야하며 소스 코드를 보유한 사람도 비밀번호를 가지고 있음을 의미합니다. 일반적인 모범 사례는이 작업을 수행하지 않고 자격 증명 (사용자 이름, 암호 해시, 암호 솔트)을 데이터 저장소에 저장하는 것입니다.


2
아직 잘 모르겠지만 "... RESTful API 에서 가져 오려고합니다 ..." 는 OP가 서버 측 환경에 대해 말하고 있지 않다는 것을 나타냅니다. 그가 서버로 인증하는 클라이언트 측 응용 프로그램에 대해 이야기하고 있다고 생각합니다. __ 따라서 클라이언트 측은 저장소 (암호화) 내에서 자격 증명 만 보호하고 서버로 안전하게 전송해야합니다 (TSL / SSL-본질적으로 암호화 및 메시지 다이제스트를 적용) .___ 메시지 다이제스트 (등록 또는 비교 용) )는 서버 측에서만 수행해야합니다. 그렇지 않으면 안전하지 않습니다. ___ 내 답변의 댓글에 모두 있습니다.
XenoRo

또한 귀하의 답변은 아마도 오래된 API (jBcrypt-베타 v0.3에 있으며 2010 년 1 월에 마지막으로 업데이트되었으므로 프로젝트가 종료되었음을 나타낼 수 있음)의 사용을 나타냅니다. Java 에는 이미 자체 표준 메시지 다이제스트 클래스 가 있으며 타사 API에 대한 필수 요구 사항은 없다고 생각합니다.
XenoRo

클라이언트 대 서버 측 혼란에 대해 옳은 것 같습니다. 소스 코드가 아닌 데이터 저장소에 자격 증명을 저장하는 것이 좋지만이 경우 해싱이 아닌 암호화가 필요합니다.
Mzzl

Bcrypt는 메시지 다이제스트가 아니라 복어 기반 키 생성 체계입니다. 나는 이것을 SpringSecurity의 일부로 사용하는데, 이것은 매우 살아있다. SHA-1 또는 MD5와 같은 일반 메시지 다이제스트 알고리즘은 암호 해싱이 아니라 빠른 해싱을위한 것입니다. 가능한 한 빨리 비디오 또는 텍스트 청크를 해시해야하는 경우 이들 또는 더 현대적인 대체물을 사용할 수 있습니다. 암호 해싱에 관심이 있다면 속도가 적입니다. 사용 된 해싱 알고리즘이 빠를수록 무차별 대입 공격이 더 빨리 성공할 수 있습니다.
Mzzl

흠. 일부 Google 검색에서는 Blowfish가 암호화 (복호화 가능)라고 제안하는 반면 jBcrypt의 페이지는 blowfish 기반 메시지 파기 ( 암호화 해시 함수 )를 사용한다고 표시합니다 . 혼란 스럽습니다. ___ SpringSecurity는 살아 있고 Bcrypt는 그렇지 않을 수도 있습니다. 그들은 별도의 프로젝트입니다. ___ 어쨌든, Java 1.7은 이미 blowfish 암호를 포함하고 있으며 Security 클래스의 모듈 식 구조는 security.Provider이전 버전에서도 쉽게 구현할 수 있으므로 여전히 타사 API가 필요하지 않습니다.
XenoRo

1
  1. 요청을 초기화하는 보안 컴퓨터 (사용자 컴퓨터). 그 기계가 안전하지 않다면 아무것도 당신을 보호 할 수 없습니다. 완전히 별개의 주제 (최신 소프트웨어, 올바르게 구성된 강력한 암호, 암호화 된 스왑, 하드웨어 스니퍼, 물리적 보안 등)
  2. 저장소를 보호하십시오. 자격 증명을 저장하는 데 사용하는 매체는 암호화되어야합니다. 해독 된 자격 증명은 보안 시스템의 램에만 저장되어야합니다.
  3. 하드웨어를 유지하는 사람은 신뢰할 수 있어야합니다 (아마도 가장 약한 링크).
  4. 또한 가능한 한 적은 수를 알아야합니다. 그것은 고무 호스 암호 분석으로부터의 보호입니다
  5. 자격 증명은 모든 보안 권장 사항 (적절한 길이, 임의성, 단일 목적 등)을 충족해야합니다.
  6. 원격 서비스에 대한 연결이 보안되어야합니다 (SSL 등).
  7. 원격 서비스를 신뢰할 수 있어야합니다 (포인트 1-4 참조). 또한 해킹 가능성이 높아야합니다 (데이터 / 서비스가 안전하지 않은 경우 자격 증명을 보호하는 것은 무의미합니다). 또한 자격 증명을 저장해서는 안됩니다.

게다가 내가 잊은 수천 가지 것 :)


귀하의 답변은 클라이언트 및 서버 측 보호 단계를 요약되었지만 매우 명확하고 "추종 할 수있는" 방식으로 모두 다룹니다 . 나는 그것을 매우 좋아했다! [+1] --- 조금만 더 설명해야한다고 생각하는 몇 가지 사항이 있으며, 철자 및 서식 문제도 몇 가지 있었기 때문에 자유롭게 편집 할 수있었습니다. --- 대부분의 텍스트뿐만 아니라 일반적인 구조는 변경되지 않았습니다. 부족하다고 생각되는 부분을 추가하고 기존 텍스트를 이에 맞게 재구성했습니다. 괜찮 으시길 바랍니다.
XenoRo

철자, 링크, 문법 등은 괜찮습니다. 감사합니다. 그러나 무언가를 추가하고 싶다면 내 대답을 바꾸지 마십시오. 누락 된 것이 있다고 생각되면 의견을 추가하거나 직접 답변을 작성하십시오. 나는 내 말로만 서명하는 것을 선호합니다
piotrek

이해 했어요. --- 글쎄, 내 편집은 어떤 식 으로든 대답의 의미를 실제로 변경하지 않았습니다. 대부분은 맞춤법과 형식을 수정하는 것이었고 수정이 필요한 형식은 어쨌든 약간의 텍스트 변경이 필요합니다. 몇 가지 추가 설명은 이미 말한 내용의 확장 일뿐입니다. --- 어떤 경우 든 철자 (구문 시작 부분의 대문자가 주요 문제임)와 형식 ( "내용"에서 "주제"를 올바르게 분리)을 수정하고 필요한 맞춤 조정을 텍스트에 적용하십시오. 또한 # 7의 "prone"을 참조하십시오 . --- 그리고 물론 그것을 할 때 추가를 고려하는 것이 좋을 것입니다.
XenoRo

-1

프로그램이 실행중인 환경을 신뢰할 수 없지만 일반 암호 또는 인증서를 통해 인증해야하는 경우 자격 증명을 보호하기 위해 할 수있는 작업이 없습니다. 당신이 할 수있는 가장 큰 방법은 다른 답변에 설명 된 방법으로 난독 화하는 것입니다.

해결 방법으로 신뢰할 수있는 프록시를 통해 RESTful API에 대한 모든 요청을 실행하고 거기에서 일반 텍스트 비밀번호 인증을 수행합니다.


"프로그램이 실행중인 환경을 신뢰할 수 없다면 ..., 자격 증명을 보호하기 위해 할 수있는 일이 없습니다." -그것이 사실이라면 자격 증명에 대한 "자동 채우기"옵션이있는 거의 모든 응용 프로그램이 매우 심각한 문제에 처할 것입니다. ___ 멀티 플레이어 게임 및 웹 기반 응용 프로그램과 같은 많은 양측 응용 프로그램 (이 질문은?)이 계정 자격 증명을 로컬에 저장하며 심각한 보안 문제가 거의 없습니다. ___ 데이터는 환경에 관계없이 절대 100 % 안전하지 않습니다. 신뢰할 수있는 환경은 또 다른 보안 ( "안전한") 단계입니다.
XenoRo

음, 주어진 시나리오에서 자격 증명을 난독화할 수는 있지만 100 % (암호화) 보안에 도달 할 수는 없습니다. 당신이 바랄 수있는 가장 큰 방법은 공격자가 일반 텍스트 암호를 얻는 것을 너무 복잡하게 만드는 것이므로 노력할 가치가 없습니다. 일반적인 웹 기반 응용 프로그램의 저장된 암호를 얻으려면 브라우저의 옵션 메뉴로 이동하여 "암호 표시"를 선택하기 만하면됩니다.
Twilite

이 상황 에서든 다른 시나리오에서든 100 % 보안에 도달 할 수는 없습니다 . 이는 결국 모든 것이 본질적으로 항상 되돌릴 수있는 특정 논리적 규칙 집합에 따라 달성되는 일련의 01메모리에 있기 때문입니다 . ___ 암호화 보안의 기본은 "너무 어렵게 만드는 것은 노력할 가치가 없다"는 것입니다. ___ 마지막으로, 브라우저의 자동 채우기 / 로그인 (웹 사이트 용)을 애플리케이션의 자동 인증 / 로그인 (암호화 된 파일에만 저장 됨)으로 잘못 인식하고 있습니다.
XenoRo

암호화에 대해 읽어야합니다. 데이터를 비가 역적으로 암호화하는 방법에는 여러 가지가 있습니다. 암호화 된 데이터와 암호화 키를 모두 가지고 있어도 암호화 된 데이터를 해독 할 수없는 "단방향 해시"(md5) 또는 공개 키 암호화를 살펴보십시오. 이러한 방법을 사용하면 사실에 입각 한 100 % 보안을 얻을 수 있습니다.
Twilite

사실은 아닙니다. -내가 말했듯이, 방법은 특정 논리적 규칙 집합을 따르며 그 방법은 되돌릴 수 있습니다. ___ 암호화 해시 함수의 경우 해커가 해시를 생성 한 일련의 규칙을 알고 있으면 원본 데이터의 몇 비트를 다시보고 원본의 길이가 어떻게 될지에 대한 일반적인 아이디어를 얻을 수 있습니다. --- 무차별 대입과 추측이 많지만 100 % 깨지지 않는 것은 아닙니다. 그리고 그것은 100 % 보안 의 무한한 시도 와 는 거리가 멀다. ___ 나는 어떤 해커도 힘들게 노력할 것이라고 생각하지 않는다. 보상에 관계없이 노력할만한 가치가 없습니다.
XenoRo

-1

사람들은 왜 해싱에 대해 이야기하고 있습니다. OP는 외부 리소스에 액세스하기 위해 사용자 자격 증명을 저장하려고합니다. 그의 암호를 해싱하는 것은 도움이되지 않습니다.

이제 그것은 길을 벗어났습니다. 모든 레이어에 대해 간단한 모범 사례를 사용합니다.

1 . 자바 앱에 비밀번호 저장. : Char Array로 저장합니다. 암호 저장소 클래스를 만들고 액세스하려는 리소스로 키를 사용하여 암호를 해시 맵으로 저장하고 사용자 이름과 암호를 포함하는 일부 개체로 값을 저장합니다. 일부 인증을 사용하여이 API에 대한 진입 점을 제한합니다. 예 : 로그인 한 사용자의 자격 증명을 수락하여 해당 리소스에 대한 해당 사용자의 액세스 수준을 확인합니다 (사용자를 액세스 할 수있는 암호 목록에 매핑하기 만하면됩니다. 그룹을 많이 생성 한 경우 그리고 해당 그룹에 passwordmap 키 매핑) 비밀번호를 저장하기 위해이 이상의 모든 것은 jvm 자체에 대한 편집증에 따라 유출됩니다.

  1. 암호를 전송하려면 보안 포르토 콜로 전송하고 있는지 확인하십시오 (예 : Https는 양호, http는 불량). 실제로 안전하지 않은 프로토콜을 통해 전송해야하는 경우 암호화하고 base64로 인코딩합니다. 수신자가 암호를 해독하고 암호를 해독 할 수 있는지 확인하십시오.

OP는 자격 증명을 저장하는 상황이 있었지만 그것은 문제가 아닙니다. OP는 특히 보안에 대해 질문했습니다. => "보안에 더 신경을 쓰는 방법이 있습니까?"
XenoRo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.