콘텐츠 상단/하단 파일 경로를 저장 전에 먼저 정규화한 뒤 확장자와 위치를 검증하도록 순서를 조정한다. 정규화 과정에서 확장자가 사라져 검증을 우회하던 경로를 차단한다. 실행 직전 bbs/content.php 에서도 승인 확장자와 data 디렉터리 귀결 여부를 다시 확인하는 is_content_include_allowed() 를 적용하고, 콘텐츠 상단/하단 이미지 업로드는 이미지 파일만 허용하도록 검사한다. 미완료주문 상세의 결제방법 출력에 get_text() 인코딩을 적용하고, 임시주문 저장 시 정제한 결제수단 값을 원본에도 반영한다. 개인결제 주문 세션은 결제정보 검증을 통과한 뒤에만 설정한다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 196255d76912531d7e13861821bd59ca15c967d1)
192 lines
7.8 KiB
PHP
192 lines
7.8 KiB
PHP
<?php
|
|
$sub_menu = '300600';
|
|
require_once './_common.php';
|
|
|
|
if ($w == "u" || $w == "d") {
|
|
check_demo();
|
|
}
|
|
|
|
if ($w == 'd') {
|
|
auth_check_menu($auth, $sub_menu, "d");
|
|
} else {
|
|
auth_check_menu($auth, $sub_menu, "w");
|
|
}
|
|
|
|
check_admin_token();
|
|
|
|
$co_row = array('co_id' => '', 'co_include_head' => '', 'co_include_tail' => '');
|
|
|
|
if ($w == "" || $w == "u") {
|
|
if (isset($_REQUEST['co_id']) && preg_match("/[^a-z0-9_]/i", $_REQUEST['co_id'])) {
|
|
alert("ID 는 영문자, 숫자, _ 만 가능합니다.");
|
|
}
|
|
|
|
$sql = " select * from {$g5['content_table']} where co_id = '$co_id' ";
|
|
$co_row = sql_fetch($sql);
|
|
}
|
|
|
|
$co_id = isset($_REQUEST['co_id']) ? preg_replace('/[^a-z0-9_]/i', '', $_REQUEST['co_id']) : '';
|
|
$co_subject = isset($_POST['co_subject']) ? addslashes(strip_tags(clean_xss_attributes(stripslashes($_POST['co_subject'])))) : '';
|
|
$co_include_head = isset($_POST['co_include_head']) ? preg_replace(array("#[\\\]+$#", "#(<\?php|<\?)#i"), "", substr($_POST['co_include_head'], 0, 255)) : '';
|
|
$co_include_tail = isset($_POST['co_include_tail']) ? preg_replace(array("#[\\\]+$#", "#(<\?php|<\?)#i"), "", substr($_POST['co_include_tail'], 0, 255)) : '';
|
|
|
|
// 최고 관리자가 아니면 include 경로 변경 불가 (board_form_update.php 와 동일 정책)
|
|
if ($is_admin !== 'super') {
|
|
if ($w == 'u') {
|
|
$co_include_head = isset($co_row['co_include_head']) ? $co_row['co_include_head'] : '';
|
|
$co_include_tail = isset($co_row['co_include_tail']) ? $co_row['co_include_tail'] : '';
|
|
} else {
|
|
$co_include_head = '';
|
|
$co_include_tail = '';
|
|
}
|
|
}
|
|
// 저장·검증 전에 경로를 먼저 정규화하여, 검증 이후 경로가 달라지지 않도록 한다.
|
|
if (function_exists('filter_input_include_path')) {
|
|
$co_include_head = filter_input_include_path($co_include_head);
|
|
$co_include_tail = filter_input_include_path($co_include_tail);
|
|
}
|
|
|
|
$co_tag_filter_use = isset($_POST['co_tag_filter_use']) ? (int) $_POST['co_tag_filter_use'] : 1;
|
|
$co_himg_del = (isset($_POST['co_himg_del']) && $_POST['co_himg_del']) ? 1 : 0;
|
|
$co_timg_del = (isset($_POST['co_timg_del']) && $_POST['co_timg_del']) ? 1 : 0;
|
|
$co_html = isset($_POST['co_html']) ? (int) $_POST['co_html'] : 0;
|
|
$co_content = isset($_POST['co_content']) ? $_POST['co_content'] : '';
|
|
$co_mobile_content = isset($_POST['co_mobile_content']) ? $_POST['co_mobile_content'] : '';
|
|
$co_skin = isset($_POST['co_skin']) ? addslashes(clean_xss_tags(stripslashes($_POST['co_skin']), 1, 1)) : '';
|
|
$co_mobile_skin = isset($_POST['co_mobile_skin']) ? addslashes(clean_xss_tags(stripslashes($_POST['co_mobile_skin']), 1, 1)) : '';
|
|
|
|
// 관리자가 자동등록방지를 사용해야 할 경우
|
|
if (((isset($co_row['co_include_head']) && $co_row['co_include_head'] !== $co_include_head) || (isset($co_row['co_include_tail']) && $co_row['co_include_tail'] !== $co_include_tail)) && function_exists('get_admin_captcha_by') && get_admin_captcha_by()) {
|
|
include_once G5_CAPTCHA_PATH . '/captcha.lib.php';
|
|
|
|
if (!chk_captcha()) {
|
|
alert('자동등록방지 숫자가 틀렸습니다.');
|
|
}
|
|
}
|
|
|
|
@mkdir(G5_DATA_PATH . "/content", G5_DIR_PERMISSION);
|
|
@chmod(G5_DATA_PATH . "/content", G5_DIR_PERMISSION);
|
|
|
|
if ($co_himg_del) {
|
|
@unlink(G5_DATA_PATH . "/content/{$co_id}_h");
|
|
}
|
|
if ($co_timg_del) {
|
|
@unlink(G5_DATA_PATH . "/content/{$co_id}_t");
|
|
}
|
|
|
|
$error_msg = '';
|
|
|
|
if ($co_include_head) {
|
|
$file_ext = pathinfo($co_include_head, PATHINFO_EXTENSION);
|
|
|
|
if (!$file_ext || !in_array($file_ext, array('php', 'htm', 'html')) || !preg_match('/^.*\.(php|htm|html)$/i', $co_include_head)) {
|
|
alert('상단 파일 경로의 확장자는 php, htm, html 만 허용합니다.');
|
|
}
|
|
}
|
|
|
|
if ($co_include_tail) {
|
|
$file_ext = pathinfo($co_include_tail, PATHINFO_EXTENSION);
|
|
|
|
if (!$file_ext || !in_array($file_ext, array('php', 'htm', 'html')) || !preg_match('/^.*\.(php|htm|html)$/i', $co_include_tail)) {
|
|
alert('하단 파일 경로의 확장자는 php, htm, html 만 허용합니다.');
|
|
}
|
|
}
|
|
|
|
if ($co_include_head && function_exists('is_content_include_allowed') && !is_content_include_allowed($co_include_head)) {
|
|
alert('상단 파일 경로로 사용할 수 없는 위치입니다.');
|
|
}
|
|
|
|
if ($co_include_tail && function_exists('is_content_include_allowed') && !is_content_include_allowed($co_include_tail)) {
|
|
alert('하단 파일 경로로 사용할 수 없는 위치입니다.');
|
|
}
|
|
|
|
if ($co_include_head && !is_include_path_check($co_include_head, 1)) {
|
|
$co_include_head = '';
|
|
$error_msg = '/data/file/ 또는 /data/editor/ 포함된 문자를 상단 파일 경로에 포함시킬수 없습니다.';
|
|
}
|
|
|
|
if ($co_include_tail && !is_include_path_check($co_include_tail, 1)) {
|
|
$co_include_tail = '';
|
|
$error_msg = '/data/file/ 또는 /data/editor/ 포함된 문자를 하단 파일 경로에 포함시킬수 없습니다.';
|
|
}
|
|
|
|
$co_seo_title = exist_seo_title_recursive('content', generate_seo_title($co_subject), $g5['content_table'], $co_id);
|
|
|
|
$sql_common = " co_include_head = '$co_include_head',
|
|
co_include_tail = '$co_include_tail',
|
|
co_html = '$co_html',
|
|
co_tag_filter_use = '$co_tag_filter_use',
|
|
co_subject = '$co_subject',
|
|
co_content = '$co_content',
|
|
co_mobile_content = '$co_mobile_content',
|
|
co_seo_title = '$co_seo_title',
|
|
co_skin = '$co_skin',
|
|
co_mobile_skin = '$co_mobile_skin' ";
|
|
|
|
if ($w == "") {
|
|
$row = $co_row;
|
|
if (isset($row['co_id']) && $row['co_id']) {
|
|
alert("이미 같은 ID로 등록된 내용이 있습니다.");
|
|
}
|
|
|
|
$sql = " insert {$g5['content_table']}
|
|
set co_id = '$co_id',
|
|
$sql_common ";
|
|
sql_query($sql);
|
|
run_event('admin_content_created', $co_id);
|
|
} elseif ($w == "u") {
|
|
$sql = " update {$g5['content_table']}
|
|
set $sql_common
|
|
where co_id = '$co_id' ";
|
|
sql_query($sql);
|
|
run_event('admin_content_updated', $co_id);
|
|
} elseif ($w == "d") {
|
|
@unlink(G5_DATA_PATH . "/content/{$co_id}_h");
|
|
@unlink(G5_DATA_PATH . "/content/{$co_id}_t");
|
|
|
|
$sql = " delete from {$g5['content_table']} where co_id = '$co_id' ";
|
|
sql_query($sql);
|
|
run_event('admin_content_deleted', $co_id);
|
|
}
|
|
|
|
if (function_exists('get_admin_captcha_by')) {
|
|
get_admin_captcha_by('remove');
|
|
}
|
|
|
|
g5_delete_cache_by_prefix('content-' . $co_id . '-');
|
|
|
|
if ($w == "" || $w == "u") {
|
|
foreach (array('co_himg', 'co_timg') as $content_img_field) {
|
|
if (empty($_FILES[$content_img_field]['name']) || empty($_FILES[$content_img_field]['tmp_name']))
|
|
continue;
|
|
|
|
// 상단/하단 이미지 슬롯에는 이미지만 허용하고, 실행 가능한 태그가 담긴 파일은 거부한다.
|
|
$tmp_file = $_FILES[$content_img_field]['tmp_name'];
|
|
$is_valid_image = @getimagesize($tmp_file);
|
|
$head_bytes = @file_get_contents($tmp_file, false, null, 0, 8192);
|
|
|
|
if ($is_valid_image === false || ($head_bytes !== false && preg_match('/<\?php|<\?=|<\?|<script/i', $head_bytes))) {
|
|
alert('상단/하단 이미지에는 이미지 파일만 등록할 수 있습니다.');
|
|
}
|
|
}
|
|
|
|
if ($_FILES['co_himg']['name']) {
|
|
$dest_path = G5_DATA_PATH . "/content/" . $co_id . "_h";
|
|
@move_uploaded_file($_FILES['co_himg']['tmp_name'], $dest_path);
|
|
@chmod($dest_path, G5_FILE_PERMISSION);
|
|
}
|
|
if ($_FILES['co_timg']['name']) {
|
|
$dest_path = G5_DATA_PATH . "/content/" . $co_id . "_t";
|
|
@move_uploaded_file($_FILES['co_timg']['tmp_name'], $dest_path);
|
|
@chmod($dest_path, G5_FILE_PERMISSION);
|
|
}
|
|
|
|
if ($error_msg) {
|
|
alert($error_msg, "./contentform.php?w=u&co_id=$co_id");
|
|
} else {
|
|
goto_url("./contentform.php?w=u&co_id=$co_id");
|
|
}
|
|
} else {
|
|
goto_url("./contentlist.php");
|
|
}
|