From 0242e8f1a32bfb99ced44ed33adc80c672960dbe Mon Sep 17 00:00:00 2001 From: whitedot Date: Mon, 31 Aug 2026 18:22:00 +0900 Subject: [PATCH] =?UTF-8?q?security:=20SVG=20=EB=B0=8F=20SVGZ=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=B0=A8=EB=8B=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 게시판, 1:1문의, 폼메일 및 쇼핑몰 설정의 클라이언트와 서버 검증에서 SVG 계열 파일 업로드를 거부한다. --- adm/shop_admin/configformupdate.php | 10 ++++ bbs/formmail_send.php | 19 +++++- bbs/qawrite_update.php | 9 +++ bbs/write_update.php | 9 +++ extend/version.extend.php | 2 +- js/common.js | 92 ++++++++++++++++++++++++++++- lib/common.lib.php | 20 +++++++ 7 files changed, 156 insertions(+), 5 deletions(-) diff --git a/adm/shop_admin/configformupdate.php b/adm/shop_admin/configformupdate.php index 74f492807..f3f263cad 100644 --- a/adm/shop_admin/configformupdate.php +++ b/adm/shop_admin/configformupdate.php @@ -15,6 +15,16 @@ if(! (isset($_POST['de_admin_company_tel']) && check_vaild_callback($_POST['de_a // 로그인을 바로 이 주소로 하는 경우 쇼핑몰설정값이 사라지는 현상을 방지 if (!$_POST['de_admin_company_owner']) goto_url("./configform.php"); +$logo_img_fields = array('logo_img', 'logo_img2', 'mobile_logo_img', 'mobile_logo_img2'); +foreach ($logo_img_fields as $logo_img_field) { + if (isset($_FILES[$logo_img_field]['name']) && $_FILES[$logo_img_field]['name']) { + $filename = get_safe_filename($_FILES[$logo_img_field]['name']); + if (is_disallowed_svg_filename($filename)) { + alert('허용되지 않는 파일 확장자입니다. (svg, svgz)'); + } + } +} + if (! empty($_POST['logo_img_del'])) @unlink(G5_DATA_PATH."/common/logo_img"); if (! empty($_POST['logo_img_del2'])) @unlink(G5_DATA_PATH."/common/logo_img2"); if (! empty($_POST['mobile_logo_img_del'])) @unlink(G5_DATA_PATH."/common/mobile_logo_img"); diff --git a/bbs/formmail_send.php b/bbs/formmail_send.php index 3d43aebae..355524f35 100644 --- a/bbs/formmail_send.php +++ b/bbs/formmail_send.php @@ -28,9 +28,22 @@ if (!preg_match("/([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)\.([0-9a-zA-Z_-]+)/", $to)){ } $file = array(); + +// 첨부파일을 이동하기 전에 모든 파일명을 먼저 확인한다. for ($i=1; $i<=$attach; $i++) { - if ($_FILES['file'.$i]['name']) - $file[] = attach_file($_FILES['file'.$i]['name'], $_FILES['file'.$i]['tmp_name']); + $file_key = 'file'.$i; + if (isset($_FILES[$file_key]['name']) && $_FILES[$file_key]['name']) { + $filename = get_safe_filename($_FILES[$file_key]['name']); + if (is_disallowed_svg_filename($filename)) { + alert_close('허용되지 않는 파일 확장자입니다. (svg, svgz)'); + } + } +} + +for ($i=1; $i<=$attach; $i++) { + $file_key = 'file'.$i; + if (isset($_FILES[$file_key]['name']) && $_FILES[$file_key]['name']) + $file[] = attach_file($_FILES[$file_key]['name'], $_FILES[$file_key]['tmp_name']); } $content = stripslashes($content); @@ -78,4 +91,4 @@ include_once(G5_PATH.'/head.sub.php'); alert_close('메일을 정상적으로 발송하였습니다.'); -include_once(G5_PATH.'/tail.sub.php'); \ No newline at end of file +include_once(G5_PATH.'/tail.sub.php'); diff --git a/bbs/qawrite_update.php b/bbs/qawrite_update.php index 3c0397d1a..35d63cfef 100644 --- a/bbs/qawrite_update.php +++ b/bbs/qawrite_update.php @@ -133,6 +133,15 @@ if($w == 'u' || $w == 'a' || $w == 'r') { } } +if (isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) { + foreach ($_FILES['bf_file']['name'] as $filename) { + $filename = get_safe_filename($filename); + if (is_disallowed_svg_filename($filename)) { + alert('허용되지 않는 파일 확장자입니다. (svg, svgz)'); + } + } +} + // 파일개수 체크 $file_count = 0; $upload_count = isset($_FILES['bf_file']['name']) ? count($_FILES['bf_file']['name']) : 0; diff --git a/bbs/write_update.php b/bbs/write_update.php index be65214be..e851908b4 100644 --- a/bbs/write_update.php +++ b/bbs/write_update.php @@ -137,6 +137,15 @@ for ($i=1; $i<=10; $i++) { } } +if (isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) { + foreach ($_FILES['bf_file']['name'] as $filename) { + $filename = get_safe_filename($filename); + if (is_disallowed_svg_filename($filename)) { + alert('허용되지 않는 파일 확장자입니다. (svg, svgz)'); + } + } +} + @include_once($board_skin_path.'/write_update.head.skin.php'); run_event('write_update_before', $board, $wr_id, $w, $qstr); diff --git a/extend/version.extend.php b/extend/version.extend.php index 086d5a9cc..00235bf44 100644 --- a/extend/version.extend.php +++ b/extend/version.extend.php @@ -5,4 +5,4 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 // 예) https://도메인/css/default.css?ver=220620 // 예) https://도메인/js/common.js?ver=220620 define('G5_CSS_VER', '2303229'); -define('G5_JS_VER', '2605291'); +define('G5_JS_VER', '2608311'); diff --git a/js/common.js b/js/common.js index 233358870..644ee11ad 100644 --- a/js/common.js +++ b/js/common.js @@ -59,6 +59,72 @@ function trim(s) return t; } +// SVG/SVGZ 업로드 파일 차단 +function is_disallowed_svg_filename(filename) +{ + // 서버의 파일명 정규화와 동일하게 특수문자를 제거 + filename = (filename || "").replace(/[\0"'<>=#&!%\\()*+?]/g, ""); + filename = filename.substring(filename.lastIndexOf("/") + 1); + filename = filename.replace(/[ .\t\n\r\v]+$/, ""); + + return /\.(svg|svgz)$/i.test(filename); +} + +function is_svg_upload_target(input) +{ + var name = input.name || ""; + + if (name.indexOf("bf_file[") === 0) { + return true; + } + + if (input.form && input.form.name == "fformmail" && (name == "file1" || name == "file2")) { + return true; + } + + return input.form && input.form.name == "fconfig" && + (name == "logo_img" || name == "logo_img2" || name == "mobile_logo_img" || name == "mobile_logo_img2"); +} + +function has_disallowed_svg_file(input) +{ + var i; + + if (input.files && input.files.length) { + for (i=0; i