security: SVG 및 SVGZ 파일 업로드 차단

게시판, 1:1문의, 폼메일 및 쇼핑몰 설정의 클라이언트와 서버 검증에서 SVG 계열 파일 업로드를 거부한다.
This commit is contained in:
whitedot
2026-09-01 11:40:15 +09:00
parent 4e8474f231
commit 0242e8f1a3
7 changed files with 156 additions and 5 deletions
+10
View File
@@ -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");
+16 -3
View File
@@ -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');
include_once(G5_PATH.'/tail.sub.php');
+9
View File
@@ -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;
+9
View File
@@ -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);
+1 -1
View File
@@ -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');
+91 -1
View File
@@ -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<input.files.length; i++) {
if (is_disallowed_svg_filename(input.files[i].name)) {
return true;
}
}
return false;
}
return is_disallowed_svg_filename(input.value);
}
function check_disallowed_svg_upload(form)
{
var invalid_file = null;
$(form).find("input[type=file]").each(function() {
if (is_svg_upload_target(this) && has_disallowed_svg_file(this)) {
invalid_file = this;
return false;
}
});
if (!invalid_file) {
return true;
}
alert("허용되지 않는 파일 확장자입니다. (svg, svgz)");
$(form).find("input:submit, button:submit, input:image").prop("disabled", false);
invalid_file.focus();
return false;
}
// 자바스크립트로 PHP의 number_format 흉내를 냄
// 숫자에 , 를 출력
function number_format(data)
@@ -728,6 +794,30 @@ function get_write_token(bo_table)
}
$(function() {
$(document).on("change", "input[type=file]", function() {
if (!is_svg_upload_target(this) || !has_disallowed_svg_file(this)) {
return true;
}
alert("허용되지 않는 파일 확장자입니다. (svg, svgz)");
this.value = "";
return false;
});
if (document.addEventListener) {
document.addEventListener("submit", function(event) {
if (!check_disallowed_svg_upload(event.target)) {
event.preventDefault();
event.stopPropagation();
}
}, true);
} else {
$(document).on("submit", "form", function() {
return check_disallowed_svg_upload(this);
});
}
$(document).on("click", "form[name=fwrite] input:submit, form[name=fwrite] button:submit, form[name=fwrite] input:image", function() {
var f = this.form;
@@ -752,4 +842,4 @@ $(function() {
return true;
});
});
});
+20
View File
@@ -4011,6 +4011,26 @@ function get_safe_filename($name)
return $name;
}
// 업로드 파일명이 SVG 또는 SVGZ 확장자인지 확인
function is_disallowed_svg_filename($filename)
{
if (!is_string($filename) || $filename === '') {
return false;
}
$filename = str_replace(array("\0", '\\'), array('', '/'), $filename);
$filename = basename($filename);
$filename = rtrim($filename, " .\t\n\r\0\x0B");
if ($filename === '') {
return false;
}
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($extension, array('svg', 'svgz'), true);
}
// 파일명 치환
function replace_filename($name)
{