🐞 제거되지 않은 디버그 코드 시뮬레이터 KISA A-6 · 제거되지 않은 디버그 코드
디버그·테스트 코드가 운영에 그대로 배포된 상황입니다.
?debug=1이 설정/시크릿을 덤프하고, /test/login-as 백도어가 인증을 우회하며, 상세 로그가 실토큰을 출력합니다. 직접 켜 보고, 빌드 단계 제거가 모든 문을 닫는 것을 비교합니다.
① 디버그 코드가 그대로 포함된 운영 앱 VULNERABLE
이 mock 운영 서버에는 DEBUG 분기가 남아 있습니다. 엔드포인트를 골라 실행하면, 시크릿이 새고 자격증명 없이 인증이 우회됩니다.
② 동일 요청 — 강화된 운영 빌드 SECURE
릴리스 빌드는 컴파일 시 디버그 분기를 제거하고(
if(false) → 데드코드 제거), ENV=production에서는 /test/* 마운트를 거부하며, 로그 레벨을 운영 설정으로 고정합니다. 디버그 문은 404/거부로 응답합니다.
③ 취약 vs 안전 코드
VULNERABLE
// debug code shipped to prod
final boolean DEBUG = true; // never flipped off
@GetMapping("/account")
String account(HttpServletRequest r){
if ("1".equals(r.getParameter("debug"))) {
return dump(Config.ALL) + // secrets!
"\nstack=" + currentStack();
}
return view();
}
// hidden helper left in the router
@GetMapping("/test/login-as")
String loginAs(String user){
grantAdmin(user); // auth bypass
return "logged in as " + user;
}
log.debug("issued token=" + jwt); // token in logs
SECURE
// debug gated by compile-time/env flag
static final boolean DEBUG =
Boolean.parseBoolean(env("DEBUG","false")); // false in prod
@GetMapping("/account")
String account(HttpServletRequest r){
// if(DEBUG) branch removed by dead-code
// elimination when DEBUG is a final false
return view();
}
// /test/* only mounted in non-prod profile
@Profile("!production")
@Configuration class TestRoutes { /* ... */ }
// secure log config: level fixed, secrets masked
log.debug("issued token={}", mask(jwt)); // ****
방어 핵심: 디버그 동작은 운영에서 false인 단일 설정 플래그로 제어 · 빌드 플래그/데드코드 제거로 릴리스 산출물에서 디버그 분기를 삭제 · 운영에는
/test/*·백도어 라우트를 절대 마운트하지 않음(프로파일/피처 게이트) · 인증 우회 지름길 금지 · 로그의 시크릿·토큰 마스킹 및 로그 레벨 운영 설정 고정 · 릴리스 전 잔존 디버그 마커 스캔/린트.
⚠️ 교육용 시뮬레이션입니다. 서버·시크릿·토큰·라우트는 모두 페이지 내부 mock이며 실제 시스템·자격증명·백도어는 없습니다.