중요 사항:
인증 시스템을 전체적으로 설계하는 경우 암호가 암호화되어 있더라도 저장해서는 안됩니다. 해시를 저장하고 로그인 중에 제공된 암호가 동일한 해시와 일치하는지 확인합니다. 이렇게하면 데이터베이스의 보안 위반으로 인해 사용자의 암호가 노출되는 것을 방지 할 수 있습니다.
즉, 내적 사고 방식으로 프로세스를 보호하기위한 몇 가지 단계는 다음과 같습니다.
첫 번째 단계는 비밀번호 처리를에서 String
로 변경해야합니다 character array
.
그 이유는 a String
가 immutable
객체이므로 객체가로 설정되어 있어도 데이터가 즉시 정리되지 않기 때문입니다 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의 요청에 따라 다음은 첫 번째 단계와 두 번째 단계를 다루는 예입니다.
private static final Map<String, String> COMMON_ATTRIBUTES = new HashMap<String, String>();
private static final Map<String, char[]> SECURE_ATTRIBUTES = new HashMap<String, char[]>();
private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray();
private static final byte[] SALT = {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
private static final File DESKTOP = new File(System.getProperty("user.home") + "/Desktop");
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";
public static void main(String[] args) throws GeneralSecurityException, FileNotFoundException, IOException {
COMMON_ATTRIBUTES.put("Gender", "Male");
COMMON_ATTRIBUTES.put("Age", "21");
COMMON_ATTRIBUTES.put("Name", "Hypot Hetical");
COMMON_ATTRIBUTES.put("Nickname", "HH");
SECURE_ATTRIBUTES.put("Username", "Hypothetical".toCharArray());
SECURE_ATTRIBUTES.put("Password", "LetMePass_Word".toCharArray());
create_EncryptedFile(NO_ENCRYPTION, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 0);
create_EncryptedFile(SINGLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 1);
create_EncryptedFile(DOUBLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 2);
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");
String decryptedContent = readFile_ApplyDecryption(DOUBLE_LAYER);
System.out.println("READ: [first layer decrypted]\n" + decryptedContent + "\n\n\n");
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));
String encrypted = Base64.encodeBytes(pbeCipher.doFinal(property));
for (int i = 0; i < property.length; i++) {
property[i] = 0;
}
property = null;
System.gc();
return encrypted;
}
private static String encrypt(char[] property) throws GeneralSecurityException {
byte[] bytes = new byte[property.length];
for (int i = 0; i < property.length; i++) {
bytes[i] = (byte) property[i];
}
String encrypted = encrypt(bytes);
return encrypted;
}
private static String encrypt(String property) throws GeneralSecurityException {
String encrypted = encrypt(property.getBytes());
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());
}
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());
}
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));
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());
}
모든 보호 단계를 다루는 전체 예는 "어떻게 적용 할 것인가 " 가 아니라 " 단계가 무엇인지" 에 관한 것이기 때문에이 질문에 대해 합리적이라고 생각하는 것보다 훨씬 뛰어납니다 .
내 대답 (마지막으로 샘플링) 이 훨씬 더 커질 것이지만 여기에있는 다른 질문은 이미 해당 단계 의 "방법" 에 대한 것이며 훨씬 더 적절하며 구현에 대한 더 나은 설명과 샘플링을 제공합니다. 각 개별 단계.