security: KVE-2026-2346 CRLF 설치본의 DB 업그레이드 중단 수정
This commit is contained in:
@@ -52,6 +52,14 @@ php bin/check-migration-schema.php
|
||||
|
||||
2026-09-10 검증 결과: PHP 8.4.22, MySQL 8.0.45에서 HTTP·DB 검사 420개와 PHP 문법 검사, 신규 설치/마이그레이션 스키마 대조가 통과했다.
|
||||
|
||||
### Windows CRLF 설치본 업그레이드 보완
|
||||
|
||||
추가 설치 검증에서 기존 `@skip-if-column` 파서가 CRLF 줄 끝을 인식하지 못해, 이미 존재하는 `cf_email_certify_minutes` 컬럼을 다시 추가하려다 오류 1060으로 중단되는 문제가 재현됐다. 이 선행 오류 때문에 KCP 마이그레이션까지 실행되지 않았다. 파서가 CRLF 및 줄 끝 공백·탭을 허용하도록 수정했다. 인자 구분은 수평 공백으로 제한하여 불완전한 지시문이 다음 줄을 소비하지 않도록 했으며, SQL 파일과 체크섬 산정 방식은 변경하지 않았다.
|
||||
|
||||
`php tests/migration_line_endings.php`로 LF·CRLF, 줄 끝 공백·탭, 파일 끝 지시문, 불완전한 지시문 및 원본 체크섬 보존을 포함한 13개 검사가 통과했다.
|
||||
|
||||
PHP 8.4.25·MySQL 8.0.46의 별도 Windows 프로세스와 전용 DB에서 수정 전 소스를 실제 설치하고, 비회원 PC·회원 모바일 HTTP 주문 2건을 저장한 뒤 수정 소스를 적용했다. 원본 CRLF SQL을 유지한 상태에서 실제 관리자 로그인·CSRF 토큰을 사용한 전체 DB 업그레이드 및 KCP 마이그레이션 재실행이 통과했다. 기존 주문 금액·상태와 과거 상점코드 빈값이 유지됐고, 통보 테이블 컬럼이 신규 설치본과 일치했다. 결과는 `tests/reports/kve-2026-2346-migration-fixed.json`에 기록했다. VM·컨테이너 및 실제 KCP 발급·입금 연동 시험은 포함하지 않았다.
|
||||
|
||||
## 배포·회신 상태
|
||||
|
||||
코드 변경 및 격리 회귀 검증 단계다. 신고 대상은 5.6.35이며 검토 기준 5.6.37 개발 코드에도 원인이 있었다. 전체 영향 버전, 수정 배포 버전·배포일은 미확정이다. 실제 KCP 연동 시험, 구형 발신 범위 확인, 운영 반영, KISA 회신 완료를 의미하지 않는다. 원본 PDF는 저장소에 포함하지 않는다.
|
||||
|
||||
@@ -110,7 +110,7 @@ function g5_migration_parse_file($file)
|
||||
$migration['description'] = trim($matches[1]);
|
||||
}
|
||||
|
||||
if (preg_match('/^--\s*@skip-if-column\s+(\S+)\s+(\S+)$/mi', $contents, $matches)) {
|
||||
if (preg_match('/^--[\t ]*@skip-if-column[\t ]+(\S+)[\t ]+(\S+)[\t ]*\r?$/mi', $contents, $matches)) {
|
||||
$migration['skip_table'] = g5_migration_replace_placeholders(trim($matches[1], '`'));
|
||||
$migration['skip_column'] = trim($matches[2], '`');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// 실행: php tests/migration_line_endings.php (DB 연결 불필요)
|
||||
define('_GNUBOARD_', true);
|
||||
define('G5_TABLE_PREFIX', 'g5_');
|
||||
$g5 = array('config_table' => 'g5_config');
|
||||
require dirname(__DIR__) . '/lib/migration.lib.php';
|
||||
|
||||
$directory = sys_get_temp_dir() . '/g5-migration-' . uniqid('', true);
|
||||
mkdir($directory);
|
||||
$file = $directory . '/20260904_001_add_email_certify_minutes.sql';
|
||||
$count = 0;
|
||||
try {
|
||||
foreach (array("\n", "\r\n") as $newline) {
|
||||
foreach (array('', ' ', "\t") as $trailing) {
|
||||
foreach (array(false, true) as $atEnd) {
|
||||
$directive = '-- @skip-if-column {{config_table}} cf_email_certify_minutes' . $trailing;
|
||||
$sql = 'ALTER TABLE `{{config_table}}` ADD `cf_email_certify_minutes` int;';
|
||||
$contents = $atEnd ? $sql . $newline . $directive : $directive . $newline . $sql . $newline;
|
||||
file_put_contents($file, $contents);
|
||||
$parsed = g5_migration_parse_file($file);
|
||||
if ($parsed['skip_table'] !== 'g5_config' || $parsed['skip_column'] !== 'cf_email_certify_minutes'
|
||||
|| $parsed['checksum'] !== hash('sha256', $contents) || count($parsed['statements']) !== 1) {
|
||||
throw new RuntimeException('줄바꿈·공백·EOF 조건 또는 원본 체크섬 보존 실패');
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 불완전한 지시문의 컬럼을 다음 줄에서 가져오면 안 된다.
|
||||
file_put_contents($file, "-- @skip-if-column {{config_table}}\r\nSELECT 1;\r\n");
|
||||
$parsed = g5_migration_parse_file($file);
|
||||
if ($parsed['skip_table'] !== '' || $parsed['skip_column'] !== '') {
|
||||
throw new RuntimeException('지시문이 다음 줄을 소비함');
|
||||
}
|
||||
$count++;
|
||||
echo $count . "개 마이그레이션 줄바꿈 검사 통과\n";
|
||||
} finally {
|
||||
unlink($file);
|
||||
rmdir($directory);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"upgrade": "passed_with_original_CRLF_SQL",
|
||||
"rerun": "passed",
|
||||
"real_http_orders_preserved": 2,
|
||||
"admin_login": "real",
|
||||
"csrf_token": "real",
|
||||
"historical_site_codes": "empty",
|
||||
"notification_schema": "matches_fresh_install",
|
||||
"sql_files_modified": false
|
||||
}
|
||||
Reference in New Issue
Block a user