본문으로 건너뛰기

zip 압축·파일 처리 보안 강화

PHP 7 · GODO25
변경 파일 수: 4개
변경 파일 목록
총 4개
변경 파일 코드
추가삭제
Component/File/Webftp.php+89-23
변경 전Webftp.php
namespace Bundle\Component\File;
 
use Component\Validator\Validator;
 
use Framework\Utility\ArrayUtils;
use Framework\Utility\FileUtils;
use App;
//change directory so the zip file doesnt have a tree structure in it.
chdir($directory);
 
// TODO: Probably we have to parse exclude list more carefully
$excludeList = implode(' ', array_merge($this->_config['hidden_files'], ['index.php']));
$excludeList = str_replace("*", "\*", $excludeList);
 
if ($this->_config['zip_stream']) {
 
 
 
// zip the stuff (dir and all in there) into the streamed zip file
$stream = popen('/usr/bin/zip -' . $this->_config['zip_compression_level'] . ' -r -q - * -x ' . $excludeList, 'r');
 
if ($stream) {
fpassthru($stream);
fclose($stream);
}
 
} else {
 
// get a tmp name for the .zip
$tmpZip = tempnam('tmp', 'tempzip') . '.zip';
 
// zip the stuff (dir and all in there) into the tmp_zip file
exec('zip -' . $this->_config['zip_compression_level'] . ' -r ' . $tmpZip . ' * -x ' . $excludeList);
 
// calc the length of the zip. it is needed for the progress bar of the browser
$filesize = filesize($tmpZip);
header("Content-Length: $filesize");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// deliver the zip file
$fp = fopen($tmpZip, 'r');
echo fpassthru($fp);
 
 
 
 
 
 
 
 
 
 
// clean up the tmp zip file
unlink($tmpZip);
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
/**
 
변경 후Webftp.php
namespace Bundle\Component\File;
 
use Component\Validator\Validator;
use Framework\File\Archive\ArchiveHandler;
use Framework\Utility\ArrayUtils;
use Framework\Utility\FileUtils;
use App;
//change directory so the zip file doesnt have a tree structure in it.
chdir($directory);
 
$excludePatterns = array_merge($this->_config['hidden_files'], ['index.php']);
$targetList = $this->_collectZipTargets('.', $excludePatterns);
 
 
// ZipArchive 는 스트림 출력을 지원하지 않아 임시 파일을 거쳐 전달한다
// 확장자를 덧붙이면 대상이 없어 아카이브를 만들지 않았을 때 존재하지 않는 경로를 다루게 된다
$tmpZip = tempnam(sys_get_temp_dir(), 'tempzip');
 
if (!empty($targetList)) {
(new ArchiveHandler())->create($tmpZip, $targetList, null, 'zip');
}
 
 
 
 
 
 
 
 
 
 
if (!$this->_config['zip_stream']) {
 
 
// calc the length of the zip. it is needed for the progress bar of the browser
$filesize = filesize($tmpZip);
header("Content-Length: $filesize");
}
 
// deliver the zip file
$fp = fopen($tmpZip, 'r');
 
if ($fp) {
fpassthru($fp);
fclose($fp);
}
 
// clean up the tmp zip file
unlink($tmpZip);
}
}
 
/**
* Collects relative paths of files to be archived.
*
* @param string $directory Relative path of directory to collect
* @param array $excludePatterns fnmatch patterns to exclude
*
* @return array Relative paths of target files
* @access protected
*/
protected function _collectZipTargets(string $directory, array $excludePatterns): array
{
$targetList = [];
 
try {
$iterator = new \DirectoryIterator($directory);
} catch (\UnexpectedValueException $e) {
// 기존 zip -r 도 열 수 없는 디렉토리는 건너뛰고 나머지를 계속 압축했다
return $targetList;
}
 
foreach ($iterator as $item) {
if ($item->isDot()) {
continue;
}
 
// 최상위 dot file 은 기존 shell glob(*) 이 걸러내던 대상
if ($directory === '.' && strpos($item->getFilename(), '.') === 0) {
continue;
}
 
$relativePath = $directory === '.' ? $item->getFilename() : $directory . '/' . $item->getFilename();
 
if ($item->isDir()) {
$collectedList = $this->_collectZipTargets($relativePath, $excludePatterns);
 
if (!empty($collectedList)) {
array_push($targetList, ...$collectedList);
}
continue;
}
 
if ($this->_isExcluded($relativePath, $excludePatterns)) {
continue;
}
 
$targetList[] = $relativePath;
}
 
return $targetList;
}
 
/**
* Checks if the file matches any exclude pattern.
*
* @param string $relativePath Relative path of file to check
* @param array $excludePatterns fnmatch patterns to exclude
*
* @return bool
* @access protected
*/
protected function _isExcluded(string $relativePath, array $excludePatterns): bool
{
foreach ($excludePatterns as $excludePattern) {
if (fnmatch($excludePattern, $relativePath)) {
return true;
}
}
 
return false;
}
 
/**
 
Component/Sms/SmsSender.php+2-4
변경 전SmsSender.php
use Component\Sms\SmsLog;
use Component\Sms\SmsUtil;
use Component\Validator\Validator;
 
use Framework\Security\CredentialsFile;
use Framework\Security\Otp;
use Framework\Utility\ComponentUtils;
\FileHandler::delete($compressFileName, true);
}
// 압축 대상
$compressTarget = self::SMS_LARGE_TXT_FILE_NAME;
// 파일 압축
exec('cd ' . $this->_smsLargeFilePath . ' && zip -r "' . $compressFileName . '" ' . $compressTarget);
// 압축파일 체크
if (\FileHandler::isExists($compressFileName) === false) {
 
변경 후SmsSender.php
use Component\Sms\SmsLog;
use Component\Sms\SmsUtil;
use Component\Validator\Validator;
use Framework\File\Archive\ArchiveHandler;
use Framework\Security\CredentialsFile;
use Framework\Security\Otp;
use Framework\Utility\ComponentUtils;
\FileHandler::delete($compressFileName, true);
}
 
 
 
// 파일 압축
(new ArchiveHandler())->create($compressFileName, [$tmpFilePath], null, 'zip', $this->_smsLargeFilePath);
// 압축파일 체크
if (\FileHandler::isExists($compressFileName) === false) {
 
Component/Worker/DbUrl.php+21-6
변경 전DbUrl.php
$this->mergedFilePath = $path;
if($this->totalDburlData >= $this->dburl_max_count || $lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
exec('cp ' . $path . " " . $path.'_back_up');
return true;
}
 
$this->mergedFilePath = $path;
if($this->totalDburlData >= $this->dburl_max_count || $lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
exec('cp ' . $path . " " . $path.'_back_up');
return true;
}
 
$this->mergedFilePath = $path;
if(/*$this->totalDburlData >= $this->dburl_max_count || */$lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
exec('cp ' . $path . " " . $path.'_back_up');
return true;
}
}
*/
private function fileMerge($tmpFileCnt, $path)
{
//초기화
exec('cat /dev/null > ' . $path);
 
for ($num = 0; $num < $tmpFileCnt; $num++) {
$tmpFileName = $path . '_tmp_' . $num;
//머지 후 삭제
if (is_file($tmpFileName) === true) {
exec('cat ' . $tmpFileName . ' >> ' . $path);
 
 
 
 
 
 
 
 
 
 
unlink($tmpFileName);
}
}
 
 
 
 
 
$this->isMerge = true;
}
 
 
변경 후DbUrl.php
$this->mergedFilePath = $path;
if($this->totalDburlData >= $this->dburl_max_count || $lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
copy($path, $path . '_back_up');
return true;
}
 
$this->mergedFilePath = $path;
if($this->totalDburlData >= $this->dburl_max_count || $lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
copy($path, $path . '_back_up');
return true;
}
 
$this->mergedFilePath = $path;
if(/*$this->totalDburlData >= $this->dburl_max_count || */$lastPageNum == $page) {
$this->fileMerge($this->totalDburlPage, $path);
copy($path, $path . '_back_up');
return true;
}
}
*/
private function fileMerge($tmpFileCnt, $path)
{
// 쓰기 모드로 한 번만 열어 초기화까지 겸한다
$mergedHandle = fopen($path, 'wb');
 
for ($num = 0; $num < $tmpFileCnt; $num++) {
$tmpFileName = $path . '_tmp_' . $num;
//머지 후 삭제
if (is_file($tmpFileName) === true) {
$tmpHandle = fopen($tmpFileName, 'rb');
 
if ($tmpHandle !== false) {
if ($mergedHandle !== false) {
// 대용량 파일을 메모리에 적재하지 않도록 스트림으로 이어 붙인다
stream_copy_to_stream($tmpHandle, $mergedHandle);
}
 
fclose($tmpHandle);
}
 
unlink($tmpFileName);
}
}
 
if ($mergedHandle !== false) {
fclose($mergedHandle);
}
 
$this->isMerge = true;
}
 
 
Controller/Admin/Share/PopupWebftpController.php+3
변경 전PopupWebftpController.php
}
 
if (Request::get()->has('zip')) {
 
 
 
$dirArray = $webftp->zipDirectory(Request::get()->get('zip'));
} else {
// Initialize the directory array
 
변경 후PopupWebftpController.php
}
 
if (Request::get()->has('zip')) {
// ZipArchive 압축은 스크립트 실행시간에 포함되어 대용량 디렉토리에서 기본 30초를 초과할 수 있다
set_time_limit(RUN_TIME_LIMIT);
 
$dirArray = $webftp->zipDirectory(Request::get()->get('zip'));
} else {
// Initialize the directory array