9. 압축/암호화 협상과 폴백 (전송 변환 파이프라인)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 압축/암호화 협상과 폴백 (전송 변환 파이프라인)
// ----------------------------------------------------------------------------
// 시나리오:
// - 핸드셰이크에서 클라/서버가 "지원하는 압축/암호화 알고리즘"을 교환하고,
// 공통으로 지원하는 것 중 하나를 골라 이후 모든 패킷에 적용한다.
// - 패킷 헤더에는 적용된 변환을 나타내는 1바이트 flags 가 있다:
// bit0 = 압축됨, bit1 = 암호화됨
// - 협상이 실패하거나 한쪽이 알고리즘을 모르면 "무압축/무암호" 로 폴백한다.
// - 운영 중 신버전(새 알고리즘 zstd, AES-GCM 추가)과 구버전(기존 deflate, AES-CBC)이
// 공존한다.
//
// 요구사항:
// - 협상된 변환은 세션 전체에 일관 적용되어야 하고, 송신이 적용한 변환과
// 수신이 푸는 변환이 항상 일치해야 한다.
// - 협상 실패가 "평문 전송"으로 조용히 떨어지면 안 된다(보안 다운그레이드).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 발생하는지(특히 버전 공존/폴백 시점),
// 수정안과 더 나은 협상 설계를 제시하라.
// ============================================================================
using System;
using System.Collections.Generic;
[Flags]
public enum XformFlags : byte
{
None = 0,
Compressed = 1 << 0,
Encrypted = 1 << 1,
}
public enum CompAlgo : byte { None, Deflate, Zstd }
public enum CryptAlgo : byte { None, AesCbc, AesGcm }
public class SessionXform
{
public CompAlgo Comp = CompAlgo.None;
public CryptAlgo Crypt = CryptAlgo.None;
}
public class XformNegotiator
{
// 서버가 지원하는 알고리즘 (신버전)
private static readonly CompAlgo[] ServerComp = { CompAlgo.Zstd, CompAlgo.Deflate };
private static readonly CryptAlgo[] ServerCrypt = { CryptAlgo.AesGcm, CryptAlgo.AesCbc };
// 핸드셰이크: 클라가 지원하는 목록을 받아 협상
public void Negotiate(SessionXform x, CompAlgo[] clientComp, CryptAlgo[] clientCrypt)
{
// (A) 서버 우선순위 순으로 클라가 지원하면 채택
foreach (var c in ServerComp)
if (Array.IndexOf(clientComp, c) >= 0) { x.Comp = c; break; }
foreach (var c in ServerCrypt)
if (Array.IndexOf(clientCrypt, c) >= 0) { x.Crypt = c; break; }
// (B) 공통이 없으면 None 으로 남는다(폴백). 별도 처리 없음.
}
}
public class PacketCodec
{
// 송신: 변환 적용 후 flags 세팅
public byte[] Encode(SessionXform x, byte[] payload)
{
XformFlags flags = XformFlags.None;
byte[] body = payload;
// (C) 압축 → 암호화 순서로 적용
if (x.Comp != CompAlgo.None) { body = Compress(x.Comp, body); flags |= XformFlags.Compressed; }
if (x.Crypt != CryptAlgo.None) { body = Encrypt(x.Crypt, body); flags |= XformFlags.Encrypted; }
var outBuf = new byte[1 + body.Length];
outBuf[0] = (byte)flags;
Array.Copy(body, 0, outBuf, 1, body.Length);
return outBuf;
}
// 수신: flags 를 보고 역변환
public byte[] Decode(SessionXform x, byte[] packet)
{
XformFlags flags = (XformFlags)packet[0];
byte[] body = new byte[packet.Length - 1];
Array.Copy(packet, 1, body, 0, body.Length);
// (D) flags 비트가 켜져 있으면 푼다
if ((flags & XformFlags.Encrypted) != 0) body = Decrypt(x.Crypt, body);
if ((flags & XformFlags.Compressed) != 0) body = Decompress(x.Comp, body);
return body;
}
private byte[] Compress(CompAlgo a, byte[] d) => d; // 구현 생략
private byte[] Decompress(CompAlgo a, byte[] d) => d;
private byte[] Encrypt(CryptAlgo a, byte[] d) => d;
private byte[] Decrypt(CryptAlgo a, byte[] d) => d;
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 압축/암호화 협상과 폴백 (전송 변환 파이프라인)
// ----------------------------------------------------------------------------
// 시나리오:
// - 핸드셰이크에서 클라/서버가 "지원하는 압축/암호화 알고리즘"을 교환하고,
// 공통으로 지원하는 것 중 하나를 골라 이후 모든 패킷에 적용한다.
// - 패킷 헤더에는 적용된 변환을 나타내는 1바이트 flags 가 있다:
// bit0 = 압축됨, bit1 = 암호화됨
// - 협상이 실패하거나 한쪽이 알고리즘을 모르면 "무압축/무암호" 로 폴백한다.
// - 운영 중 신버전(새 알고리즘 zstd, AES-GCM 추가)과 구버전(기존 deflate, AES-CBC)이
// 공존한다.
//
// 요구사항:
// - 협상된 변환은 세션 전체에 일관 적용되어야 하고, 송신이 적용한 변환과
// 수신이 푸는 변환이 항상 일치해야 한다.
// - 협상 실패가 "평문 전송"으로 조용히 떨어지면 안 된다(보안 다운그레이드).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 발생하는지(특히 버전 공존/폴백 시점),
// 수정안과 더 나은 협상 설계를 제시하라.
// ============================================================================
#include <cstdint>
#include <cstring>
#include <vector>
#include <algorithm>
enum XformFlags : uint8_t {
XF_None = 0,
XF_Compressed = 1 << 0,
XF_Encrypted = 1 << 1,
};
enum class CompAlgo : uint8_t { None, Deflate, Zstd };
enum class CryptAlgo : uint8_t { None, AesCbc, AesGcm };
struct SessionXform {
CompAlgo Comp = CompAlgo::None;
CryptAlgo Crypt = CryptAlgo::None;
};
class XformNegotiator {
public:
// 핸드셰이크: 클라가 지원하는 목록을 받아 협상
void Negotiate(SessionXform& x,
const std::vector<CompAlgo>& clientComp,
const std::vector<CryptAlgo>& clientCrypt)
{
// (A) 서버 우선순위 순으로 클라가 지원하면 채택
for (auto c : ServerComp)
if (std::find(clientComp.begin(), clientComp.end(), c) != clientComp.end()) { x.Comp = c; break; }
for (auto c : ServerCrypt)
if (std::find(clientCrypt.begin(), clientCrypt.end(), c) != clientCrypt.end()) { x.Crypt = c; break; }
// (B) 공통이 없으면 None 으로 남는다(폴백). 별도 처리 없음.
}
private:
// 서버가 지원하는 알고리즘 (신버전)
const std::vector<CompAlgo> ServerComp { CompAlgo::Zstd, CompAlgo::Deflate };
const std::vector<CryptAlgo> ServerCrypt { CryptAlgo::AesGcm, CryptAlgo::AesCbc };
};
class PacketCodec {
public:
// 송신: 변환 적용 후 flags 세팅
std::vector<uint8_t> Encode(const SessionXform& x, const std::vector<uint8_t>& payload)
{
uint8_t flags = XF_None;
std::vector<uint8_t> body = payload;
// (C) 압축 → 암호화 순서로 적용
if (x.Comp != CompAlgo::None) { body = Compress(x.Comp, body); flags |= XF_Compressed; }
if (x.Crypt != CryptAlgo::None) { body = Encrypt(x.Crypt, body); flags |= XF_Encrypted; }
std::vector<uint8_t> out(1 + body.size());
out[0] = flags;
std::memcpy(out.data() + 1, body.data(), body.size());
return out;
}
// 수신: flags 를 보고 역변환
std::vector<uint8_t> Decode(const SessionXform& x, const uint8_t* packet, size_t len)
{
uint8_t flags = packet[0];
std::vector<uint8_t> body(packet + 1, packet + len);
// (D) flags 비트가 켜져 있으면 푼다
if (flags & XF_Encrypted) body = Decrypt(x.Crypt, body);
if (flags & XF_Compressed) body = Decompress(x.Comp, body);
return body;
}
private:
std::vector<uint8_t> Compress(CompAlgo, const std::vector<uint8_t>& d) { return d; }
std::vector<uint8_t> Decompress(CompAlgo, const std::vector<uint8_t>& d) { return d; }
std::vector<uint8_t> Encrypt(CryptAlgo, const std::vector<uint8_t>& d) { return d; }
std::vector<uint8_t> Decrypt(CryptAlgo, const std::vector<uint8_t>& d) { return d; }
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.