diff --git a/adm/mail_select_update.php b/adm/mail_select_update.php index c0d7c7400..c6083d90d 100644 --- a/adm/mail_select_update.php +++ b/adm/mail_select_update.php @@ -8,6 +8,8 @@ $html_title = '회원메일 발송'; check_demo(); +$security_mail_url = g5_require_security_mail_url(); + check_admin_token(); require_once './admin.head.php'; @@ -59,7 +61,7 @@ for ($i = 0; $i < count($member_list); $i++) { $content = preg_replace("/{회원아이디}/", $mb_id, (string)$content); $content = preg_replace("/{이메일}/", $to_email, (string)$content); - $content = $content . "
▶ 더 이상 정보 수신을 원치 않으시면 [수신거부] 해 주십시오.
"; + $content = $content . "▶ 더 이상 정보 수신을 원치 않으시면 [수신거부] 해 주십시오.
"; mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $to_email, $subject, $content, 1); diff --git a/adm/mail_test.php b/adm/mail_test.php index 13d74aeca..afb8a8fd8 100644 --- a/adm/mail_test.php +++ b/adm/mail_test.php @@ -12,6 +12,8 @@ auth_check_menu($auth, $sub_menu, 'w'); check_demo(); +$security_mail_url = g5_require_security_mail_url(); + $g5['title'] = '회원메일 테스트'; $name = get_text($member['mb_name']); @@ -33,7 +35,7 @@ $content = preg_replace("/{이메일}/", $email, (string)$content); $mb_md5 = md5($member['mb_id'] . $member['mb_email'] . $member['mb_datetime']); -$content = $content . '더 이상 정보 수신을 원치 않으시면 [수신거부] 해 주십시오.
'; +$content = $content . '더 이상 정보 수신을 원치 않으시면 [수신거부] 해 주십시오.
'; mailer($config['cf_title'], $member['mb_email'], $member['mb_email'], $subject, $content, 1); diff --git a/bbs/password_lost2.php b/bbs/password_lost2.php index 04b629a2c..1df835d34 100644 --- a/bbs/password_lost2.php +++ b/bbs/password_lost2.php @@ -20,6 +20,12 @@ if (!$email) // (이메일 열거 공격 방지) $generic_message = $email.' 메일로 회원아이디와 비밀번호를 인증할 수 있는 메일이 발송 되었습니다.\\n\\n메일을 확인하여 주십시오.'; +$security_mail_url = g5_security_mail_base_url(); +if ($security_mail_url === false) { + error_log('[g5 security mail] Valid G5_DOMAIN is required.'); + alert_close($generic_message); +} + $sql = " select count(*) as cnt from {$g5['member_table']} where mb_email = '$email' "; $row = sql_fetch($sql); if ($row['cnt'] > 1) { @@ -48,7 +54,7 @@ $sql = " update {$g5['member_table']} set mb_lost_certify = '$mb_nonce $mb_lost_ sql_query($sql); // 인증 링크 생성 -$href = G5_BBS_URL.'/password_lost_certify.php?mb_no='.$mb['mb_no'].'&mb_nonce='.$mb_nonce; +$href = $security_mail_url.'/'.G5_BBS_DIR.'/password_lost_certify.php?mb_no='.$mb['mb_no'].'&mb_nonce='.$mb_nonce; $subject = "[".$config['cf_title']."] 요청하신 회원정보 찾기 안내 메일입니다."; @@ -60,7 +66,7 @@ $content .= '';
$content .= addslashes($mb['mb_name'])." (".addslashes($mb['mb_nick']).")"." 회원님은 ".G5_TIME_YMDHIS." 에 회원정보 찾기 요청을 하셨습니다.
";
diff --git a/bbs/register_email_update.php b/bbs/register_email_update.php
index 97b47cb22..0c4963c0d 100644
--- a/bbs/register_email_update.php
+++ b/bbs/register_email_update.php
@@ -40,17 +40,19 @@ if ($row['cnt']) {
alert("{$mb_email} 메일은 이미 존재하는 메일주소 입니다.\\n\\n다른 메일주소를 입력해 주십시오.");
}
+$security_mail_url = g5_require_security_mail_url();
+
// 인증메일 발송
$subject = '['.$config['cf_title'].'] 인증확인 메일입니다.';
$mb_name = $mb['mb_name'];
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
-$mb_md5 = get_email_certify_token();
+$mb_md5 = get_email_certify_token();
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '{$esc_mb_id}' ");
-$certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
+$certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
ob_start();
include_once ('./register_form_update_mail3.php');
@@ -62,4 +64,4 @@ mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $mb_email, $su
$sql = " update {$g5['member_table']} set mb_email = '$mb_email' where mb_id = '{$esc_mb_id}' ";
sql_query($sql);
-alert("인증메일을 {$mb_email} 메일로 다시 보내 드렸습니다.\\n\\n잠시후 {$mb_email} 메일을 확인하여 주십시오.", G5_URL);
+alert("인증메일을 {$mb_email} 메일로 다시 보내 드렸습니다.\\n\\n잠시후 {$mb_email} 메일을 확인하여 주십시오.", G5_URL);
diff --git a/bbs/register_form_update.php b/bbs/register_form_update.php
index be3aebcfe..b27cf3cb2 100644
--- a/bbs/register_form_update.php
+++ b/bbs/register_form_update.php
@@ -65,6 +65,10 @@ $mb_9 = isset($_POST['mb_9']) ? trim($_POST['mb_9'])
$mb_10 = isset($_POST['mb_10']) ? trim($_POST['mb_10']) : "";
$mb_name = addslashes(clean_xss_tags(stripslashes($mb_name), 1, 1));
$mb_email = get_email_address($mb_email);
+$security_mail_url = g5_security_mail_base_url();
+if ($config['cf_use_email_certify'] && ($w == '' || $member['mb_email'] != $mb_email)) {
+ $security_mail_url = g5_require_security_mail_url();
+}
$mb_homepage = addslashes(clean_xss_tags(stripslashes($mb_homepage), 1, 1));
$mb_tel = addslashes(clean_xss_tags(stripslashes($mb_tel), 1, 1));
$mb_zip1 = preg_replace('/[^0-9]/', '', $mb_zip1);
@@ -327,7 +331,7 @@ if ($w == '') {
if ($config['cf_use_email_certify']) {
$mb_md5 = get_email_certify_token();
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
- $certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
+ $certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
}
ob_start();
@@ -613,7 +617,7 @@ if ($config['cf_use_email_certify'] && $old_email != $mb_email) {
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
- $certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
+ $certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5;
ob_start();
include_once ('./register_form_update_mail3.php');
diff --git a/bbs/register_form_update_mail1.php b/bbs/register_form_update_mail1.php
index 78e78d5a1..8d59e889c 100644
--- a/bbs/register_form_update_mail1.php
+++ b/bbs/register_form_update_mail1.php
@@ -1,6 +1,7 @@
@@ -18,7 +19,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원가입을 축하합니다.
-
+
+
+
님의 회원가입을 진심으로 축하합니다.
@@ -31,7 +34,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
메일인증
- 사이트바로가기
+
+ 사이트바로가기
+
diff --git a/bbs/register_form_update_mail2.php b/bbs/register_form_update_mail2.php
index f30b7b9e4..55b17679a 100644
--- a/bbs/register_form_update_mail2.php
+++ b/bbs/register_form_update_mail2.php
@@ -1,6 +1,7 @@
@@ -18,7 +19,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원가입 알림 메일
-
+
+
+
님께서 회원가입 하셨습니다.
diff --git a/bbs/register_form_update_mail3.php b/bbs/register_form_update_mail3.php
index c9750e678..2a0913c73 100644
--- a/bbs/register_form_update_mail3.php
+++ b/bbs/register_form_update_mail3.php
@@ -1,6 +1,7 @@
@@ -18,7 +19,9 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
회원 인증 메일입니다.
-
+
+
+
diff --git a/lib/common.lib.php b/lib/common.lib.php index 17fec1a85..9ca2dc112 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -2696,11 +2696,53 @@ function get_email_cert_key($mb_id, $mb_datetime) return hash_hmac('sha256', $payload, $key); } -/** - * 발급 시각을 포함한 메일 인증용 일회용 토큰을 생성한다. - * - * @return string - */ +// 보안 메일은 요청 헤더가 아닌 운영자가 지정한 공개 URL만 사용한다. +function g5_security_mail_base_url($domain = null) +{ + if ($domain === null) { + $domain = defined('G5_DOMAIN') ? G5_DOMAIN : ''; + } + if (!is_string($domain) || $domain === '' || preg_match('/[\x00-\x20\x7f\\\\<>"\']/', $domain)) { + return false; + } + $parts = @parse_url($domain); + if (!$parts || empty($parts['scheme']) || !in_array(strtolower($parts['scheme']), array('http', 'https'), true) + || empty($parts['host']) || isset($parts['user']) || isset($parts['pass']) + || isset($parts['query']) || isset($parts['fragment'])) { + return false; + } + // 호스트에는 포트 구분용 IPv6 대괄호 외의 URL 구문을 허용하지 않는다. + $host = $parts['host']; + if ($host[0] === '[') { + if (substr($host, -1) !== ']' || !filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return false; + } elseif (!preg_match('/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9.])?$/i', $host)) { + return false; + } + if ($host[0] !== '[') { + if (strlen($host) > 254) return false; + $hostname = substr($host, -1) === '.' ? substr($host, 0, -1) : $host; + foreach (explode('.', $hostname) as $label) { + if (!preg_match('/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i', $label)) return false; + } + } + if (isset($parts['port']) && ($parts['port'] < 1 || $parts['port'] > 65535)) return false; + if (isset($parts['path']) && (preg_match('/%(?:0[0-9a-f]|1[0-9a-f]|7f|2f|5c)/i', $parts['path']) + || preg_match('~(?:^|/)(?:\.|%2e){1,2}(?:/|$)~i', $parts['path']))) return false; + + return rtrim($domain, '/'); +} + +function g5_require_security_mail_url() +{ + $url = g5_security_mail_base_url(); + if ($url === false) { + error_log('[g5 security mail] Valid G5_DOMAIN is required.'); + alert('메일 인증을 위한 사이트 주소가 설정되지 않았습니다. 사이트 관리자에게 문의해 주십시오.'); + } + return $url; +} + +// 발급 시각을 포함한 메일 인증용 일회용 토큰을 생성한다. function get_email_certify_token() { return get_random_token_string(16) . '.' . G5_SERVER_TIME; diff --git a/plugin/social/register_member_update.php b/plugin/social/register_member_update.php index 6d624abd7..eb7c8dbf7 100644 --- a/plugin/social/register_member_update.php +++ b/plugin/social/register_member_update.php @@ -86,10 +86,12 @@ $data = array( 'mb_name' => $mb_name, ); +$security_mail_url = g5_security_mail_base_url(); $mb_email_certify = G5_TIME_YMDHIS; //메일인증을 사용한다면 if( defined('G5_SOCIAL_CERTIFY_MAIL') && G5_SOCIAL_CERTIFY_MAIL && $config['cf_use_email_certify'] ){ + $security_mail_url = g5_require_security_mail_url(); $mb_email_certify = ''; } @@ -277,7 +279,7 @@ if($result) { sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' "); - $certify_href = G5_BBS_URL.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5; + $certify_href = $security_mail_url.'/'.G5_BBS_DIR.'/email_certify.php?mb_id='.$mb_id.'&mb_md5='.$mb_md5; ob_start(); include_once (G5_BBS_PATH.'/register_form_update_mail3.php');