변경 파일 목록
총 1개
Component (1)
Validator.phpValidator/변경 파일 코드
추가삭제
변경 전Validator.php
| } | |
| /** | |
| * 웹쉘이 추가된 이미지 업로드시 eval 포함여부 검증 | |
| * | |
| * @param string $tmpName | |
| * @return bool | |
| */ | |
| public static function validateIncludeEval($tmpName) { | |
| try { | |
| $fp = fopen($tmpName, 'rb'); | |
| $text = fread($fp, filesize($tmpName)); | |
| fclose($fp); | |
| /* | |
| * 1차 : preg_math_all 을 통해 php 구문이 존재할 경우 구문안의 문자열 추출 | |
| * 2차 : 구문안의 문자열 중 <? 존재할 경우 explode 배열 만든 후 마지막 <? 데이터만 추출 (예 : <? 문자1 <? 문자2 <? 문자3 ?>) | |
| * 3차 : 최종 추출한 문자열에 불가 문자 여부를 검출 (대소문자 구분 x) | |
| */ | |
| $checkContent = nl2br(str_replace(["\r\n", "\r", "\n"], "", $text)); | |
| if (preg_match_all('/(?<=\<\?)(.*?)(?=\?>)/', $checkContent, $matches)) { | |
| for ($i = 0; $i < gd_count($matches[0]); $i++) { | |
| $checkText = $matches[1][$i]; | |
| $checkTextArr = explode('<?', $checkText); | |
| $checkVal = $checkTextArr[gd_array_last_key($checkTextArr)]; | |
| // 추출 문자내 불가 문자 검증 | |
| if (preg_match('/eval|\<\?php|\$_POST|\$_GET|\$_REQUEST/i', $checkVal)) { | |
| return false; | |
| } | |
| } | |
변경 후Validator.php
| } | |
| /** | |
| * 웹쉘이 추가된 이미지 업로드시 PHP 코드 포함여부 검증 | |
| * | |
| * @param string $tmpName | |
| * @return bool true=안전, false=차단 | |
| */ | |
| public static function validateIncludeEval($tmpName) { | |
| try { | |
| $text = file_get_contents($tmpName); | |
| if ($text === false) { | |
| return false; | |
| } | |
| /* | |
| * PHP 열림 태그(<?php / <?=)부터 닫힘 태그(또는 파일 끝)까지 추출 — 닫힘 태그 우회 차단 + 바이너리 false positive 회피. | |
| * 열림 태그를 <?php / <?= 로 한정: 바이너리에 우연히 나온 <? 가 거대한 가짜 PHP 블록을 만들어 정상 이미지를 오탐하던 문제 차단. | |
| * /s 모디파이어로 . 가 newline 도 매칭하여 nl2br 트릭 불필요. /i 로 <?PHP 대소문자 무시. | |
| */ | |
| if (preg_match_all('/<\?(?:php|=)(.*?)(?:\?>|$)/si', $text, $matches)) { | |
| $dangerPatterns = [ | |
| 'variable_access' => [ | |
| 'description' => '슈퍼글로벌 변수 및 변수 변수 접근', | |
| 'patterns' => [ | |
| '\$_(?:GET|POST|REQUEST|COOKIE|SERVER|FILES|ENV|SESSION)', | |
| '\$\{\s*[\'"]?_(?:GET|POST|REQUEST|COOKIE|SERVER|FILES|ENV|SESSION)', | |
| '\$\$\w+', | |
| ] | |
| ], | |
| 'code_execution' => [ | |
| 'description' => '코드 실행 함수', | |
| 'patterns' => [ | |
| '\beval\s*\(', | |
| '\bassert\s*\(', | |
| '\bcreate_function\s*\(', | |
| ] | |
| ], | |
| 'system_commands' => [ | |
| 'description' => '시스템 명령 실행', | |
| 'patterns' => [ | |
| '\b(?:system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec)\s*\(', | |
| ] | |
| ], | |
| 'file_operations' => [ | |
| 'description' => '파일 포함 및 읽기/쓰기', | |
| 'patterns' => [ | |
| '\b(?:include|require)(?:_once)?\b', | |
| '\b(?:file_get_contents|file_put_contents|fopen|readfile|file)\s*\(', | |
| ] | |
| ], | |
| 'dynamic_calls' => [ | |
| 'description' => '동적 함수 호출', | |
| 'patterns' => [ | |
| '\bcall_user_func(?:_array)?\s*\(', | |
| ] | |
| ], | |
| 'encoding_functions' => [ | |
| 'description' => '인코딩/디코딩 함수 (코드 실행 가능)', | |
| 'patterns' => [ | |
| '\b(?:base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin|convert_uudecode)\s*\(', | |
| ] | |
| ], | |
| 'regex_functions' => [ | |
| 'description' => '정규식 함수 (코드 실행 가능)', | |
| 'patterns' => [ | |
| '\bpreg_replace\s*\(', | |
| ] | |
| ], | |
| 'stream_wrappers' => [ | |
| 'description' => 'PHP 스트림 래퍼', | |
| 'patterns' => [ | |
| 'php:\/\/', | |
| ] | |
| ], | |
| ]; | |
| $combinedPattern = '/' . implode('|', array_merge(...array_column($dangerPatterns, 'patterns'))) . '/i'; | |
| foreach ($matches[1] as $phpContent) { | |
| if (preg_match($combinedPattern, $phpContent)) { | |
| return false; | |
| } | |
| } | |