Devin.KR

복구와 재발 방지 검증

80분 안팎

학습 목표

자료 무결성과 정상 접근 복구를 확인합니다.

개념

복구 파일보다 복구 조건이 먼저입니다

자료 파일을 되돌렸다고 정상 운영이 회복된 것은 아닙니다. 정상 소유자가 자료를 읽을 수 있고 타인은 계속 거절되며 폐기한 세션이 다시 살아나지 않아야 합니다. 탐지 규칙도 변경 전과 같은 경계를 지켜야 합니다. 복구 담당자는 백업의 출처와 생성 조건을 확인한 뒤 쓸 파일을 정합니다. 이 레슨은 손상된 합성 SQLite 파일을 복구하는 훈련이며 실제 유출이나 공격자가 제거됐음을 입증하는 시험은 아닙니다.

일관된 백업만 비교합니다

실습 store.py는 요청마다 SQLite 연결을 열고 닫습니다. 모든 생성 요청이 끝난 뒤 연결이 닫힌 파일을 복사하므로 WAL이나 동시 쓰기를 사용하지 않는 fixture입니다. 실제 쓰기가 계속되는 DB 파일을 이 방법으로 복사하면 일관된 백업이 아닐 수 있습니다. 별도 백업 API나 서비스에 맞는 절차가 필요합니다. WAL 사이드 파일과 여러 프로세스가 있는 환경에 이 단일 파일 복사 코드를 적용하지 않는 이유를 제출물에서 설명합니다.

기대 해시의 출처

복구 시 backup_hash_mismatch가 나오면 대상 파일을 바꾸기 전에 중단합니다. expected는 백업을 정상 상태에서 보존했을 때 기록한 해시입니다. 지금 손상됐을 수 있는 백업을 다시 해시해 expected로 넣으면 비교는 통과해도 신뢰 기준이 사라집니다. 해시 기록과 백업이 함께 공격자에게 바뀔 수 있다는 한계도 있습니다. 실습은 고정된 정상 자료 생성 뒤 해시를 기억하는 방식이며 외부 보관과 출처 인증은 구현하지 않습니다.

검증 후 교체합니다

restore는 백업 바이트를 읽어 기대 해시와 비교하고 target.restore에 새 파일을 쓴 다음 renameSync로 대상 이름을 교체합니다. stage가 이미 있으면 wx 때문에 실패하므로 이전 실패 흔적을 먼저 조사합니다. 같은 디렉터리에 쓰는 이유는 파일 교체 위치를 단순하게 만들기 위한 것입니다. 디스크 오류·권한·시스템 중단에 대한 내구성 전체를 보장하는 구현은 아닙니다. 검사와 교체 중 동시 쓰기를 허용하지 않는 훈련 조건을 유지합니다.

나쁜 백업의 거절을 시험합니다

테스트는 target을 damaged로 만든 뒤 틀린 기대 해시로 복구를 요청합니다. 이때 예외가 나고 target은 여전히 damaged여야 합니다. 대상이 먼저 바뀌면 실패가 있었어도 잘못된 백업을 적용한 것입니다. 그 뒤 올바른 해시를 사용해 복구하고 파일 전체 해시가 같음을 확인합니다. 파일이 같다는 검사와 앱이 정상이라는 검사는 서로 다르므로 이어서 실제 저장소 함수를 통한 자료 조회도 수행합니다.

정상 경로와 거절 경로

Alice가 새로 로그인해 original 내용의 자료를 읽으면 소유자 경로가 살아 있습니다. Bob이 같은 id를 읽을 때 404이면 소유자 정책이 유지됩니다. 옛 Alice와 Bob 세션은 전역 키 교체 후 401이어야 합니다. 상태 코드가 모두 성공인 테스트는 정상 기능만 확인한 것이며 접근 통제가 무너졌는지 보지 못합니다. 반대로 모두 실패인 테스트는 보안처럼 보여도 서비스를 사용할 수 없습니다. 두 종류의 기준을 한 시나리오에 묶습니다.

자료와 계정 상태를 분리합니다

자료 백업에 옛 로그인 상태가 섞이면 복구 과정이 격리를 취소할 수 있습니다. 이 앱은 자료를 SQLite에, 차단 상태와 세션을 메모리에 두므로 자료 복구가 세션을 되살리지 않습니다. 같은 앱 인스턴스를 유지하고 복구 직후 옛 세션 401을 다시 확인합니다. 새 프로세스를 만들면 메모리 차단은 유지되지 않는다는 한계가 있습니다. 복구 성공이라는 단어를 재시작·분산 환경까지 확장하지 않습니다.

격리 해제에는 새 인증이 필요합니다

테스트의 release 호출은 훈련 소유자 확인을 승인 fixture로 대체한 동작입니다. 차단 집합에서 계정을 빼도 기존 세션을 복원하지 않습니다. 새 로그인으로 새 세션을 얻고 접근을 확인합니다. 실제로는 비밀번호 재설정, 계정 소유자 확인, 추가 인증, 담당 승인 같은 조건을 서비스 정책에 맞게 정해야 합니다. 이 교재가 그 절차를 수행했다고 적지 않습니다. 해제 판단과 기술 동작을 분리해 미구현 범위를 남깁니다.

탐지 회귀를 복구 문턱에 둡니다

python3 detection-test.py는 빈 입력·임계값·시간창 끝·시간대·계정과 흐름 키·중복·결측 정책을 다시 검사합니다. 자료를 복구하는 코드는 탐지 함수와 멀리 있어 보여도 최종 산출물을 묶을 때 잘못된 파일을 전달할 수 있습니다. 이전 규칙 버전과 같은 기대 결과를 재실행하면 그 실수를 찾을 수 있습니다. 이 검사는 합성 데이터 규칙 회귀이지 실제 운영 로그 수집기가 살아 있다는 증거는 아닙니다.

실패 메시지를 순서대로 읽습니다

backup_hash_mismatch는 입력 백업 또는 기대 해시의 문제이고 restore_hash_mismatch는 대상 교체 뒤 바이트 불일치입니다. storage_failure는 Python 저장소 실행이나 SQLite 파일 상태를 확인할 신호입니다. 원인을 찾기 위해 자료 본문이나 키를 통째로 출력하지 않습니다. 거절 상태 비교 AssertionError는 expected 401 또는 404와 실제 값을 보고 인증·인가 중 어느 경계가 달라졌는지 나눕니다. 실패한 항목이 남으면 복구 완료로 표시하지 않습니다.

재발 방지 요청을 측정 가능하게 씁니다

보안을 강화하자는 요청 대신 계정 격리 상태를 영속 저장하고 재시작 후에도 신규 로그인·옛 세션을 거절하는 테스트를 추가한다고 씁니다. 담당 app-team과 외부 공개 전이라는 완료 시점을 붙입니다. 결측 로그에는 수집 상태 확인과 누락 경보의 책임자를 정합니다. 모든 결함을 이번 훈련에서 해결했다고 쓰지 않고 이번 구현, 다음 수정 요청, 조사 미확인을 구분해야 인계자가 작업 우선순위를 판단할 수 있습니다.

복구 기준을 설명합니다

제출 시 백업을 만든 상태, 신뢰한 해시의 위치, 잘못된 백업 거절 결과, 정상 자료 조회, 타인 거절, 옛 세션 거절, 탐지 회귀 결과를 묶습니다. 실행 종료 코드와 테스트 의미를 같이 기록하며 PASS 한 줄만으로 실제 복구 시간이나 데이터 유실량을 주장하지 않습니다. 더 읽기의 테스트 장은 격리된 테스트와 실패 읽기 방법을 확장합니다. 마지막 레슨에서는 이 근거를 전체 트랙 보고서의 잔여 위험과 연결하여 다음 담당자에게 전달합니다.

따라하기

백업 검사 후 쓰기 조건

기대 해시 불일치 때 대상이 유지되는 조건을 확인합니다. 실제 파일 복구와 SQLite 조회는 로컬 테스트로 검증합니다.

const {createHash}=require('node:crypto');
const hash=x=>createHash('sha256').update(x).digest('hex');
const backup=Buffer.from('original');let target='damaged';
if(hash(backup)==='wrong')target=backup.toString();
console.log(target);
console.log(hash(backup)===hash(Buffer.from('original')));

실행 결과

damaged
true

복구 문턱을 합칩니다

자료 조회만 성공해도 옛 세션 거절이 실패하면 완료 문턱을 통과하지 못합니다.

const gates={integrity:true,owner:true,foreignDenied:true,oldSessionDenied:false,detection:true};
console.log(Object.entries(gates).filter(([,ok])=>!ok).map(([name])=>name).join(','));
console.log(Object.values(gates).every(Boolean));

실행 결과

oldSessionDenied
false

이전 탐지 한계 유지

복구 중 새 로그를 확보하지 않았다면 앞 모듈의 조사 필요·관찰 공백 표시를 그대로 유지합니다.

const result={alerts:[{classification:'review'}],coverage:'unknown',missing:['m1']};
console.log(result.alerts[0].classification);
console.log(result.coverage,result.missing.join(','));

실행 결과

review
unknown m1

앱에서 회귀 검사 실행

해당 실습 ZIP 루트에서 다음 명령을 실행합니다. starter는 지정 TODO의 AssertionError로 실패합니다. 수정 뒤 각 PASS 행과 종료 코드 0을 확인합니다. 외부 Docker 검사는 별도 대기입니다.

bash check.sh

확인 문제

실습

ZIP 루트에서 bash check.sh를 실행합니다. auth.cjs의 격리·해제와 response.cjs의 복구 전 해시 검사를 완성합니다. 잘못된 백업 때 대상 보존, 정상 백업 무결성, 새 소유자 세션 조회, 타인 404, 옛 세션 401 및 탐지 회귀를 확인합니다. 테스트 기준은 바꾸지 않습니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

종료 코드 0, 복구·세션·탐지 회귀 통과

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 취약점 수정 전후의 테스트 내용을 설명합니다.