본문으로 건너뛰기

네이버 아이디 로그인 차단 현상 수정 (네이버 CallbackURL 검증 강화 대응)

PHP 8 · GODO26
변경 파일 수: 1개
변경 파일 목록
1
변경 파일 코드
추가삭제
Controller/Front/Member/Naver/NaverLoginController.php+62-48
변경 전NaverLoginController.php
 
$logger->info(sprintf('start controller: %s', __METHOD__));
try {
$functionName = 'popup';
if(gd_is_skin_division()) {
$functionName = 'gd_popup';
}
 
if ($request->isMyapp() && $request->get('saveAutoLogin')) {
$tempArray = explode('?', $request->get()->get('saveAutoLogin'));
$session->set(MyappAuth::MYAPP_ENABLED_AUTO_LOGIN_IN_BY_WEB_PAGE, $saveAutoLogin);
}
 
switch ($request->get()->get('mode', 'login')) {
 
 
 
 
 
 
 
case 'login':
$referer = $request->get()->get('referer', '');
if (empty($referer) === true) {
$returlUrl = $request->get()->get('returnUrl', '');
$param = explode('?', $returlUrl);
parse_str($param[1], $output);
$referer = $output['referer'];
}
 
 
if($naverApi->hasError()) {
throw new Exception($request->get()->get('error_description'));
}
 
if($naverApi->isAuthorizationResponse()) {
 
 
 
 
$naverToken = $naverApi->getToken($request->get()->get('code'));
$session->set(GodoNaverServerApi::SESSION_ACCESS_TOKEN, $naverToken);
 
$logger->info(sprintf('member join mall number[%s], mall session sno[%d]', $memberSns['mallSno'], $mallBySession['sno']));
$js = "
alert('" . __('회원을 찾을 수 없습니다.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../main/index.php';
} else {
opener.location.href='../../main/index.php';
$logger->info('not eq memNo');
$js = "
alert('" . __('로그인 시 인증한 정보와 다릅니다 .') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../mypage/my_page_password.php';
} else {
opener.location.href='../../mypage/my_page_password.php';
";
$this->js($js);
}
if (StringUtils::contains($referer, 'my_page_password') || gd_in_array($request->get()->get('naverType', ''), ['my_page_password', 'hack_out']) === true) {
if ($request->get()->get('naverType', '') == 'my_page_password') {
$logger->info('move my page');
$session->set(MyPage::SESSION_MY_PAGE_PASSWORD, true);
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../mypage/my_page.php';
} else {
opener.location.href='../../mypage/my_page.php';
self.close();
}
";
} elseif ($request->get()->get('naverType', '') == 'hack_out') {
$logger->info('move hack out');
$session->set(GodoNaverServerApi::SESSION_NAVER_HACK, true);
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../mypage/hack_out.php';
} else {
opener.location.href='../../mypage/hack_out.php';
$logger->info('move main or reload');
$js = "
alert('" . __('회원으로 로그인 된 상태입니다.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../main/index.php';
} else {
opener.location.href='../../main/index.php';
$myappAuth = \App::load(MyappAuth::class);
$solutionAuthenticationKey = $myappAuth->createAuthenticationKey($memberData);
 
if (StringUtils::contains($referer, 'join_method') || $request->get()->get('naverType', '') == 'join_method') {
$nextUrl = MyappUtil::getHomeUrl();
} else {
$nextUrl = MemberUtil::getLoginReturnURL();
$logger->info(sprintf('has attendance message: %s', $message));
$js = "
alert('" . $message . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='" . MemberUtil::getLoginReturnURL() . "';
} else {
opener.location.href='" . MemberUtil::getLoginReturnURL() . "';
$logger->error(__METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage());
}
 
if (StringUtils::contains($referer, 'join_method') || $request->get()->get('naverType', '') == 'join_method') {
$logger->info('join member');
$js = "
alert('" . __('이미 가입한 회원입니다.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../member/login.php';
} else {
opener.location.href='../../member/login.php';
// 성인인증 로그인 시도 회원정보 없음
if (StringUtils::contains($referer, 'intro/adult')) {
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
if (confirm('" . __('가입되지 않은 회원정보입니다. 회원가입을 진행하시겠습니까?') . "')) {
location.href = '../join_agreement.php';
} else {
$this->js($js);
}
// 회원가입에서의 로그인 약관 동의 화면 이동
if (StringUtils::contains($referer, 'member/join_method') || $request->get()->get('naverType', '') == 'join_method') {
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../join_agreement.php';
} else {
opener.location.href='../join_agreement.php';
if (StringUtils::contains($referer, 'mypage/my_page_password')) {
$js = "
alert('" . __('가입된 계정이 아닙니다. 가입하신 계정으로 재인증 진행해주세요.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='" . $request->getReferer() . "';
} else {
self.close();
}
// 회원가입을 하지 않은 경우
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
if (confirm('" . __('가입되지 않은 회원정보입니다. 회원가입을 진행하시겠습니까?') . "')) {
location.href = '../join_agreement.php';
} else {
}
// 응답 실패인 경우
$js = "
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../join_method.php';
} else {
opener.location.href='../join_method.php';
$this->js($js);
}
 
$parseUrl = parse_url($request->getRequestUri(),PHP_URL_QUERY);
parse_str($parseUrl,$parseReturlUrlQuery);
if(strpos($parseReturlUrlQuery['returnUrl'], '?')!==false){
$returnURL = $request->getDomainUrl() . $request->getRequestUri() . '&referer=' . $request->getReferer();
}
else {
$returnURL = $request->getDomainUrl() . $request->getRequestUri() . '?referer=' . $request->getReferer();
}
 
if (strpos($returnURL, 'returnUrl') === false) {
$returnURL .= '&returnUrl=' . MemberUtil::getLoginReturnURL();
}
$logger->info(sprintf('NaverLogin Return Login. url is %s', $returnURL));
$loginURL = $naverApi->getLoginURL($returnURL);
 
 
 
 
 
$logger->info(sprintf('Redirect naver login. url is %s', $loginURL));
$this->redirect($loginURL);
throw new Exception($request->get()->get('error_description'));
}
if (Request::get()->has('code') && Request::get()->has('state')) {
 
 
 
 
$NaverToken = $naverApi->getToken(Request::get()->get('code'));
Session::set(GodoNaverServerApi::SESSION_ACCESS_TOKEN, $NaverToken);
if ($naverApi->isSuccess($NaverToken)) {
if ($memberSnsService->hasSnsMember($NaverToken['response']['id'])) {
$js = "
alert('" . __('이미 다른 회원정보와 연결된 계정입니다. 다른 계정을 이용해주세요.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../main/index.php';
} else {
self.close();
$naverApi->logByLink();
$js = "
alert('" . __('계정 연결이 완료되었습니다. 로그인 시 연결된 계정으로 로그인 하실 수 있습니다.') . "');
if (window.opener === null || Object.keys(window.opener).indexOf('" . $functionName . "') < 0) {
location.href='../../mypage/my_page.php';
} else {
opener.location.href='../../mypage/my_page.php';
$this->js($js);
}
}
$returnURL = Request::getDomainUrl() . Request::getRequestUri() . '&referer=' . Request::getReferer();
$loginURL = $naverApi->getLoginURL($returnURL);
 
 
$this->redirect($loginURL);
break;
case 'disconnect':
} catch (AlertRedirectCloseException $e) {
throw $e;
} catch(\Exception $e) {
switch ($request->get()->get('mode', 'login')) {
case 'login':
if (Request::isMobile()) {
$redirectUrl = '../login.php';
 
변경 후NaverLoginController.php
 
$logger->info(sprintf('start controller: %s', __METHOD__));
try {
// 팝업 부모창(opener) 판정 — 스킨 세대(popup/gd_popup)·skin_version 비의존
$openerGuard = "window.opener === null || (typeof window.opener.gd_popup !== 'function' && typeof window.opener.popup !== 'function')";
 
 
 
if ($request->isMyapp() && $request->get('saveAutoLogin')) {
$tempArray = explode('?', $request->get()->get('saveAutoLogin'));
$session->set(MyappAuth::MYAPP_ENABLED_AUTO_LOGIN_IN_BY_WEB_PAGE, $saveAutoLogin);
}
 
// 콜백은 고정 redirect_uri로 돌아와 mode 파라미터가 없으므로 state에서 복원
$mode = $request->get()->get('mode', '');
if (empty($mode)) {
$stateData = $naverApi->decodeState((string) $request->get()->get('state', ''));
$mode = !empty($stateData['mode']) ? $stateData['mode'] : 'login';
}
 
switch ($mode) {
case 'login':
// 동적값은 state에서 복원
$state = (string) $request->get()->get('state', '');
$stateData = $naverApi->decodeState($state);
 
// state는 외부 입력이므로 비문자열 위조값 방어를 위해 문자열 캐스트
$referer = (string) ($stateData['referer'] ?? '');
$returlUrl = (string) ($stateData['returnUrl'] ?? '');
$naverType = (string) ($stateData['naverType'] ?? '');
 
if($naverApi->hasError()) {
throw new Exception($request->get()->get('error_description'));
}
 
if($naverApi->isAuthorizationResponse()) {
// CSRF: state 토큰 검증
if (!$naverApi->verifyState($state)) {
throw new Exception(__('잘못된 접근입니다.'));
}
$naverToken = $naverApi->getToken($request->get()->get('code'));
$session->set(GodoNaverServerApi::SESSION_ACCESS_TOKEN, $naverToken);
 
$logger->info(sprintf('member join mall number[%s], mall session sno[%d]', $memberSns['mallSno'], $mallBySession['sno']));
$js = "
alert('" . __('회원을 찾을 수 없습니다.') . "');
if (" . $openerGuard . ") {
location.href='../../main/index.php';
} else {
opener.location.href='../../main/index.php';
$logger->info('not eq memNo');
$js = "
alert('" . __('로그인 시 인증한 정보와 다릅니다 .') . "');
if (" . $openerGuard . ") {
location.href='../../mypage/my_page_password.php';
} else {
opener.location.href='../../mypage/my_page_password.php';
";
$this->js($js);
}
if (StringUtils::contains($referer, 'my_page_password') || gd_in_array($naverType, ['my_page_password', 'hack_out']) === true) {
if ($naverType == 'my_page_password') {
$logger->info('move my page');
$session->set(MyPage::SESSION_MY_PAGE_PASSWORD, true);
$js = "
if (" . $openerGuard . ") {
location.href='../../mypage/my_page.php';
} else {
opener.location.href='../../mypage/my_page.php';
self.close();
}
";
} elseif ($naverType == 'hack_out') {
$logger->info('move hack out');
$session->set(GodoNaverServerApi::SESSION_NAVER_HACK, true);
$js = "
if (" . $openerGuard . ") {
location.href='../../mypage/hack_out.php';
} else {
opener.location.href='../../mypage/hack_out.php';
$logger->info('move main or reload');
$js = "
alert('" . __('회원으로 로그인 된 상태입니다.') . "');
if (" . $openerGuard . ") {
location.href='../../main/index.php';
} else {
opener.location.href='../../main/index.php';
$myappAuth = \App::load(MyappAuth::class);
$solutionAuthenticationKey = $myappAuth->createAuthenticationKey($memberData);
 
if (StringUtils::contains($referer, 'join_method') || $naverType == 'join_method') {
$nextUrl = MyappUtil::getHomeUrl();
} else {
$nextUrl = MemberUtil::getLoginReturnURL();
$logger->info(sprintf('has attendance message: %s', $message));
$js = "
alert('" . $message . "');
if (" . $openerGuard . ") {
location.href='" . MemberUtil::getLoginReturnURL() . "';
} else {
opener.location.href='" . MemberUtil::getLoginReturnURL() . "';
$logger->error(__METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage());
}
 
if (StringUtils::contains($referer, 'join_method') || $naverType == 'join_method') {
$logger->info('join member');
$js = "
alert('" . __('이미 가입한 회원입니다.') . "');
if (" . $openerGuard . ") {
location.href='../../member/login.php';
} else {
opener.location.href='../../member/login.php';
// 성인인증 로그인 시도 회원정보 없음
if (StringUtils::contains($referer, 'intro/adult')) {
$js = "
if (" . $openerGuard . ") {
if (confirm('" . __('가입되지 않은 회원정보입니다. 회원가입을 진행하시겠습니까?') . "')) {
location.href = '../join_agreement.php';
} else {
$this->js($js);
}
// 회원가입에서의 로그인 약관 동의 화면 이동
if (StringUtils::contains($referer, 'member/join_method') || $naverType == 'join_method') {
$js = "
if (" . $openerGuard . ") {
location.href='../join_agreement.php';
} else {
opener.location.href='../join_agreement.php';
if (StringUtils::contains($referer, 'mypage/my_page_password')) {
$js = "
alert('" . __('가입된 계정이 아닙니다. 가입하신 계정으로 재인증 진행해주세요.') . "');
if (" . $openerGuard . ") {
location.href='" . $request->getReferer() . "';
} else {
self.close();
}
// 회원가입을 하지 않은 경우
$js = "
if (" . $openerGuard . ") {
if (confirm('" . __('가입되지 않은 회원정보입니다. 회원가입을 진행하시겠습니까?') . "')) {
location.href = '../join_agreement.php';
} else {
}
// 응답 실패인 경우
$js = "
if (" . $openerGuard . ") {
location.href='../join_method.php';
} else {
opener.location.href='../join_method.php';
$this->js($js);
}
 
// redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match)
$redirectUri = $request->getDomainUrl() . parse_url($request->getRequestUri(), PHP_URL_PATH);
 
 
 
 
 
 
 
// 동적값은 state로 이관
$state = $naverApi->createState([
'referer' => $request->getReferer(),
'returnUrl' => MemberUtil::getLoginReturnURL(),
'naverType' => $request->get()->get('naverType', ''),
]);
 
$logger->info(sprintf('NaverLogin Return Login. redirect_uri is %s', $redirectUri));
$loginURL = $naverApi->getLoginURL($redirectUri, $state);
 
$logger->info(sprintf('Redirect naver login. url is %s', $loginURL));
$this->redirect($loginURL);
throw new Exception($request->get()->get('error_description'));
}
if (Request::get()->has('code') && Request::get()->has('state')) {
// connect도 동일하게 state 검증 (CSRF)
if (!$naverApi->verifyState((string) Request::get()->get('state'))) {
throw new Exception(__('잘못된 접근입니다.'));
}
$NaverToken = $naverApi->getToken(Request::get()->get('code'));
Session::set(GodoNaverServerApi::SESSION_ACCESS_TOKEN, $NaverToken);
if ($naverApi->isSuccess($NaverToken)) {
if ($memberSnsService->hasSnsMember($NaverToken['response']['id'])) {
$js = "
alert('" . __('이미 다른 회원정보와 연결된 계정입니다. 다른 계정을 이용해주세요.') . "');
if (" . $openerGuard . ") {
location.href='../../main/index.php';
} else {
self.close();
$naverApi->logByLink();
$js = "
alert('" . __('계정 연결이 완료되었습니다. 로그인 시 연결된 계정으로 로그인 하실 수 있습니다.') . "');
if (" . $openerGuard . ") {
location.href='../../mypage/my_page.php';
} else {
opener.location.href='../../mypage/my_page.php';
$this->js($js);
}
}
// redirect_uri는 등록 콜백URL과 동일한 고정 경로 (strict match), mode는 state로 복원
$returnURL = Request::getDomainUrl() . parse_url(Request::getRequestUri(), PHP_URL_PATH);
$state = $naverApi->createState(['mode' => 'connect']);
$loginURL = $naverApi->getLoginURL($returnURL, $state);
$this->redirect($loginURL);
break;
case 'disconnect':
} catch (AlertRedirectCloseException $e) {
throw $e;
} catch(\Exception $e) {
switch (isset($mode) ? $mode : 'login') {
case 'login':
if (Request::isMobile()) {
$redirectUrl = '../login.php';