POSA_LEAKSMS/scripts/backup_evidence.php
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

79 lines
2.7 KiB
PHP

<?php
// =============================================================================
// backup_evidence.php - generate backup and restore-test evidence markdown
//
// Usage:
// php scripts/backup_evidence.php --backup-dir C:\path\to\backups --restore-test "2026-05 restore OK"
// =============================================================================
if (php_sapi_name() !== 'cli') {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo "Forbidden\n";
exit;
}
$root = dirname(__DIR__);
$backupDir = $root . DIRECTORY_SEPARATOR . 'backups';
$restoreTest = 'not recorded';
for ($i = 1; $i < count($argv); $i++) {
if ($argv[$i] === '--backup-dir' && isset($argv[$i + 1])) {
$backupDir = $argv[++$i];
} elseif ($argv[$i] === '--restore-test' && isset($argv[$i + 1])) {
$restoreTest = $argv[++$i];
}
}
function backup_evidence_bool(bool $value): string {
return $value ? 'yes' : 'no';
}
function backup_evidence_files(string $dir): array {
if (!is_dir($dir)) {
return [];
}
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
$files[] = [
'path' => $file->getPathname(),
'size' => $file->getSize(),
'mtime' => date('Y-m-d H:i:s', $file->getMTime()),
];
}
usort($files, fn($a, $b) => strcmp($b['mtime'], $a['mtime']));
return $files;
}
$files = backup_evidence_files($backupDir);
$generatedAt = date('Y-m-d H:i:s');
$configLocal = $root . DIRECTORY_SEPARATOR . 'php' . DIRECTORY_SEPARATOR . 'config.local.php';
$adminAudit = $root . DIRECTORY_SEPARATOR . 'php' . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'admin_audit.log';
echo "# backup-evidence\n\n";
echo "- generated_at: {$generatedAt}\n";
echo "- backup_dir: {$backupDir}\n";
echo "- backup_file_count: " . count($files) . "\n";
echo "- restore_test: {$restoreTest}\n";
echo "- config_local_present: " . backup_evidence_bool(is_file($configLocal)) . "\n";
echo "- admin_audit_log_present: " . backup_evidence_bool(is_file($adminAudit)) . "\n\n";
echo "## Backup Files\n\n";
echo "| modified_at | size_bytes | path |\n";
echo "|---|---:|---|\n";
foreach (array_slice($files, 0, 50) as $file) {
$safePath = str_replace('|', '/', $file['path']);
echo "| {$file['mtime']} | {$file['size']} | {$safePath} |\n";
}
if (!$files) {
echo "| - | 0 | no backup files found |\n";
}
echo "\n## Restore Evidence\n\n";
echo "Record the latest restore drill result in `--restore-test`. Include DB restore, login, SMS test, and monthly report checks.\n";