🎲 초기화되지 않은 변수 시뮬레이터 KISA A-5 · 초기화되지 않은 변수 사용
지역 버퍼나 플래그를 대입 전에 읽으면 이전 호출이 스택에 남긴 잔존 바이트가 그대로 반환됩니다. 그 잔존값은 비밀을 유출하거나(정보 노출) 보안 분기를 비결정적으로 만들어(때때로 접근을 허용) 위험합니다.
① 정보 유출: 스택 재사용이 이전 비밀을 노출 VULNERABLE
handle_secret()가 16바이트 스택 버퍼에 토큰을 쓰고 반환합니다(바이트는 지워지지 않음). handle_public()이 같은 스택 프레임에 초기화되지 않은 char buf[16]를 선언해 클라이언트로 전송하면 — 잔존 토큰이 유출됩니다.
스택 프레임 버퍼 (16바이트 @ 0x7ffe...)
② 비결정적 분기: 초기화되지 않은 인증 플래그 VULNERABLE
bool isAdmin;이 선언되지만 항상 실행되지는 않는 if 안에서만 설정됩니다. 조건을 건너뛰면 isAdmin은 잔존 스택 바이트를 가지며 — 때때로 0이 아니어서 접근이 허용됩니다. 여러 번 실행해 결과가 뒤집히는 것을 보세요.
③ 안전: 선언 시 항상 초기화 SECURE
char buf[16] = {0};와 bool isAdmin = false;는 모든 바이트를 결정적으로 만듭니다. 버퍼는 이전 비밀을 절대 담지 않고 분기는 절대 뒤집히지 않습니다 — 명시적으로 허용하지 않으면 접근은 거부됩니다.
0으로 초기화된 버퍼
④ 취약 vs 안전 코드
VULNERABLE — C
void handle_public(int sock){
char buf[16]; // 초기화 안 됨!
// buf 에는 이전 호출(handle_secret)의
// 토큰 바이트가 그대로 잔존
send(sock, buf, 16, 0); // ← 비밀 유출
bool isAdmin; // 초기화 안 됨!
if (parse_role() == 1) // 조건이 거짓이면
isAdmin = true; // ← 이 줄 건너뜀
if (isAdmin) // 잔존 바이트가 참일 수도
grant_admin(); // ← 우발적 권한 상승
}
SECURE — C
void handle_public(int sock){
char buf[16] = {0}; // 모든 바이트 0
fill_public(buf); // 의도한 값만 기록
send(sock, buf, 16, 0); // 잔존 비밀 없음
bool isAdmin = false; // 안전한 기본값
if (parse_role() == 1)
isAdmin = true;
if (isAdmin) // 명시 허용 시에만 참
grant_admin();
}
// 컴파일 시 -Wuninitialized / -Werror,
// MSAN(MemorySanitizer)로 조기 탐지
방어 핵심: 모든 변수를 선언 시 초기화(
= 0 / = {0} / = false / 생성자) · 보안 플래그는 기본 거부(deny-by-default) · 비밀을 담은 버퍼는 재사용 전 0으로 소거 · CI에서 -Wuninitialized -Werror·MemorySanitizer 활성화.
⚠️ 교육용 시뮬레이션입니다. 스택 바이트·비밀·플래그는 모두 mock JS 값이며 실제 메모리를 초기화 없이 읽지 않습니다.