본문으로 건너뛰기

WebFTP 보안 강화

PHP 8 · GODO26
변경 파일 수: 1개
변경 파일 목록
총 1개
변경 파일 코드
추가삭제
Controller/Admin/Share/PopupWebftpPsController.php+59
변경 전PopupWebftpPsController.php
]
);
}
 
 
 
 
 
 
 
 
 
 
 
FileHandler::delete(iconv('UTF-8', 'CP949', $file), true);
$this->json(
[
if (!Request::request()->has('target')) {
throw new \InvalidArgumentException(__('변경할 이름을 입력해주세요.'));
}
 
 
 
 
 
 
 
 
 
if (getenv('setting') == 'y') {
FileHandler::rename(iconv('UTF-8', 'CP949', $file), iconv('UTF-8', 'CP949', Request::request()->get('target')));
} else {
exit;
}
 
 
 
 
 
 
// 한글 파일명 업로드를 위해 인코딩 변경 처리
move_uploaded_file($tmpName[0], iconv('UTF-8', 'CP949', $file . '/' . $name[0]));
break;
}
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
변경 후PopupWebftpPsController.php
]
);
}
 
// 목록에 없는 경로를 직접 보내 상점 폴더 밖을 지우는 것을 막는다 — data/../../route.php
if ($this->isOutsideDataPath($file)) {
$this->json(
[
'error' => 1,
'message' => __('삭제할 파일이 없습니다.'),
]
);
}
 
FileHandler::delete(iconv('UTF-8', 'CP949', $file), true);
$this->json(
[
if (!Request::request()->has('target')) {
throw new \InvalidArgumentException(__('변경할 이름을 입력해주세요.'));
}
 
// 세팅 모드 target 은 앞단에서 USERPATH 로 치환돼 들어오므로 아래 호출부와 같은 방식으로 조립한다
$renameTarget = getenv('setting') == 'y' ? Request::request()->get('target') : USERPATH . Request::request()->get('target');
 
// 원본은 밖의 파일을 건드리는 것을(route.php → x.js), 대상은 밖으로 빼내는 것을(a.png → ../../evil.js) 막는다
if ($this->isOutsideDataPath($file) || $this->isOutsideDataPath($renameTarget)) {
throw new \InvalidArgumentException(__('잘못된 경로로 접근하셨습니다.'));
}
 
if (getenv('setting') == 'y') {
FileHandler::rename(iconv('UTF-8', 'CP949', $file), iconv('UTF-8', 'CP949', Request::request()->get('target')));
} else {
exit;
}
 
// 저장 위치와 파일명이 합쳐진 최종 경로로 판정한다 — 둘 중 어느 쪽에 상위 이동이 섞여도 걸린다
if ($this->isOutsideDataPath($file . '/' . $name[0])) {
throw new \InvalidArgumentException(__('잘못된 경로로 접근하셨습니다.'));
}
 
// 한글 파일명 업로드를 위해 인코딩 변경 처리
move_uploaded_file($tmpName[0], iconv('UTF-8', 'CP949', $file . '/' . $name[0]));
break;
}
}
}
 
/**
* 작업 대상이 상점 data 폴더 밖을 가리키는지 확인한다.
*
* @param string $path 파일 처리 함수에 넘어갈 경로
*
* @return bool data 폴더 밖이면 true
*/
protected function isOutsideDataPath(string $path): bool
{
// 세팅 모드 파일 경로는 앞에 슬래시가 하나 더 붙어 들어온다 — //shop/user/data/a.png
$targetPath = preg_replace('#/+#', '/', $path);
 
// 세팅 모드는 화면이 넘긴 현재 경로가 상대형으로 온다 — data/logo.png
if (strpos($targetPath, USERPATH) !== 0) {
$targetPath = USERPATH . ltrim($targetPath, '/');
}
 
// 상위 이동을 해석해 실제로 가리키는 경로를 구한다 — data/skin/../../route.php 는 상점 폴더 밖
$resolved = [];
foreach (explode('/', $targetPath) as $segment) {
if ($segment === '.') {
continue;
}
if ($segment === '..') {
array_pop($resolved);
continue;
}
$resolved[] = $segment;
}
$resolvedPath = implode('/', $resolved);
 
return strpos($resolvedPath, USERPATH . 'data/') !== 0;
}
}