CT: 18. 쿠키 변조
분류: Web Application(웹)
중요도: 상
개요
점검 내용
쿠키 변조를 통한 임의의 타 사용자 권한 탈취 여부 점검
점검 목적
쿠키를 사용하는 경우, 안전한 알고리즘으로 암호화하여 공격자가 쿠키 값 변조를 통해 다른 사용자로 위장하거나 권한을 변경하는 것을 방지하기 위함
보안 위협
클라이언트에 전달되는 쿠키에 사용자 식별 값이 평문으로 노출될 경우 쿠키 변조를 통해 타 사용자의 유효한 세션을 취득할 수 있으며, 기타 중요 정보의 유출 및 변조 가능함
참고
쿠키(Cookie)
서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각. 브라우저는 그 데이터 조각들을 저장 후, 동일한 서버에 대하여 재요청 시 저장된 데이터를 함께 전송
참고
소스코드 및 취약점 점검 필요
점검 대상 및 판단 기준
대상
웹 애플리케이션 소스코드
판단 기준
✅ 양호: 쿠키를 사용하지 않고 서버 사이드 세션을 사용하고 있거나, 쿠키를 사용하는 경우 안전한 알고리즘(SEED, 3DES, AES)이 적용되어 있는 경우
❌ 취약: 안전한 알고리즘이 적용되지 않은 쿠키를 사용하거나, 쿠키로만 인증 및 권한 부여를 적용하는 경우
조치 방법
쿠키 대신 서버 사이드 세션 방식을 사용하거나, 쿠키를 통해 인증 등 중요한 기능을 구현해야 하는 경우 안전한 알고리즘(SEED, 3DES, AES 등)을 적용
조치 시 영향
일반적인 경우 영향 없음
점검 및 조치 사례
점검 방법
-
일반 사용자 계정으로 로그인 한 뒤 쿠키 내용 및 발행되는 쿠키에 중요 정보(인증을 위한 ID, 권한을 위한 구분자 등)의 노출 여부 확인 후 변조 시도
-
쿠키 내 노출되는 중요 정보를 변조하여 다른 사용자 및 권한으로 정상 이용이 가능한지 확인
조치 방법
-
쿠키 대신 보안성이 강한 서버 사이드 세션 방식 사용. 클라이언트 사이드 방식인 쿠키는 구조상 다양한 취약점에 노출될 가능성이 존재
-
쿠키를 사용해서 중요 정보나 인증을 구현해야 할 경우엔 안전한 알고리즘(SEED, 3DES, AES 등) 적용
-
쿠키 서명(HMAC)을 통해 변조 여부 검증 및 HttpOnly, Secure, SameSite 속성을 적용하여 보안 강화
Java
AES + HMAC CookieUtil 예시
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class CookieUtil {
private static final int IV_LEN = 16, HMAC_LEN = 32;
private static final SecureRandom RNG = new SecureRandom();
private final byte[] encKey;
private final byte[] hmacKey;
public CookieUtil(byte[] encKey, byte[] hmacKey) {
if (encKey.length != 32) throw new IllegalArgumentException("AES key must be 32 bytes");
this.encKey = encKey;
this.hmacKey = hmacKey;
}
// 쿠키 생성 (HttpOnly, Secure, SameSite 설정 포함)
public void addSecureCookie(HttpServletResponse resp, String name, String plaintext, int maxAgeSec) throws Exception {
byte[] iv = new byte[IV_LEN];
RNG.nextBytes(iv);
// AES-256-CBC 암호화
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encKey, "AES"), new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(hmacKey, "HmacSHA256"));
mac.update(iv);
mac.update(ciphertext);
byte[] hmac = mac.doFinal();
byte[] payload = new byte[iv.length + hmac.length + ciphertext.length];
System.arraycopy(iv, 0, payload, 0, iv.length);
System.arraycopy(hmac, 0, payload, iv.length, hmac.length);
System.arraycopy(ciphertext, 0, payload, iv.length + hmac.length, ciphertext.length);
String encoded = Base64.getUrlEncoder().withoutPadding().encodeToString(payload);
// HttpOnly, Secure 속성 설정
Cookie cookie = new Cookie(name, encoded);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(maxAgeSec);
// SameSite 속성은 Cookie API로 직접 지정 불가 → 헤더로 세팅
String header = String.format(
"%s=%s; Max-Age=%d; Path=/; HttpOnly; Secure; SameSite=Strict",
name, encoded, maxAgeSec
);
resp.addHeader("Set-Cookie", header);
}
// 쿠키 읽기 (HMAC 검증 + 복호화)
public String readSecureCookie(String value) throws Exception {
if (value == null) return null;
byte[] data;
try {
data = Base64.getUrlDecoder().decode(value);
} catch (IllegalArgumentException e) {
return null;
}
if (data.length < IV_LEN + HMAC_LEN + 1) return null;
byte[] iv = Arrays.copyOfRange(data, 0, IV_LEN);
byte[] hmac = Arrays.copyOfRange(data, IV_LEN, IV_LEN + HMAC_LEN);
byte[] ciphertext = Arrays.copyOfRange(data, IV_LEN + HMAC_LEN, data.length);
// 복호화 전 HMAC 검증
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(hmacKey, "HmacSHA256"));
mac.update(iv);
mac.update(ciphertext);
byte[] calc = mac.doFinal();
if (!MessageDigest.isEqual(hmac, calc)) return null; // 변조 탐지 후 복호화
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(encKey, "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
}
}
ASP.NET
MachineKey 적용 예시
-
web.config -
cookie.aspx.cs// 쿠키 발급 (FormsAuthenticationTicket 활용) var ticket = new FormsAuthenticationTicket( 1, "username", DateTime.Now, DateTime.Now.AddMinutes(30), true, "role=admin"); string encrypted = FormsAuthentication.Encrypt(ticket); // 내부적으로 AES + HMAC 적용 var cookie = new HttpCookie("AuthCookie", encrypted) { // 쿠키 보안 적용 HttpOnly = true, Secure = true, SameSite = SameSiteMode.Lax }; Response.Cookies.Add(cookie);