티스토리 뷰
회원제 서비스를 구현하게 될 때 무심코 모든 회원의 정보를 하나의 테이블에 넣는 경우가 있습니다.
(물론 취향의 차이로 존중해줄 수 있습니다.ㅎㅎ)
이 상태에서 만약 DB가 해킹된다면?
회원 정보를 담은 테이블을 읽으면 김명준의 본명과 주소지를 알아낼 수도 있습니다.
이를 예방하기 위해서 DB에 넣을 때 소중한 정보를 암호화하여 넣어줄 수 있습니다.
그렇게 암호화된 정보를 DB에 저장할 시 ,
DB 를 해킹하더라도 암호키를 알지 못한다면, DB에서는 알 수 없는 값이 보여지기 때문에 개인정보를 알아내기 어렵습니다.
그럼 다시, 회원의 모든 정보를 암호화해서 넣어줘야 할까?
물론 모든 정보에 대해 암호화하여 관리하면 더 안전할 겁니다.
그러나 자주 사용되고 중요하지 않은 공개될 수 있는 (공개 게시물 제목, 내용과 같은)데이터라면, 빠르게 넣고 뺄 수 있도록 본래 값을 DB에 넣고 민감한 정보는 암호화해서 넣는 것이 더 좋다고 생각합니다.
그렇기에 암호화해야 할 데이터와 암호화 하지 않아도 되는 데이터를 분리해서 DB에 넣고 빼면 좋지 않을까요?
예시로 Member에 대한 Entity 를 아래와 같이 분리할 수 있다고 생각합니다.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Entity
public class Member extends BaseEntity {
@Column(name = "login_id", nullable = false)
private String loginId;
@Column(name = "password", nullable = false)
private String password;
@Getter
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_info_id", referencedColumnName = "id", nullable = false)
private MemberInfo memberInfo;
// ...
}
OneToOne으로 연결된 MemberInfo
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@ToString
@Entity
public class MemberInfo extends BaseEntity {
@Column(name = "name", nullable = false)
private String name;
@Enumerated(EnumType.STRING)
@Column(name = "role", nullable = false)
private Role role;
@Enumerated(EnumType.STRING)
@Column(name = "gender", nullable = false)
private Gender gender;
위의 두 Entity 를 참고로, 로그인을 위한 Member 정보와 (비밀번호는 암호화한다.)
회원의 개인정보를 분리해서 암호화하여 관리한다면
데이터베이스에 값을 넣고 뺄 때,
암호화가 필요한 필드에 대한 팀원 간의 이해 비용을 낮출 수 있고
본인이 다시 코드를 보았을 다시 이해하기도 좋다고 생각합니다.
암호화를 위해서, 애플리케이션 단에서는 대칭키로 데이터에 접근할 수 있도록 합니다.
@Component
public class SymmetricKeyEncoder {
private static SymmetricEncoderProperties symmetricEncoderProperties;
private SymmetricKeyEncoder(SymmetricEncoderProperties symmetricEncoderProperties) {
SymmetricKeyEncoder.symmetricEncoderProperties = symmetricEncoderProperties;
}
public static String encrypt(String plaintext) {
try {
Cipher cipher = Cipher.getInstance(symmetricEncoderProperties.getChipper());
cipher.init(Cipher.ENCRYPT_MODE, symmetricEncoderProperties.getEncodeKey());
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
throw new RestApiException(CommonError.ENCODE_ERROR);
}
}
public static String decrypt(String ciphertext) {
try {
Cipher cipher = Cipher.getInstance(symmetricEncoderProperties.getChipper());
cipher.init(Cipher.DECRYPT_MODE, symmetricEncoderProperties.getEncodeKey());
byte[] decodedBytes = Base64.getDecoder().decode(ciphertext);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RestApiException(CommonError. DECODE_ERROR);
}
}
}
결론
암호화하는 방식과 테이블의 관리 방식은 취향에 따라 충분히 다를 수 있다고 생각합니다. 그러나, 노출이 되어서는 안되는 중요한 정보일 경우 DB에 넣을 때 암호화하여 넣는 것이 좋다고 생각합니다. 더불어 암호화된 테이블과 암호화되지 않은 테이블에 대한 분리가 되어 있다면 암호화와 복호화 작업에 대한 코드를 보다 더 쉽게 반복된 코드를 줄여 작성할 수 있고 협업 시에 이해 비용을 낮출 수 있다고 생각합니다.
'Backend(개발)' 카테고리의 다른 글
Spring REST Docs를 쓰고자 합니다. (0) | 2024.06.01 |
---|---|
Custom Validation 적용 (0) | 2024.06.01 |
FriendShip의 채팅은 어떻게 이루어지나요? (0) | 2024.06.01 |
테스트 코드를 왜 작성하는가? (with TDD) (0) | 2023.10.14 |
< git > 오류 fatal: 'origin/remote-branch-name' is not a commit and a branch 'local-branch-name' cannot be created from it (0) | 2023.07.19 |