728x90
민감한 데이터 전송 시 보안 방법
1. 전송 시 사용하는 보안 방식
- TLS(Transport Layer Security) 또는 SSL(Secure Sockets Layer) 프로토콜은 인터넷을 통한 데이터 전송에서 암호화를 제공합니다.
- 민감한 데이터를 전송할 때, 클라이언트와 서버 간의 연결을 암호화하여 중간에서 데이터가 탈취되는 것을 방지합니다.
- TLS는 HTTP over SSL(HTTPS)로 구현되어 웹 브라우저와 서버 간의 통신을 안전하게 암호화합니다.
- 이 방식은 데이터 전송 중 보안을 보장하며, 연결이 안전하게 유지되도록 합니다.
추가 설명:
- HTTPS는 HTTP 프로토콜 위에 TLS/SSL을 적용하여 데이터의 기밀성을 보장합니다. 이로 인해 중간자 공격(MITM)이나 데이터 스니핑을 방지할 수 있습니다.
2. 데이터 자체의 암호화
민감한 데이터를 서버에 전송하기 전에 암호화하여 안전하게 보호합니다.
이때 사용하는 암호화 방식은 대칭키 암호화(AES) 또는 비대칭키 암호화(RSA)입니다.
- AES (Advanced Encryption Standard):
- 대칭키 암호화 방식으로, 클라이언트에서 데이터를 암호화하여 서버에 전송하고, 서버에서 복호화하는 방식입니다.
- 암호화된 데이터는 AES 키를 사용하여 복호화합니다.
- RSA (Rivest–Shamir–Adleman):
- 공개키 암호화 방식으로, 서버가 공개키를 제공하고 클라이언트는 이 공개 키를 통해 데이터를 암호화합니다.
- 서버는 자신의 비공개키를 사용하여 복호화할 수 있습니다.
- RSA는 성능상 느리지만 보안성이 높아 더 안전한 방식으로 사용됩니다.
추가 설명:
- AES는 주로 대용량 데이터 암호화에 적합하며 빠른 처리 속도를 자랑합니다. RSA는 공개키와 비공개키의 관리가 필요하지만, 비대칭 암호화로 보안성이 뛰어납니다.
3. 세션 키 관리
- 세션 키는 클라이언트와 서버 간의 단기적인 암호화 키로, 세션이 종료되면 폐기됩니다.
- 세션 키는 대칭키 방식(AES)을 사용하여 데이터를 암호화하며, 고유한 세션 키가 클라이언트마다 다르게 생성됩니다.
- 세션 키를 안전하게 관리하고 암호화된 데이터를 전송하는 방식으로 보안을 강화합니다.
4. AES와 CBC 모드
- AES-CBC (Cipher Block Chaining)는 블록 단위로 데이터를 암호화하는 방식으로, 각 블록을 암호화할 때 이전 블록의 암호문을 사용하여 암호화합니다.
- IV(Initialization Vector)를 사용하여 각 암호화 세션마다 다르게 암호화된 결과를 생성합니다.
추가 설명:
- CBC 모드는 IV와 암호문을 함께 사용하며, IV는 무작위로 생성되고, 암호문은 블록 크기에 맞춰 암호화됩니다.
- AES-CBC는 암호화의 안전성을 보장하는 방식으로, 패딩과 암호화 후 복호화 과정을 신중하게 처리해야 합니다.
5. 클라이언트 측 암호화 구현 예제
아래는 세션 키 기반의 암호화/복호화 예제를 구현한 코드입니다. 이 코드는 AES-CBC 방식으로 데이터 암호화 및 복호화를 진행하며, 서버와 클라이언트 간의 안전한 데이터 전송을 구현합니다.
Spring Boot 백엔드 예제 (세션 키 생성 및 데이터 복호화)
import org.springframework.web.bind.annotation.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/session")
public class SessionKeyController {
private final Map<String, SecretKey> sessionKeys = new HashMap<>();
// 세션 키 생성 및 반환
@GetMapping("/key")
public Map<String, String> generateSessionKey(@RequestParam String sessionId) throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128); // AES-128 키 생성
SecretKey secretKey = keyGen.generateKey();
sessionKeys.put(sessionId, secretKey); // 세션 키 저장
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
return Map.of("sessionKey", encodedKey);
}
// 클라이언트 데이터 복호화
@PostMapping("/decrypt")
public Map<String, String> decryptData(@RequestParam String sessionId, @RequestBody Map<String, String> encryptedData) throws Exception {
SecretKey secretKey = sessionKeys.get(sessionId);
if (secretKey == null) {
return Map.of("error", "Invalid session ID");
}
// 암호화된 데이터 추출
String encryptedText = encryptedData.get("data");
byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
// IV와 암호문 분리
byte[] ivBytes = new byte[16];
System.arraycopy(decodedBytes, 0, ivBytes, 0, 16);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
byte[] cipherText = new byte[decodedBytes.length - 16];
System.arraycopy(decodedBytes, 16, cipherText, 0, cipherText.length);
// 복호화 수행
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
String decryptedText = new String(cipher.doFinal(cipherText));
return Map.of("decryptedData", decryptedText);
}
}
클라이언트 측 HTML/JavaScript 예제 (세션 키 및 데이터 암호화)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Session Key Encryption Example</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
input, button { margin: 10px 0; padding: 8px; }
</style>
</head>
<body>
<h1>Session Key 암호화 예제</h1>
<p>세션 키를 가져와 데이터를 암호화/복호화합니다.</p>
<label for="sessionId">Session ID:</label>
<input type="text" id="sessionId" placeholder="Enter session ID" required>
<button onclick="getSessionKey()">세션 키 가져오기</button>
<p id="keyOutput"></p>
<label for="dataToEncrypt">데이터 입력:</label>
<input type="text" id="dataToEncrypt" placeholder="Enter data to encrypt">
<button onclick="sendEncryptedData()">데이터 전송</button>
<p id="serverResponse"></p>
<script>
let sessionKey;
async function getSessionKey() {
const sessionId = document.getElementById("sessionId").value;
const response = await fetch(`/api/session/key?sessionId=${sessionId}`);
const data = await response.json();
sessionKey = data.sessionKey;
document.getElementById("keyOutput").innerText = "Session Key: " + sessionKey;
}
async function sendEncryptedData() {
if (!sessionKey) {
alert("세션 키를 먼저 가져오세요!");
return;
}
const data = document.getElementById("dataToEncrypt").value;
const encryptedData = await encryptData(data, sessionKey);
const sessionId = document.getElementById("sessionId").value;
const response = await fetch(`/api/session/decrypt?sessionId=${sessionId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ data: encryptedData })
});
const result = await response.json();
document.getElementById("serverResponse").innerText = "서버 응답: " + JSON.stringify(result);
}
async function encryptData(data, key) {
const keyBytes = base64ToUint8Array(key);
const cryptoKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-CBC" }, false, ["encrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(16));
const encoder = new TextEncoder();
const encrypted = await crypto.subtle.encrypt({ name: "AES-CBC", iv }, cryptoKey, encoder.encode(data));
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv, 0);
combined.set(new Uint8Array(encrypted), iv.length);
return btoa(String.fromCharCode(...combined));
}
function base64ToUint8Array(base64) {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
</script>
</body>
</html>
728x90
'Spring Boot' 카테고리의 다른 글
[Spring Boot] 로그 파일 생성하기 (0) | 2024.11.30 |
---|---|
[Spring Boot] Slf4j와 Logback (1) | 2024.11.29 |
[Spring Boot] Lombok과 직렬화/역직렬화 (0) | 2024.11.25 |
[Spring Boot] 간단한 실시간 웹소켓 채팅 구현하기 (0) | 2024.11.22 |
[Spring Boot] AccessToken 및 RefreshToken 인증 구현하기 (1) | 2024.11.21 |