Compare commits

...
67 Commits
Author SHA1 Message Date
thisgun 65a419e9cd 버전 5.6.26 수정 2026-04-16 04:38:38 +00:00
thisgunandClaude Opus 4.6 3f32ecd3e6 회원정보 수정 시 약관변경내역 로그가 매번 쌓이는 버그 수정
수신설정 섹션이 폼에 표시되지 않는 경우(cf_use_promotion 미사용, 휴대폰 미사용,
아이코드 미사용 등) _default 변수가 undefined 상태가 되어 비교 시 항상 "변경됨"으로
판단되던 문제를 해결. _default 변수를 명시적으로 초기화하고, 폼에 없는 항목은
기존 DB 값을 유지하도록 수정.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 04:01:37 +00:00
thisgun b5d218d446 인증서 교체로 인한 (구)엘지모듈 토스 결제 안되는 문제 수정 2026-04-16 03:46:30 +00:00
thisgun f2e7dbc5ed [security]그누보드5 XSS, SQL Injection 취약점 수정 2026-04-16 03:46:02 +00:00
thisgunandClaude Opus 4.6 c38de4c94b [security]KVE-2026-0701 .shtml 확장자 필터 누락으로 인한 RCE 수정
- bbs/write_update.php, bbs/qawrite_update.php: 업로드 블랙리스트에 shtml|shtm 추가
- install/install_db.php, data/.htaccess: FilesMatch 정규식에 [Ss]? 추가하여 .shtml 차단

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:39:31 +00:00
thisgunandClaude Opus 4.6 7d8a660642 [security]KVE-2026-0693 비밀번호 재설정 토큰 예측 가능 취약점 수정
rand() 대신 CSPRNG 기반 get_random_token_string() 사용
- mb_nonce: 128비트 CSPRNG 토큰으로 시드 브루트포스 차단
- change_password: CSPRNG 기반 10자리 hex로 변경

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:39:12 +00:00
thisgunandClaude Opus 4.6 5875e8b86b [security]KVE-2026-0690 설문조사 결과 페이지 Local File Inclusion 수정
skin_dir 파라미터에 디렉토리 트래버설 검증이 없어
임의 경로의 PHP 파일을 include할 수 있는 취약점을
clean_relative_paths()로 경로 조작 문자열 제거하여 수정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:38:41 +00:00
thisgunandClaude Opus 4.6 68a5b9c25a [security]KVE-2026-0689 SNS 공유 스킨 Reflected XSS 수정
$_SERVER['REQUEST_URI']를 JavaScript 문자열에 이스케이프 없이
출력하여 URL을 통한 Reflected XSS가 가능한 취약점을
json_encode()로 안전하게 이스케이프 처리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:38:21 +00:00
thisgun 2dff49914b [security]KVE-2026-0710 그누보드5 SQL Injection 수정 2026-04-16 03:35:29 +00:00
thisgun 51518e45e4 [security]KVE-2026-0709 그누보드5 SQL Injection 수정 2026-04-16 03:31:35 +00:00
thisgun 7883ff65dc check_token 함수 쓰는곳 없지만 요청사항으로 인해 타임스탬프 추가 수정 2026-04-16 03:30:19 +00:00
thisgunandClaude Opus 4.6 a2608754bd [fix]SMS 직접입력 발송 시 PHP 8.x Undefined array key 경고 수정
case 'h' 직접입력 항목에 bg_no, mb_id, bk_no 키 누락으로 257행에서 Warning 발생

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:24:09 +00:00
thisgunandClaude Opus 4.6 758bb67b7b [security]KVE-2026-0685 소셜 로그인 계정 연결 CSRF 방어
popup.php에서 mylink 진입 시 Referer 검증 및 세션 토큰 설정
functions.php에서 social_user_profile_replace() 호출 전 세션 토큰 검증

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:23:50 +00:00
thisgun 0037f72cce [security]KVE-2026-0678 사용자 영역 CSRF 방어 추가 2026-04-16 03:23:00 +00:00
thisgunandClaude Opus 4.6 df940f8168 [security]KVE-2026-0676 폼메일 발신자 위장 및 Rate Limit 우회 수정
비회원 폼메일 발송 시 From 헤더를 관리자 이메일로 고정하고 사용자 입력은 Reply-To로 설정
formmail_send.php에 세션 기반 발송 횟수 제한 추가 (직접 POST 우회 방지)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:18:19 +00:00
thisgunandClaude Opus 4.6 614b040960 [security]KVE-2026-0673 비인증 이메일 변경을 통한 계정 탈취 취약점 수정
register_email_update.php에 로그인 검증·소유권 강제·ckey HMAC 검증·SQL 이스케이프 추가
member_cert_refresh_update.php에 로그인 검증 및 소유권 강제 추가 (연관 IDOR 선제 조치)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 02:55:19 +00:00
thisgun dccb50d8a3 [security]회원 ID/이메일 열거 공격 차단 2026-04-16 02:54:05 +00:00
thisgun 5331aa8be5 [security]sql_query() 에러 노출 시 SQL/스키마 정보 차단
sql_query()의 에러 처리가 die()로 SQL 쿼리 전문, MySQL 에러 메시지,
스크립트 파일 경로를 사용자에게 그대로 노출하고 있었음. 기본값
G5_DISPLAY_SQL_ERROR=false 덕분에 일반 호출에서는 트리거되지 않으나,
일부 admin 파일이 명시적으로 sql_query(\$sql, true)로 호출하여
관리자에게 raw SQL을 노출했고, SQLi 공격 시도 중 발생한 에러로 DB
구조(테이블/컬럼명)가 학습되어 정밀 공격에 활용될 수 있었음.

조치:
- 서버 로그(error_log)에는 항상 상세 기록 → 운영자가 디버깅 가능
- \$is_debug 모드(G5_DEBUG=true 또는 super admin)에서만 상세 표시
  + XSS 방지를 위해 htmlspecialchars로 escape
- 그 외 환경에서는 "데이터베이스 처리 중 오류가 발생했습니다." 만 표시
- mysqli/mysql 폴백 분기 둘 다 동일하게 적용

변경:
- lib/common.lib.php:1932 (mysqli 분기)
- lib/common.lib.php:1942 (mysql 분기, 레거시 PHP 5.x 폴백)

PHP 5.2.17 호환 유지 (htmlspecialchars + ENT_QUOTES 모두 PHP 4.0+).
2026-04-16 02:48:31 +00:00
thisgun 79f915988b [security]개인결제 hash 검증을 strict 비교(===)로 강화
shop/personalpayformupdate.php와 lib/shop.lib.php의 ss_personalpay_hash
검증이 loose 비교(==/!=)를 사용하여 PHP type juggling 잠재적 우회 가능성이
있었음. md5 결과 중 "0e..." 형식 hash는 PHP의 loose 비교에서 0e 지수
표기법으로 해석되어 다른 "0e..." 해시와 동등 판정될 수 있음
(소위 magic hash collision 패턴).

실제 익스플로잇 가능성은 낮지만 (md5 출력은 32자 hex 문자열이고 PHP의
문자열 vs 문자열 비교에서는 0e... 패턴이 적용되지 않는 경우가 많음),
defense in depth 차원에서 strict 비교로 변경.

mobile/shop/personalpayformupdate.php는 이미 strict 비교를 사용 중이라
core 두 곳만 동일한 패턴으로 정렬.

- shop/personalpayformupdate.php:35: != / != → !== / !==
- lib/shop.lib.php:2333: == → ===
2026-04-16 02:46:49 +00:00
thisgun 3cfe8b6b84 [security]현금영수증 발급 페이지 IDOR 취약점 수정 2026-04-16 02:43:22 +00:00
thisgun 31ef78e916 [perf]strstr() 불린 검사를 strpos() !== false 로 교체
strstr()은 일치하는 부분 문자열 전체를 반환하므로 존재 여부만 체크할
때는 메모리 할당이 낭비됨. strpos()는 위치(int) 또는 false만 반환하여
메모리 할당 없이 더 빠르게 동일 동작 수행.

적용 파일 (19개, 약 25곳):
- lib/common.lib.php: wr_option html1/html2/secret 검사 3곳
- lib/shop.lib.php: de_taxsave_types 검사 2곳
- lib/latest.lib.php: wr_option secret 검사 1곳
- lib/thumbnail.lib.php: wr_option secret 검사 1곳
- lib/URI/uri.class.php: URI 확장자 검사 1곳
- bbs/view.php: subject/wr_option/content 검사 4곳
- bbs/view_comment.php: wr_option secret 검사 1곳
- bbs/search.php: wr_option/sfl 검사 3곳
- bbs/rss.php: wr_option html 검사 1곳
- bbs/write_update.php: html1/html2 검사 2곳
- bbs/move_update.php: wr_option html 검사 1곳
- adm/admin.lib.php: auth 권한 검사 1곳
- adm/admin.head.php: 메뉴 권한 검사 1곳
- adm/session_file_delete.php: sess_ prefix 검사 1곳
- adm/point_list.php, adm/auth_list.php: sfl mb_id 검사 2곳
- adm/sms_admin/_common.php: install.php 검사 1곳
- adm/shop_admin/configform.php: de_taxsave_types 검사 3곳
- head.sub.php: admin dir 검사 1곳

규칙:
  strstr($a, $b)       → strpos($a, $b) !== false
  !strstr($a, $b)      → strpos($a, $b) === false

제외:
- theme/, skin/, plugin/ (사용자 커스터마이징/서드파티)
- strstr() 반환값을 실제로 사용하는 케이스 (예: lib/common.lib.php:4135
  if( \$freg = strstr(\$ori_params, '#') ), extend/gif2mp4.extend.php)
2026-04-16 02:29:32 +00:00
thisgun a612e69d3e [perf]for 루프 조건의 count() 호출을 루프 밖으로 추출 (핵심 파일)
for ($i=0; $i<count(\$arr); $i++) 패턴은 매 반복마다 count()를 호출하여
불필요한 CPU 오버헤드를 발생시킴. 루프 전에 한 번만 count()를 계산하여
변수에 저장하는 고전적인 최적화.

적용 파일 (19개, 41곳):
- lib/common.lib.php (8)
- common.php (2)
- lib/shop.lib.php (5)
- lib/naverpay.lib.php (1)
- lib/thumbnail.lib.php (2)
- bbs/list.php (1), bbs/search.php (4), bbs/qalist.php (1), bbs/qawrite.php (1)
- bbs/ajax.filter.php (1), bbs/write_update.php (4)
- bbs/write_comment_update.php (1), bbs/memo_form_update.php (2)
- bbs/new_delete.php (1), bbs/move_update.php (4)
- shop/search.php (1), shop/orderform.sub.php (1)
- mobile/shop/search.php (1), mobile/shop/orderform.sub.php (1)

부수 효과:
- lib/shop.lib.php line 2115: 동일 루프 내 strstr($dlcomp, $company)를
  strpos($dlcomp, $company) !== false 로 변경 (strstr 최적화의 일부)

제외:
- theme/, skin/, plugin/ (사용자 커스터마이징/서드파티)
- lib/PHPExcel/ (서드파티 라이브러리)
- adm/ 및 shop/mail/ (관리자/드문 경로, 별도 후속 작업 여지)
- 주석 처리된 코드 (bbs/delete.php:127, bbs/delete_all.php:143)
2026-04-16 02:26:59 +00:00
thisgun 29e55de1fa [perf]url_auto_link() 다중 regex 호출 조건부 실행으로 최적화
기존 구현은 일반 텍스트 게시글(URL/이메일이 전혀 없는 대부분의 경우)
에서도 3개의 preg_replace를 항상 호출하여, 모든 게시글 본문 렌더링
마다 불필요한 regex 컴파일/스캔 오버헤드가 발생.

개선:
1. Fast bailout: ://, www., @, &, ' 가 하나도 없으면 즉시 반환하여
   forward/reverse str_replace와 3개의 preg_replace를 모두 생략.
2. 조건부 regex 실행: 각 regex 호출 전에 해당 패턴이 실제로 존재하는지
   strpos로 먼저 확인. URL 패턴 없으면 URL regex 생략, 이메일 없으면
   email regex 생략 등.

strpos x5 (~1ms) vs preg_replace x3 (~20ms+) 이므로 일반 텍스트 게시글
에서 10~20배 이상 빠르게 동작. 페이지에 수십~수백 개 게시글을 표시하는
목록/검색 페이지에서 누적 효과가 큼.

의미론적 동등성 유지: 각 marker가 없으면 해당 regex가 원래도 no-op였으므로
조건부 실행은 결과에 영향 없음. Fast path는 모든 marker가 없을 때만
동작하므로 str_replace의 부작용(& entity 디코딩 등)도 발생할 여지가 없음.
2026-04-16 02:25:49 +00:00
thisgun c4db6e7751 [perf]cut_str() 메모리 사용량과 CPU 오버헤드 감소
기존 구현은 preg_split("//u")로 입력 문자열의 모든 문자를 PHP 배열로
분해한 뒤 count/array_slice/join 3단계로 처리하여, 짧은 문자열이든
긴 문자열이든 항상 문자 수만큼 배열 원소를 할당하고 regex를 호출함.

게시글 목록의 제목/내용 표시 등 페이지당 수십~수백 회 호출되는
핫패스에서 불필요한 오버헤드를 발생시킴.

개선:
1. 바이트 길이 빠른 경로: strlen(str) <= len이면 UTF-8 특성상 문자 수도
   보장되므로 즉시 반환. ASCII/짧은 제목은 mb_strlen 호출 없이 종료.
2. mbstring 확장 사용: mb_strlen + mb_substr로 배열 생성 없이 상수
   메모리로 길이 체크/절단 처리.
3. preg_split 기반 기존 로직은 mbstring 미설치 환경 폴백으로 유지.

의미론적 동등성 유지 (잘라낸 결과와 suffix 부착 조건 모두 동일).
2026-04-16 02:25:12 +00:00
thisgun 47a3790c57 [perf]bbs/list.php N+1 쿼리 2건을 IN 쿼리로 배치 조회로 전환
게시판 목록 페이지에서 발생하던 N+1 쿼리 패턴 2건을 제거:

1. 공지 처리 루프 (line 94~120):
   기존에는 bo_notice의 각 wr_id마다 sql_fetch를 따로 호출하여 공지가 N개면
   N개의 쿼리가 발생. 유효한 wr_id를 정수화하여 수집한 뒤 단일 IN 쿼리로
   일괄 조회하고, id 기반 해시맵으로 원래 순서대로 처리.

2. 검색 결과 2차 조회 (line 199~200):
   DISTINCT wr_parent 쿼리로 ID만 받은 뒤, 각 행마다 sql_fetch를 호출하여
   검색 결과가 N건이면 추가로 N개의 쿼리가 발생. 단일 IN 쿼리로 모든 부모
   글을 일괄 조회 후 $rows_to_process 배열에 원래 순서대로 정렬하여 공통
   처리 루프에서 소비. 검색/비검색 분기에서 중복되던 row→list 변환 로직도
   공통화.

효과 (공지 5개 + 검색 결과 20건 기준):
  기존: 1 DISTINCT + 20 fetch + 5 notice fetch = 26 쿼리
  개선: 1 DISTINCT + 1 IN(search) + 1 IN(notice) = 3 쿼리

부수 효과:
- 삭제된 공지/부모글에 대한 에러 처리 개선
  (기존은 empty row를 get_list에 넘겨 잠재적 오류, 개선판은 자동 스킵)
- wr_id 정수화로 SQL injection 방지 계층 추가
2026-04-16 02:23:24 +00:00
thisgun 2894c269ea [security]이메일 인증 페이지 접근 토큰을 HMAC-SHA256으로 강화 2026-04-16 02:21:23 +00:00
thisgun c00c73465a [security]비회원 주문/개인결제 조회 uid를 HMAC-SHA256으로 강화 2026-04-16 02:15:29 +00:00
thisgun 3e0e8be6da [security]쇼핑몰 update 엔드포인트 CSRF 보호 추가 2026-04-16 02:02:26 +00:00
thisgun 92a052fa7a [perf]html_purifier() 메모리 사용량 최적화
한 요청에서 html_purifier()가 N번 호출되면 매번 약 10~15MB의 HTMLPurifier
인스턴스를 새로 생성하여, 게시글 목록처럼 반복 호출되는 페이지에서
수백 MB~GB 단위로 메모리가 낭비되었음.

- HTMLPurifier 인스턴스를 요청 단위로 캐싱 (admin/normal 2개 변형만 유지)
- safeiframe.txt 파일 I/O + 파싱 결과도 요청 단위로 캐싱
- G5_HTMLPURIFIER_NO_CACHE 상수로 캐싱 opt-out 가능
  ($html 내용에 따라 config를 동적 변경하는 플러그인 호환용)

동작 변경:
- html_purifier_config / html_purifier_safeiframes hook은 캐시 미스 시점에만
  실행됨 (요청당 최대 2회). 정적 config를 설정하는 일반적인 hook에는 영향 없음.
- html_purifier_result hook은 매 호출마다 정상 실행됨.

효과 (게시글 목록 30건 + 첨부파일 평균 3개 = 120회 호출 기준):
  기존 ~1.8GB → 개선 후 ~15MB
2026-04-16 01:36:59 +00:00
thisgun 4ebdd8ba30 [fix]포인트 사용/환원 함수의 race condition 수정
insert_use_point, delete_use_point, delete_expire_point 함수의 SELECT-then-UPDATE
패턴이 동시 호출 시 데이터 무결성을 깰 수 있는 race condition을 가지고 있었음.
같은 회원의 포인트 작업이 동시에 발생하면 PHP 단에서 캐시한 잔여량을 기준으로
판단하여 결과적으로 음수/초과 차감 등 포인트가 꼬이는 현상이 발생.

MyISAM은 트랜잭션과 FOR UPDATE를 지원하지 않으므로, 락 없이 무결성을 보장하는
lock-free atomic UPDATE 패턴으로 재구현:

- 매 단계마다 가장 우선 처리할 행 1개를 SELECT (LIMIT 1)
- WHERE 절에 사전 검증 조건(잔여량/상태)을 포함한 원자적 UPDATE
- get_sql_affected_rows()로 성공/실패 판별, 실패 시 재시도
- max_iter=1000 안전장치로 무한루프 방지

특징:
- 단일 행 차감(가장 흔한 케이스)에서 기존과 동일한 쿼리 수
- GET_LOCK 등 추가 락 없음 → 같은 사용자 동시 요청도 throughput 손실 없음
- MyISAM/InnoDB 모두 호환, MySQL 5.0~8.x 모두 동작
2026-04-16 01:35:45 +00:00
thisgun 4d5c597665 [KVE-2026-0610]그누보드5 자동 로그인 취약점 수정 2026-04-16 01:31:19 +00:00
thisgun 7e65a297bf [KVE-2026-0608]그누보드5 Race Condition 취약점 수정
- bbs/poll_update.php: 설문조사 중복 투표 방지 (FIND_IN_SET을 WHERE에 포함한 원자적 UPDATE)
- bbs/good.php: 추천/비추천 카운터 부풀리기 방지 (INSERT IGNORE 우선 후 카운터 증가)
- lib/common.lib.php: insert_point() 포인트 중복 지급 방지 (MySQL named lock GET_LOCK 사용)
2026-04-16 01:07:17 +00:00
thisgun ddcc82d89b .gitignore 에 CLAUDE.local.md 추가 2026-04-16 01:00:28 +00:00
thisgun ad99ea7673 [KVE-2026-0599]그누보드5 XSS 취약점 수정 2026-04-06 06:27:09 +00:00
thisgun b3fa6dc127 [KVE-2026-0595]영카트5 SQL Injection 취약점 수정 2026-04-06 06:26:20 +00:00
thisgun e6780f3b4b 버전 5.6.25 수정 2026-04-06 03:47:15 +00:00
thisgun bededeaf01 .gitignore에 IDE 및 AI 도구 설정 파일 추가 2026-04-06 03:46:41 +00:00
thisgunandClaude Opus 4.6 73b868a444 Cloudflare IPv4 대역 업데이트: 104.16.0.0/12 → 104.16.0.0/13
공식 Cloudflare IP 목록(https://www.cloudflare.com/ips)에 맞게 수정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 03:29:55 +00:00
thisgun eb249a2aee Merge branch 'master' of github.com:gnuboard/gnuboard5 2026-04-06 03:21:45 +00:00
HanbitGaram(ハンビッガラム)andGitHub 813fdef0c7 다음 우편번호 서비스 네임스페이스 및 서비스 도메인 변경 (#374) 2026-04-06 12:20:42 +09:00
kms0219kmsandGitHub f8561e9398 ✏️ feat: 토스페이먼츠 상점관리자 - ‘테스트모드‘ 추가 반영, NHN KCP 상점관리자 주소 변경 (#368)
* ✏️ feat: 토스페이먼츠 테스트모드 반영, KCP 주소 업데이트

* ✏️ feat: 토스페이먼츠 테스트모드 문구 반영, KCP 주소 업데이트

* ✏️ typo: KCP 테스트환경 주소 변경
2026-04-06 12:19:11 +09:00
thisgun 52590509d5 [KVE-2026-0559]그누보드5 SQL Injection 취약점 수정 2026-04-03 03:59:23 +00:00
thisgun 604cef808e [보안패치]그누보드 영카트 SQL_injection 취약점 수정 2026-03-31 01:21:11 +00:00
thisgun 82574fd4a8 [KVE-2026-0570]그누보드,영카트 SQL Injection 취약점 수정 2026-03-30 12:01:44 +00:00
thisgun c3db9b454e [KVE-2026-0569]그누보드,영카트 SQL Injection 취약점 수정 2026-03-30 11:50:30 +00:00
thisgun bf19cd8bf5 [KVE-2026-0568]영카트 SQL Injection 취약점 수정 2026-03-30 11:03:16 +00:00
thisgun b2c0e1af29 [KVE-2026-0554]그누보드,영카트 SQL Injection 취약점 수정 2026-03-30 09:52:41 +00:00
thisgun 60d37b853b [KVE-2026-0553]그누보드 SQL Injection 취약점 수정 2026-03-30 09:50:38 +00:00
thisgun 919b75a138 [KVE-2026-0340]그누보드_SQL_Injection취약점_수정 2026-03-26 03:38:19 +00:00
thisgun 01fab8136e git commit -am "[KVE-2026-0330]영카트_SQL_Injection취약점_수정"; 2026-03-26 01:32:14 +00:00
thisgun 5054e24631 [KVE-2026-0243]영카트주문시_쿠폰중복사용취약점_수정 2026-03-25 09:18:48 +00:00
thisgun deff7d2079 warning 메시지 수정 2026-03-25 05:06:29 +00:00
thisgun 5e6b4fa668 [KVE-2026-0242]영카트주문취소시_포인트복구취약점_수정 2026-03-25 04:59:56 +00:00
thisgun ef1be317e1 버전 5.6.24 수정 2026-02-03 06:25:50 +00:00
thisgun 137d78ff73 영카트 관리자모드 분류출력 개선 2026-02-03 06:24:34 +00:00
thisgun 0ddcda4b0c toss 모바일결제 포인트 적용시 결제 오류 수정 2026-02-02 09:37:49 +00:00
thisgun bf27af3925 영카트 shop_is_taxsave 에서 볌수 코드 오타 수정 2026-02-02 07:33:36 +00:00
thisgun 607e15424d 토스결제 비회원결제가 안되는 오류 및 모바일 개인결제 취소과정 코드 수정 2026-02-02 07:18:28 +00:00
thisgun 7c490448ec 메일보내기 발신자 위장 방지 및 암호화 키의 안전성 취약점 수정 2026-01-30 05:00:43 +00:00
thisgun d775d2255f [KVE-2026-0029]Stored XSS 취약점 수정 2026-01-27 05:50:34 +00:00
thisgun b94771de92 썸네일 파일 코드 위치변경 2026-01-27 03:34:20 +00:00
thisgun b29e16a198 게시판 관리자가 게시판 수정시 자동등록방지 숫자가 틀렸습니다 라고 나오는 오류 수정 2026-01-26 09:40:43 +00:00
thisgun 0e06b4f9ce 영카트5에서 사용자가 현금영수증 발급버튼이 출력되지 않는 오류 수정 2026-01-26 09:33:19 +00:00
thisgun f2ab751e5f [KVE-2025-0828]영카트 취약점 수정 2025-11-14 07:30:47 +00:00
thisgun 3432497efe 삼성브라우저PC버전 대응 2025-11-14 02:08:02 +00:00
총andGitHub 60bb03049e Merge pull request #61 from gnuboard/fix/admin-member-header
style: 관리자 > 회원관리 > 목록, 광고성 정보 수신 동의에 따른 테이블 폭 조정 처리
2025-09-25 10:07:49 +09:00
whitedot a7541256cb style: 관리자 > 회원관리 > 목록, 광고성 정보 수신 동의에 따른 테이블 폭 조정 처리 2025-09-25 10:02:25 +09:00
148 changed files with 16441 additions and 11313 deletions
+11
View File
@@ -13,6 +13,17 @@ cheditor5.*/
log/
g5_tree/
.vscode/
.claude/
.cursor/
.windsurf/
.idea/
.playwright-mcp
.mcp.json
.agents
.DS_Store
*.swp
*.swo
CLAUDE.local.md
naver*.html
initests01/
SIRsoft000/
+1 -1
View File
@@ -42,7 +42,7 @@ function print_menu2($key, $no = '')
continue;
}
if ($is_admin != 'super' && (!array_key_exists($menu[$key][$i][0], $auth) || !strstr($auth[$menu[$key][$i][0]], 'r'))) {
if ($is_admin != 'super' && (!array_key_exists($menu[$key][$i][0], $auth) || strpos($auth[$menu[$key][$i][0]], 'r') === false)) {
continue;
}
+2 -2
View File
@@ -281,7 +281,7 @@ function auth_check($auth, $attr, $return = false)
$attr = strtolower($attr);
if (!strstr($auth, $attr)) {
if (strpos($auth, $attr) === false) {
if ($attr == 'r') {
$msg = '읽을 권한이 없습니다.';
if ($return) {
@@ -389,7 +389,7 @@ function order_select($fld, $sel = '')
// 불법접근을 막도록 토큰을 생성하면서 토큰값을 리턴
function get_admin_token()
{
$token = md5(uniqid(rand(), true));
$token = get_random_token_string(16);
set_session('ss_admin_token', $token);
return $token;
+1 -1
View File
@@ -17,7 +17,7 @@ $print_version = ($is_admin == 'super') ? 'Version ' . G5_GNUBOARD_VER : '';
</div>
<footer id="ft">
<p>
Copyright &copy; <?php echo $_SERVER['HTTP_HOST']; ?>. All rights reserved. <?php echo $print_version; ?><br>
Copyright &copy; <?php echo htmlspecialchars($_SERVER['HTTP_HOST']); ?>. All rights reserved. <?php echo $print_version; ?><br>
<button type="button" class="scroll_top"><span class="top_img"></span><span class="top_txt">TOP</span></button>
</p>
</footer>
+4 -1
View File
@@ -23,6 +23,9 @@ if (!$sst) {
$sst = "a.mb_id, au_menu";
$sod = "";
}
$allowed_sst = array('a.mb_id', 'mb_nick', 'au_menu', 'au_auth', 'a.mb_id, au_menu');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'a.mb_id, au_menu';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by $sst $sod ";
$sql = " select count(*) as cnt
@@ -151,7 +154,7 @@ $colspan = 5;
//if (isset($stx))
// echo '<script>document.fsearch.sfl.value = "'.$sfl.'";</script>'."\n";
if (strstr($sfl, 'mb_id')) {
if (strpos($sfl, 'mb_id') !== false) {
$mb_id = $stx;
} else {
$mb_id = '';
+16 -13
View File
@@ -39,14 +39,17 @@ $bo_include_tail = isset($_POST['bo_include_tail']) ? preg_replace(array("#[\\\]
$check_captcha = false;
// 관리자가 자동등록방지 CAPTCHA를 사용해야 할 경우
if ($w === 'u') {
if (isset($board['bo_include_head'], $board['bo_include_tail']) &&
($board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail)) {
$check_captcha = true;
}
} elseif ($w === '') {
if ($bo_include_head !== '_head.php' || $bo_include_tail !== '_tail.php') {
$check_captcha = true;
// 최고 관리자인 경우에만 수정가능
if ($is_admin === 'super') {
if ($w === 'u') {
if (isset($board['bo_include_head'], $board['bo_include_tail']) &&
($board['bo_include_head'] !== $bo_include_head || $board['bo_include_tail'] !== $bo_include_tail)) {
$check_captcha = true;
}
} elseif ($w === '') {
if ($bo_include_head !== '_head.php' || $bo_include_tail !== '_tail.php') {
$check_captcha = true;
}
}
}
@@ -136,7 +139,7 @@ $bo_hot = isset($_POST['bo_hot']) ? (int) $_POST['bo_hot'] : 0;
$bo_image_width = isset($_POST['bo_image_width']) ? (int) $_POST['bo_image_width'] : 0;
$bo_use_search = isset($_POST['bo_use_search']) ? (int) $_POST['bo_use_search'] : 0;
$bo_use_cert = isset($_POST['bo_use_cert']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['bo_use_cert']) : '';
$bo_device = isset($_POST['bo_device']) ? clean_xss_tags($_POST['bo_device'], 1, 1) : '';
$bo_device = isset($_POST['bo_device']) ? addslashes(clean_xss_tags(stripslashes($_POST['bo_device']), 1, 1)) : '';
$bo_list_level = isset($_POST['bo_list_level']) ? (int) $_POST['bo_list_level'] : 0;
$bo_read_level = isset($_POST['bo_read_level']) ? (int) $_POST['bo_read_level'] : 0;
$bo_write_level = isset($_POST['bo_write_level']) ? (int) $_POST['bo_write_level'] : 0;
@@ -152,9 +155,9 @@ $bo_read_point = isset($_POST['bo_read_point']) ? (int) $_POST['bo_read_point']
$bo_write_point = isset($_POST['bo_write_point']) ? (int) $_POST['bo_write_point'] : 0;
$bo_comment_point = isset($_POST['bo_comment_point']) ? (int) $_POST['bo_comment_point'] : 0;
$bo_download_point = isset($_POST['bo_download_point']) ? (int) $_POST['bo_download_point'] : 0;
$bo_select_editor = isset($_POST['bo_select_editor']) ? clean_xss_tags($_POST['bo_select_editor'], 1, 1) : '';
$bo_skin = isset($_POST['bo_skin']) ? clean_xss_tags($_POST['bo_skin'], 1, 1) : '';
$bo_mobile_skin = isset($_POST['bo_mobile_skin']) ? clean_xss_tags($_POST['bo_mobile_skin'], 1, 1) : '';
$bo_select_editor = isset($_POST['bo_select_editor']) ? addslashes(clean_xss_tags(stripslashes($_POST['bo_select_editor']), 1, 1)) : '';
$bo_skin = isset($_POST['bo_skin']) ? addslashes(clean_xss_tags(stripslashes($_POST['bo_skin']), 1, 1)) : '';
$bo_mobile_skin = isset($_POST['bo_mobile_skin']) ? addslashes(clean_xss_tags(stripslashes($_POST['bo_mobile_skin']), 1, 1)) : '';
$bo_content_head = isset($_POST['bo_content_head']) ? $_POST['bo_content_head'] : '';
$bo_content_tail = isset($_POST['bo_content_tail']) ? $_POST['bo_content_tail'] : '';
$bo_mobile_content_head = isset($_POST['bo_mobile_content_head']) ? $_POST['bo_mobile_content_head'] : '';
@@ -173,7 +176,7 @@ $bo_write_min = isset($_POST['bo_write_min']) ? (int) $_POST['bo_write_min'] : 0
$bo_write_max = isset($_POST['bo_write_max']) ? (int) $_POST['bo_write_max'] : 0;
$bo_comment_min = isset($_POST['bo_comment_min']) ? (int) $_POST['bo_comment_min'] : 0;
$bo_comment_max = isset($_POST['bo_comment_max']) ? (int) $_POST['bo_comment_max'] : 0;
$bo_sort_field = isset($_POST['bo_sort_field']) ? clean_xss_tags($_POST['bo_sort_field'], 1, 1) : '';
$bo_sort_field = isset($_POST['bo_sort_field']) ? addslashes(clean_xss_tags(stripslashes($_POST['bo_sort_field']), 1, 1)) : '';
if (strpbrk($bo_skin.$bo_mobile_skin, "?%*:|\"<>") !== false) {
alert('스킨 디렉토리명 오류!');
+3
View File
@@ -32,6 +32,9 @@ if (!$sst) {
$sst = "a.gr_id, a.bo_table";
$sod = "asc";
}
$allowed_sst = array('a.gr_id', 'bo_table', 'bo_skin', 'bo_mobile_skin', 'bo_subject', 'bo_use_sns', 'bo_use_search', 'bo_order', 'a.gr_id, a.bo_table');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'a.gr_id, a.bo_table';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by $sst $sod ";
$sql = " select count(*) as cnt {$sql_common} {$sql_search} {$sql_order} ";
+3
View File
@@ -34,6 +34,9 @@ if ($stx) {
$sql_search .= " ) ";
}
$allowed_sst = array('gr_id', 'gr_subject', 'gr_admin', 'gr_order');
if ($sst && !in_array($sst, $allowed_sst)) $sst = '';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
if ($sst) {
$sql_order = " order by {$sst} {$sod} ";
} else {
+3
View File
@@ -28,6 +28,9 @@ if (!$sst) {
$sst = "gm_datetime";
$sod = "desc";
}
$allowed_sst = array('gm_datetime', 'b.mb_id', 'b.mb_name', 'b.mb_nick', 'b.mb_today_login', 'a.gm_datetime');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'gm_datetime';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by {$sst} {$sod} ";
$sql = " select count(*) as cnt
+2 -2
View File
@@ -35,8 +35,8 @@ $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']) ? clean_xss_tags($_POST['co_skin'], 1, 1) : '';
$co_mobile_skin = isset($_POST['co_mobile_skin']) ? clean_xss_tags($_POST['co_mobile_skin'], 1, 1) : '';
$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()) {
+1 -1
View File
@@ -586,7 +586,7 @@ td.td_grpset {width:160px;border-left:1px solid #e9ecee;text-align:center}
.td_time{text-align:center;width:130px}
.td_center{text-align:center;}
.td_type{width:120px}
.td_consent{width:200px}
.td_consent{min-width:70px;max-width:200px}
.td_mng_s{width:60px}
.td_mng_m{width:100px}
+57
View File
@@ -317,6 +317,63 @@ if (!isset($member['mb_marketing_agree'])) {
$is_check = true;
}
// 쿠폰 로그 테이블에 UNIQUE 인덱스 추가 (쿠폰 이중사용 방지)
if (defined('G5_USE_SHOP') && G5_USE_SHOP) {
$result = sql_query("SHOW INDEX FROM `{$g5['g5_shop_coupon_log_table']}` WHERE Key_name = 'idx_coupon_use'", false);
if (!$result || !sql_num_rows($result)) {
// 기존에 동일 쿠폰이 중복 사용된 데이터가 있으면 UNIQUE 인덱스 생성 실패하므로 중복 데이터 정리
$dup_sql = " SELECT cp_id, mb_id, MIN(cl_id) as keep_id
FROM `{$g5['g5_shop_coupon_log_table']}`
GROUP BY cp_id, mb_id
HAVING COUNT(*) > 1 ";
$dup_result = sql_query($dup_sql, false);
if ($dup_result && sql_num_rows($dup_result)) {
while ($dup_row = sql_fetch_array($dup_result)) {
echo $dup_row['cp_id']." 의 동일 쿠폰이 중복 사용된 데이터가 있으므로 인덱스 생성이 불가합니다. <br>";
$sql = " DELETE FROM `{$g5['g5_shop_coupon_log_table']}`
WHERE cp_id = '{$dup_row['cp_id']}'
AND mb_id = '{$dup_row['mb_id']}'
AND cl_id != '{$dup_row['keep_id']}' ";
if ($is_admin === 'super') {
echo "데이터베이스에서 검토후에 이 쿼리문을 실행해 주세요.<br>$sql<br>";
}
// sql_query($sql);
}
}
// MyISAM + utf8mb4 환경에서 키 길이 초과 방지: cp_id varchar(100), mb_id varchar(100)으로 조정
sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` MODIFY `cp_id` varchar(100) NOT NULL DEFAULT '', MODIFY `mb_id` varchar(100) NOT NULL DEFAULT ''", false);
sql_query("ALTER TABLE `{$g5['g5_shop_coupon_log_table']}` ADD UNIQUE KEY `idx_coupon_use` (`cp_id`, `mb_id`)", false);
$is_check = true;
}
}
// 자동 로그인 토큰 테이블 생성 (KVE-2026-0610: 추측 가능한 자동 로그인 쿠키 위조 방지)
// 다중 디바이스 지원을 위해 회원당 여러 토큰을 별도 테이블로 관리
if (!isset($g5['member_auto_login_table'])) {
$g5['member_auto_login_table'] = G5_TABLE_PREFIX.'member_auto_login';
}
if (!sql_query(" DESC `{$g5['member_auto_login_table']}` ", false)) {
sql_query(" CREATE TABLE IF NOT EXISTS `{$g5['member_auto_login_table']}` (
`al_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(20) NOT NULL default '',
`al_token` varchar(64) NOT NULL default '',
`al_user_agent` varchar(255) NOT NULL default '',
`al_ip` varchar(45) NOT NULL default '',
`al_created` datetime DEFAULT NULL,
`al_last_used` datetime DEFAULT NULL,
`al_expire` datetime DEFAULT NULL,
PRIMARY KEY (`al_id`),
UNIQUE KEY `al_token` (`al_token`),
KEY `mb_id` (`mb_id`),
KEY `al_expire` (`al_expire`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ", true);
$is_check = true;
}
$is_check = run_replace('admin_dbupgrade', $is_check);
$db_upgrade_msg = $is_check ? 'DB 업그레이드가 완료되었습니다.' : '더 이상 업그레이드 할 내용이 없습니다.<br>현재 DB 업그레이드가 완료된 상태입니다.';
+9 -4
View File
@@ -284,14 +284,19 @@ if (!auth_check_menu($auth, '200200', 'r', true)) {
</thead>
<tbody>
<?php
$row2['mb_id'] = '';
$row2 = array('mb_id'=>'');
for ($i = 0; $row = sql_fetch_array($result); $i++) {
if ($row2['mb_id'] != $row['mb_id']) {
if (empty($row2) || $row2['mb_id'] != $row['mb_id']) {
$sql2 = " SELECT mb_id, mb_name, mb_nick, mb_email, mb_homepage, mb_point from {$g5['member_table']} where mb_id = '{$row['mb_id']}' ";
$row2 = sql_fetch($sql2);
}
$mb_nick = get_sideview($row['mb_id'], $row2['mb_nick'], $row2['mb_email'], $row2['mb_homepage']);
$mb_nick = get_sideview(
$row['mb_id'],
isset($row2['mb_nick']) ? $row2['mb_nick'] : '',
isset($row2['mb_email']) ? $row2['mb_email'] : '',
isset($row2['mb_homepage']) ? $row2['mb_homepage'] : ''
);
$link1 = $link2 = "";
if (!preg_match("/^\@/", $row['po_rel_table']) && $row['po_rel_table']) {
@@ -302,7 +307,7 @@ if (!auth_check_menu($auth, '200200', 'r', true)) {
<tr>
<td class="td_mbid"><a href="./point_list.php?sfl=mb_id&amp;stx=<?php echo $row['mb_id'] ?>"><?php echo $row['mb_id'] ?></a></td>
<td class="td_mbname"><?php echo get_text($row2['mb_name']); ?></td>
<td class="td_mbname"><?php echo isset($row2['mb_name']) ? get_text($row2['mb_name']) : ''; ?></td>
<td class="td_name sv_use">
<div><?php echo $mb_nick ?></div>
</td>
+1 -1
View File
@@ -80,7 +80,7 @@ require_once './admin.head.php';
<tr>
<th scope="row"><label for="mb_email">E-mail</label></th>
<td>
<?php echo help("메일 주소에 단어 포함 (예 : @" . preg_replace('#^(www[^\.]*\.){1}#', '', $_SERVER['HTTP_HOST']) . ")") ?>
<?php echo help("메일 주소에 단어 포함 (예 : @" . htmlspecialchars(preg_replace('#^(www[^\.]*\.){1}#', '', $_SERVER['HTTP_HOST'])) . ")") ?>
<input type="text" name="mb_email" value="<?php echo get_sanitize_input($mb_email); ?>" id="mb_email" class="frm_input" size="50">
</td>
</tr>
+4 -4
View File
@@ -12,10 +12,10 @@ $sql_common = " from {$g5['member_table']} ";
$sql_where = " where (1) ";
$mb_id1 = isset($_POST['mb_id1']) ? (int) $_POST['mb_id1'] : 1;
$mb_id1_from = isset($_POST['mb_id1_from']) ? clean_xss_tags($_POST['mb_id1_from'], 1, 1, 30) : '';
$mb_id1_to = isset($_POST['mb_id1_to']) ? clean_xss_tags($_POST['mb_id1_to'], 1, 1, 30) : '';
$mb_email = isset($_POST['mb_email']) ? clean_xss_tags($_POST['mb_email'], 1, 1, 100) : '';
$mb_mailling = isset($_POST['mb_mailling']) ? clean_xss_tags($_POST['mb_mailling'], 1, 1, 100) : '';
$mb_id1_from = isset($_POST['mb_id1_from']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_id1_from']), 1, 1, 30)) : '';
$mb_id1_to = isset($_POST['mb_id1_to']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_id1_to']), 1, 1, 30)) : '';
$mb_email = isset($_POST['mb_email']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_email']), 1, 1, 100)) : '';
$mb_mailling = isset($_POST['mb_mailling']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_mailling']), 1, 1, 100)) : '';
$mb_level_from = isset($_POST['mb_level_from'])? (int) $_POST['mb_level_from'] : 1;
$mb_level_to = isset($_POST['mb_level_to']) ? (int) $_POST['mb_level_to'] : 10;
+4 -4
View File
@@ -19,8 +19,8 @@ $mb_certify = isset($_POST['mb_certify']) ? preg_replace('/[^0-9a-z_]/i', ''
$mb_zip = isset($_POST['mb_zip']) ? preg_replace('/[^0-9a-z_]/i', '', $_POST['mb_zip']) : '';
// 광고성 정보 수신
$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? clean_xss_tags($_POST['mb_marketing_agree'], 1, 1) : '0';
$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? clean_xss_tags($_POST['mb_thirdparty_agree'], 1, 1) : '0';
$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_marketing_agree']), 1, 1)) : '0';
$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? addslashes(clean_xss_tags(stripslashes($_POST['mb_thirdparty_agree']), 1, 1)) : '0';
// 관리자가 자동등록방지를 사용해야 할 경우 ( 회원의 비밀번호 변경시 캡챠를 체크한다 )
if ($mb_password) {
@@ -84,9 +84,9 @@ for ($i = 1; $i <= 10; $i++) {
foreach ($check_keys as $key) {
if( in_array($key, array('mb_signature', 'mb_profile')) ){
$posts[$key] = isset($_POST[$key]) ? clean_xss_tags($_POST[$key], 1, 1, 0, 0) : '';
$posts[$key] = isset($_POST[$key]) ? addslashes(clean_xss_tags(stripslashes($_POST[$key]), 1, 1, 0, 0)) : '';
} else {
$posts[$key] = isset($_POST[$key]) ? clean_xss_tags($_POST[$key], 1, 1) : '';
$posts[$key] = isset($_POST[$key]) ? addslashes(clean_xss_tags(stripslashes($_POST[$key]), 1, 1)) : '';
}
}
+14 -9
View File
@@ -6,6 +6,12 @@ auth_check_menu($auth, $sub_menu, 'r');
$sql_common = " from {$g5['member_table']} ";
// $sfl 화이트리스트 검증 (KVE-2026-0340)
$allowed_sfl = array('mb_id', 'mb_nick', 'mb_name', 'mb_level', 'mb_email', 'mb_tel', 'mb_hp', 'mb_point', 'mb_datetime', 'mb_ip', 'mb_recommend');
if (!in_array($sfl, $allowed_sfl)) {
$sfl = 'mb_id';
}
$sql_search = " where (1) ";
if ($stx) {
$sql_search .= " and ( ";
@@ -127,7 +133,7 @@ $colspan = 16;
<th scope="col" rowspan="2" id="mb_list_cert"><?php echo subject_sort_link('mb_certify', '', 'desc') ?>본인확인</a></th>
<th scope="col" id="mb_list_mailc"><?php echo subject_sort_link('mb_email_certify', '', 'desc') ?>메일인증</a></th>
<th scope="col" id="mb_list_open"><?php echo subject_sort_link('mb_open', '', 'desc') ?>정보공개</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>광고성이메일수신</a></th>
<th scope="col" id="mb_list_mailr"><?php echo subject_sort_link('mb_mailling', '', 'desc') ?>광고성이메일</a></th>
<th scope="col" id="mb_list_auth">상태</th>
<th scope="col" id="mb_list_mobile">휴대폰</th>
<th scope="col" id="mb_list_lastcall"><?php echo subject_sort_link('mb_today_login', '', 'desc') ?>최종접속</a></th>
@@ -137,9 +143,9 @@ $colspan = 16;
<tr>
<th scope="col" id="mb_list_name"><?php echo subject_sort_link('mb_name') ?>이름</a></th>
<th scope="col" id="mb_list_nick"><?php echo subject_sort_link('mb_nick') ?>닉네임</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>광고성SMS/카카오톡수신</a></th>
<th scope="col" id="mb_list_adultc"><?php echo subject_sort_link('mb_adult', '', 'desc') ?>성인인증</a></th>
<th scope="col" id="mb_list_auth"><?php echo subject_sort_link('mb_intercept_date', '', 'desc') ?>접근차단</a></th>
<th scope="col" id="mb_list_sms"><?php echo subject_sort_link('mb_sms', '', 'desc') ?>광고성SMS</a></th>
<th scope="col" id="mb_list_deny"><?php echo subject_sort_link('mb_level', '', 'desc') ?>권한</a></th>
<th scope="col" id="mb_list_tel">전화번호</th>
<th scope="col" id="mb_list_join"><?php echo subject_sort_link('mb_datetime', '', 'desc') ?>가입일</a></th>
@@ -258,7 +264,7 @@ $colspan = 16;
<input type="checkbox" name="mb_open[<?php echo $i; ?>]" <?php echo $row['mb_open'] ? 'checked' : ''; ?> value="1" id="mb_open_<?php echo $i; ?>">
</td>
<td headers="mb_list_mailr" class="td_consent">
<label for="mb_mailling_<?php echo $i; ?>" class="sound_only">메일수신</label>
<label for="mb_mailling_<?php echo $i; ?>" class="sound_only">광고성이메일수신</label>
<input type="checkbox" name="mb_mailling[<?php echo $i; ?>]" <?php echo $row['mb_mailling'] ? 'checked' : ''; ?> value="1" id="mb_mailling_<?php echo $i; ?>">
<input type="hidden" name="mb_mailling_default[<?php echo $i; ?>]" value="<?php echo isset($row['mb_mailling']) ? $row['mb_mailling'] : '0'; ?> " id="mb_mailling_default_<?php echo $i; ?>">
</td>
@@ -281,12 +287,6 @@ $colspan = 16;
<td headers="mb_list_nick" class="td_name sv_use">
<div><?php echo $mb_nick ?></div>
</td>
<td headers="mb_list_sms">
<label for="mb_sms_<?php echo $i; ?>" class="sound_only">SMS수신</label>
<input type="checkbox" name="mb_sms[<?php echo $i; ?>]" <?php echo $row['mb_sms'] ? 'checked' : ''; ?> value="1" id="mb_sms_<?php echo $i; ?>">
<input type="hidden" name="mb_sms_default[<?php echo $i; ?>]" value="<?php echo isset($row['mb_sms']) ? $row['mb_sms'] : '0'; ?> " id="mb_sms_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_adultc">
<label for="mb_adult_<?php echo $i; ?>" class="sound_only">성인인증</label>
<input type="checkbox" name="mb_adult[<?php echo $i; ?>]" <?php echo $row['mb_adult'] ? 'checked' : ''; ?> value="1" id="mb_adult_<?php echo $i; ?>">
@@ -297,6 +297,11 @@ $colspan = 16;
<label for="mb_intercept_date_<?php echo $i; ?>" class="sound_only">접근차단</label>
<?php } ?>
</td>
<td headers="mb_list_sms">
<label for="mb_sms_<?php echo $i; ?>" class="sound_only">광고성SMS/카카오톡수신</label>
<input type="checkbox" name="mb_sms[<?php echo $i; ?>]" <?php echo $row['mb_sms'] ? 'checked' : ''; ?> value="1" id="mb_sms_<?php echo $i; ?>">
<input type="hidden" name="mb_sms_default[<?php echo $i; ?>]" value="<?php echo isset($row['mb_sms']) ? $row['mb_sms'] : '0'; ?> " id="mb_sms_default_<?php echo $i; ?>">
</td>
<td headers="mb_list_auth" class="td_mbstat">
<?php echo get_member_level_select("mb_level[$i]", 1, $member['mb_level'], $row['mb_level']) ?>
</td>
+4 -1
View File
@@ -25,6 +25,9 @@ if (!$sst) {
$sst = "po_id";
$sod = "desc";
}
$allowed_sst = array('po_id', 'mb_id', 'po_content', 'po_point', 'po_datetime');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'po_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by {$sst} {$sod} ";
$sql = " select count(*) as cnt
@@ -66,7 +69,7 @@ if ($config['cf_point_term'] > 0) {
$po_expire_term = $config['cf_point_term'];
}
if (strstr($sfl, "mb_id")) {
if (strpos($sfl, "mb_id") !== false) {
$mb_id = $stx;
} else {
$mb_id = "";
+3
View File
@@ -21,6 +21,9 @@ if (!$sst) {
$sst = "po_id";
$sod = "desc";
}
$allowed_sst = array('po_id', 'po_subject', 'po_level', 'po_use', 'po_etc');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'po_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by {$sst} {$sod} ";
$sql = " select count(*) as cnt
+3
View File
@@ -36,6 +36,9 @@ if (!$sst) {
$sst = "pp_id";
$sod = "desc";
}
$allowed_sst = array('pp_id', 'pp_word', 'pp_date', 'pp_ip');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'pp_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by {$sst} {$sod} ";
$sql = " select count(*) as cnt
+1 -1
View File
@@ -31,7 +31,7 @@ include_once("./admin.head.php");
echo $list_tag_st;
while($file=readdir($dir)) {
if (!strstr($file,'sess_')) continue;
if (strpos($file,'sess_') === false) continue;
if (strpos($file,'sess_')!=0) continue;
$session_file = G5_DATA_PATH.'/session/'.$file;
+3
View File
@@ -42,6 +42,9 @@ if (!$sst)
$sst = "ca_id";
$sod = "asc";
}
$allowed_sst = array('ca_id', 'ca_name', 'ca_mb_id', 'ca_use');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'ca_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = "order by $sst $sod";
// 출력할 레코드를 얻음
+11 -11
View File
@@ -640,7 +640,7 @@ if(!$default['de_kakaopay_cancelpwd']){
<tr id="kcp_vbank_url" class="pg_vbank_url">
<th scope="row">NHN KCP 가상계좌<br>입금통보 URL</th>
<td>
<?php echo help("NHN KCP 가상계좌 사용시 다음 주소를 <strong><a href=\"http://admin.kcp.co.kr\" target=\"_blank\">NHN KCP 관리자</a> &gt; 상점정보관리 &gt; 정보변경 &gt; 공통URL 정보 &gt; 공통URL 변경후</strong>에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?>
<?php echo help("NHN KCP 가상계좌 사용시 다음 주소를 <strong><a href=\"https://partner.kcp.co.kr\" target=\"_blank\">NHN KCP 관리자</a> &gt; 상점정보관리 &gt; 정보변경 &gt; 공통URL 정보 &gt; 공통URL 변경후</strong>에 넣으셔야 상점에 자동으로 입금 통보됩니다."); ?>
<?php echo G5_SHOP_URL; ?>/settle_kcp_common.php</td>
</tr>
<tr id="inicis_vbank_url" class="pg_vbank_url">
@@ -694,7 +694,7 @@ if(!$default['de_kakaopay_cancelpwd']){
<tr>
<th scope="row"><label for="de_easy_pay_use">PG사 간편결제 버튼 사용</label></th>
<td>
<?php echo help("주문서 작성 페이지에 PG사 간편결제(PAYCO, PAYNOW, KPAY) 버튼의 별도 사용 여부를 설정합니다.", 50); ?>
<?php echo help("주문서 작성 페이지에 PG사 간편결제(PAYCO, 토스, KPAY...) 버튼의 별도 사용 여부를 설정합니다.", 50); ?>
<select id="de_easy_pay_use" name="de_easy_pay_use">
<option value="0" <?php echo get_selected($default['de_easy_pay_use'], 0); ?>>노출안함</option>
<option value="1" <?php echo get_selected($default['de_easy_pay_use'], 1); ?>>노출함</option>
@@ -715,13 +715,13 @@ if(!$default['de_kakaopay_cancelpwd']){
<?php
$account_checked = $vbank_checked = $transfer_checked = '';
if (strstr($default['de_taxsave_types'], 'account')) {
if (strpos($default['de_taxsave_types'], 'account') !== false) {
$account_checked = 'checked="checked"';
}
if (strstr($default['de_taxsave_types'], 'vbank')) {
if (strpos($default['de_taxsave_types'], 'vbank') !== false) {
$vbank_checked = 'checked="checked"';
}
if (strstr($default['de_taxsave_types'], 'transfer')) {
if (strpos($default['de_taxsave_types'], 'transfer') !== false) {
$transfer_checked = 'checked="checked"';
}
?>
@@ -1097,8 +1097,7 @@ if(!$default['de_kakaopay_cancelpwd']){
<a href="http://testadmin8.kcp.co.kr/" target="_blank" class="btn_frmline">테스트 관리자</a>
</div>
<div class="scf_cardtest lg_cardtest">
<a href="https://app.tosspayments.com/" target="_blank" class="btn_frmline">실결제 관리자</a>
<a href="https://pgweb.tosspayments.com/tmert" target="_blank" class="btn_frmline">테스트 관리자</a>
<a href="https://app.tosspayments.com/" target="_blank" class="btn_frmline">상점 관리자</a>
</div>
<div class="scf_cardtest toss_cardtest">
<a href="https://app.tosspayments.com/" target="_blank" class="btn_frmline">상점 관리자</a>
@@ -1122,14 +1121,14 @@ if(!$default['de_kakaopay_cancelpwd']){
<dt>휴대폰</dt><dd>테스트 지원되지 않음.</dd>
</dl>
<ul id="kcp_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li>테스트결제의 <a href="http://testadmin8.kcp.co.kr/assist/login.LoginAction.do" target="_blank">상점관리자</a> 로그인 정보는 NHN KCP로 문의하시기 바랍니다. (기술지원 1544-8661)</li>
<li>테스트결제의 <a href="https://testpartner.kcp.co.kr/" target="_blank">상점관리자</a> 로그인 정보는 NHN KCP로 문의하시기 바랍니다. (기술지원 1544-8661)</li>
<li><b>일반결제</b>의 테스트 사이트코드는 <b>T0000</b> 이며, <b>에스크로 결제</b>의 테스트 사이트코드는 <b>T0007</b> 입니다.</li>
</ul>
<ul id="lg_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li>테스트결제의 <a href="https://pgweb.tosspayments.com/tmert" target="_blank">상점관리자</a> 로그인 정보는 토스페이먼츠 상점아이디 첫 글자에 t를 추가해서 로그인하시기 바랍니다. 예) tsi_lguplus</li>
<li>테스트 결제건에 대한 <a href="https://app.tosspayments.com/" target="_blank">상점관리자</a> 접근은, 상점관리자 상단 '테스트 모드'를 활성화 하여서 접근할 수 있습니다.</li>
</ul>
<ul id="toss_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li>테스트 결제 시 <a href="https://app.tosspayments.com/" target="_blank">상점관리자</a> 로그인 정보는 실결제용 키와는 다르니 반드시 <b>[테스트] API 연동 키</b>로 로그인해야 합니다. 예) test_ck_toss</li>
<li>테스트 결제건에 대한 <a href="https://app.tosspayments.com/" target="_blank">상점관리자</a> 접근은, 상점관리자 상단 '테스트 모드'를 활성화 하여서 접근할 수 있습니다.</li>
</ul>
<ul id="inicis_cardtest_tip" class="scf_cardtest_tip_adm scf_cardtest_tip_adm_hide">
<li><b>일반결제</b>의 테스트 사이트 mid는 <b>INIpayTest</b> 이며, <b>에스크로 결제</b>의 테스트 사이트 mid는 <b>iniescrow0</b> 입니다.</li>
@@ -2142,4 +2141,5 @@ if($default['de_iche_use'] || $default['de_vbank_use'] || $default['de_hp_use']
}
}
include_once (G5_ADMIN_PATH.'/admin.tail.php');
include_once (G5_ADMIN_PATH.'/admin.tail.php');
+2
View File
@@ -64,6 +64,8 @@ foreach($check_skin_keys as $key){
if( isset($_POST[$key]) && preg_match('#\.+(\/|\\\)#', $_POST[$key]) ){
alert('스킨설정에 유효하지 문자가 포함되어 있습니다.');
}
$$key = $_POST[$key] = sql_real_escape_string($_POST[$key]);
}
// 현금영수증 발급수단
+5
View File
@@ -6,6 +6,8 @@ auth_check_menu($auth, $sub_menu, "r");
$sql_common = " from {$g5['g5_shop_coupon_table']} ";
if ($sfl && !in_array($sfl, array('mb_id', 'cp_subject', 'cp_id'))) $sfl = '';
$sql_search = " where (1) ";
if ($stx) {
$sql_search .= " and ( ";
@@ -24,6 +26,9 @@ if (!$sst) {
$sst = "cp_no";
$sod = "desc";
}
$allowed_sst = array('cp_no', 'cp_id', 'cp_subject', 'mb_id', 'cp_end', 'cp_start', 'cp_method');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'cp_no';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = " order by {$sst} {$sod} ";
$sql = " select count(*) as cnt
+14 -14
View File
@@ -212,28 +212,28 @@ if($data['od_settle_case'] == 'KAKAOPAY')
$od_pg = 'KAKAOPAY';
$od_email = get_email_address($data['od_email']);
$od_name = clean_xss_tags($data['od_name']);
$od_tel = clean_xss_tags($data['od_tel']);
$od_hp = clean_xss_tags($data['od_hp']);
$od_name = addslashes(clean_xss_tags($data['od_name']));
$od_tel = addslashes(clean_xss_tags($data['od_tel']));
$od_hp = addslashes(clean_xss_tags($data['od_hp']));
$od_zip = preg_replace('/[^0-9]/', '', $data['od_zip']);
$od_zip1 = substr($od_zip, 0, 3);
$od_zip2 = substr($od_zip, 3);
$od_addr1 = clean_xss_tags($data['od_addr1']);
$od_addr2 = clean_xss_tags($data['od_addr2']);
$od_addr3 = clean_xss_tags($data['od_addr3']);
$od_addr1 = addslashes(clean_xss_tags($data['od_addr1']));
$od_addr2 = addslashes(clean_xss_tags($data['od_addr2']));
$od_addr3 = addslashes(clean_xss_tags($data['od_addr3']));
$od_addr_jibeon = preg_match("/^(N|R)$/", $data['od_addr_jibeon']) ? $data['od_addr_jibeon'] : '';
$od_b_name = clean_xss_tags($data['od_b_name']);
$od_b_tel = clean_xss_tags($data['od_b_tel']);
$od_b_hp = clean_xss_tags($data['od_b_hp']);
$od_b_name = addslashes(clean_xss_tags($data['od_b_name']));
$od_b_tel = addslashes(clean_xss_tags($data['od_b_tel']));
$od_b_hp = addslashes(clean_xss_tags($data['od_b_hp']));
$od_b_zip = preg_replace('/[^0-9]/', '', $data['od_b_zip']);
$od_b_zip1 = substr($od_b_zip, 0, 3);
$od_b_zip2 = substr($od_b_zip, 3);
$od_b_addr1 = clean_xss_tags($data['od_b_addr1']);
$od_b_addr2 = clean_xss_tags($data['od_b_addr2']);
$od_b_addr3 = clean_xss_tags($data['od_b_addr3']);
$od_b_addr1 = addslashes(clean_xss_tags($data['od_b_addr1']));
$od_b_addr2 = addslashes(clean_xss_tags($data['od_b_addr2']));
$od_b_addr3 = addslashes(clean_xss_tags($data['od_b_addr3']));
$od_b_addr_jibeon = preg_match("/^(N|R)$/", $data['od_b_addr_jibeon']) ? $data['od_b_addr_jibeon'] : '';
$od_memo = clean_xss_tags($data['od_memo'], 0, 1, 0, 0);
$od_deposit_name = clean_xss_tags($data['od_deposit_name']);
$od_memo = addslashes(clean_xss_tags($data['od_memo'], 0, 1, 0, 0));
$od_deposit_name = addslashes(clean_xss_tags($data['od_deposit_name']));
$od_tax_flag = $default['de_tax_flag_use'];
$od_receipt_price = $tot_ct_price + $od_send_cost + $od_send_cost2 - ($od_temp_point + $tot_cp_price + $tot_sc_cp_price);
$od_receipt_point = $od_temp_point;
+3 -1
View File
@@ -67,7 +67,9 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
for ($i=0; $i<$len; $i++)
$nbsp .= "&nbsp;&nbsp;&nbsp;";
$category_select .= "<option value=\"{$row['ca_id']}\">$nbsp{$row['ca_name']}</option>\n";
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row['ca_id']) : $row['ca_name'];
$category_select .= "<option value=\"{$row['ca_id']}\">$nbsp{$category_path}</option>\n";
}
// 모바일 1줄당 이미지수 필드 추가
+3 -1
View File
@@ -107,7 +107,9 @@ if($ev_id) {
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+8 -4
View File
@@ -146,9 +146,11 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
$nbsp = "";
for ($i=0; $i<$len; $i++)
$nbsp .= "&nbsp;&nbsp;&nbsp;";
$category_select .= "<option value=\"{$row['ca_id']}\">$nbsp{$row['ca_name']}</option>\n";
// 전체 카테고리 경로 표시 (예: 남성의류 > 상의 > 셔츠)
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row['ca_id']) : $row['ca_name'];
$category_select .= "<option value=\"{$row['ca_id']}\">$nbsp{$category_path}</option>\n";
$script .= "ca_use['{$row['ca_id']}'] = {$row['ca_use']};\n";
$script .= "ca_stock_qty['{$row['ca_id']}'] = {$row['ca_stock_qty']};\n";
//$script .= "ca_explan_html['$row[ca_id]'] = $row[ca_explan_html];\n";
@@ -1394,7 +1396,9 @@ $(function(){
for ($i=0; $i<$len; $i++)
$nbsp .= "&nbsp;&nbsp;&nbsp;";
echo "<option value=\"{$row['ca_id']}\">$nbsp{$row['ca_name']}</option>\n";
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row['ca_id']) : $row['ca_name'];
echo "<option value=\"{$row['ca_id']}\">$nbsp{$category_path}</option>\n";
}
?>
</select>
+8 -2
View File
@@ -25,7 +25,8 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
for ($i=0; $i<$len; $i++) {
$nbsp .= '&nbsp;&nbsp;&nbsp;';
}
$ca_list .= '<option value="'.$row['ca_id'].'">'.$nbsp.$row['ca_name'].'</option>'.PHP_EOL;
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row['ca_id']) : $row['ca_name'];
$ca_list .= '<option value="'.$row['ca_id'].'">'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
$where = " and ";
@@ -67,6 +68,9 @@ if (!$sst) {
$sst = "it_id";
$sod = "desc";
}
$allowed_sst = array('it_id', 'it_name', 'it_order', 'it_use', 'it_soldout', 'it_hit', 'it_price', 'it_cust_price', 'it_point', 'it_stock_qty');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'it_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = "order by $sst $sod";
@@ -100,7 +104,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = '';
for ($i=0; $i<$len; $i++) $nbsp .= '&nbsp;&nbsp;&nbsp;';
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sca, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sca, $row1['ca_id']).'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+6 -1
View File
@@ -31,6 +31,9 @@ if (!$sst) {
$sst = "iq_id";
$sod = "desc";
}
$allowed_sst = array('iq_id', 'a.it_id', 'it_name', 'iq_time');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'iq_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_common = " from {$g5['g5_shop_item_qa_table']} a
left join {$g5['g5_shop_item_table']} b on (a.it_id = b.it_id)
@@ -79,7 +82,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
$selected = ($row1['ca_id'] == $sca) ? ' selected="selected"' : '';
echo '<option value="'.$row1['ca_id'].'"'.$selected.'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'"'.$selected.'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+3 -1
View File
@@ -83,7 +83,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+3 -1
View File
@@ -82,7 +82,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+6 -1
View File
@@ -46,6 +46,9 @@ if (!$sst) {
$sst = "it_id";
$sod = "desc";
}
$allowed_sst = array('it_id', 'it_name', 'it_type1', 'it_type2', 'it_type3', 'it_type4', 'it_type5', 'ca_id');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'it_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_order = "order by $sst $sod";
$sql_common = " from {$g5['g5_shop_item_table']} ";
@@ -98,7 +101,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sca, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sca, $row1['ca_id']).'>'.$nbsp.$category_path.PHP_EOL;
}
?>
</select>
+6 -1
View File
@@ -33,6 +33,9 @@ if (!$sst) {
$sst = "is_id";
$sod = "desc";
}
$allowed_sst = array('is_id', 'a.it_id', 'it_name', 'is_name', 'is_score', 'is_time');
if ($sst && !in_array($sst, $allowed_sst)) $sst = 'is_id';
if ($sod && !in_array(strtolower($sod), array('asc', 'desc'))) $sod = '';
$sql_common = " from {$g5['g5_shop_item_use_table']} a
left join {$g5['g5_shop_item_table']} b on (a.it_id = b.it_id)
@@ -81,7 +84,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
$selected = ($row1['ca_id'] == $sca) ? ' selected="selected"' : '';
echo '<option value="'.$row1['ca_id'].'"'.$selected.'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'"'.$selected.'>'.$nbsp.$category_path.PHP_EOL;
}
?>
</select>
+3 -1
View File
@@ -84,7 +84,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$row1['ca_name'].'</option>'.PHP_EOL;
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo '<option value="'.$row1['ca_id'].'" '.get_selected($sel_ca_id, $row1['ca_id']).'>'.$nbsp.$category_path.'</option>'.PHP_EOL;
}
?>
</select>
+10 -16
View File
@@ -509,39 +509,33 @@ add_javascript(G5_POSTCODE_JS, 0); //다음 주소 js
<?php
if ($od['od_settle_case'] != '무통장') {
switch($od['od_pg']) {
case 'lg':
$pg_url = 'https://app.tosspayments.com';
$pg_test = '토스페이먼츠(구버전)';
if ($default['de_card_test']) {
$pg_url = 'https://pgweb.tosspayments.com/tmert';
$pg_test .= ' 테스트 ';
}
break;
case 'inicis':
$pg_url = 'https://iniweb.inicis.com/';
$pg_test = 'KG이니시스';
$pg_test = 'KG이니시스 ';
break;
case 'KAKAOPAY':
$pg_url = 'https://mms.cnspay.co.kr';
$pg_test = 'KAKAOPAY';
$pg_test = 'KAKAOPAY ';
break;
case 'nicepay':
$pg_url = 'https://npg.nicepay.co.kr/';
$pg_test = 'NICEPAY';
$pg_test = 'NICEPAY ';
break;
case 'lg':
case 'toss':
$pg_url = 'https://app.tosspayments.com';
$pg_test = '토스페이먼츠 ';
// 상점관리자 로그인 후 상단 '테스트 모드' 활성화 시 테스트 화면 노출
break;
default:
$pg_url = 'http://admin8.kcp.co.kr';
$pg_test = 'KCP';
$pg_url = 'https://partner.kcp.co.kr';
$pg_test = 'KCP ';
if ($default['de_card_test']) {
// 로그인 아이디 / 비번
// 일반 : test1234 / test12345
// 에스크로 : escrow / escrow913
$pg_url = 'http://testadmin8.kcp.co.kr';
$pg_test .= ' 테스트 ';
$pg_url = 'https://testpartner.kcp.co.kr';
$pg_test .= '테스트 ';
}
}
@@ -1170,4 +1164,4 @@ function chk_receipt_price()
</script>
<?php
include_once(G5_ADMIN_PATH.'/admin.tail.php');
include_once(G5_ADMIN_PATH.'/admin.tail.php');
+3 -3
View File
@@ -26,8 +26,8 @@ $od_cancel_price = isset($_GET['od_cancel_price']) ? preg_replace('/[^0-9a-z]/i'
$od_refund_price = isset($_GET['od_refund_price']) ? preg_replace('/[^0-9a-z]/i', '', $_GET['od_refund_price']) : '';
$od_receipt_point = isset($_GET['od_receipt_point']) ? preg_replace('/[^0-9a-z]/i', '', $_GET['od_receipt_point']) : '';
$od_coupon = isset($_GET['od_coupon']) ? preg_replace('/[^0-9a-z]/i', '', $_GET['od_coupon']) : '';
$od_settle_case = isset($_GET['od_settle_case']) ? clean_xss_tags($_GET['od_settle_case'], 1, 1) : '';
$od_escrow = isset($_GET['od_escrow']) ? clean_xss_tags($_GET['od_escrow'], 1, 1) : '';
$od_settle_case = isset($_GET['od_settle_case']) ? addslashes(clean_xss_tags(stripslashes($_GET['od_settle_case']), 1, 1)) : '';
$od_escrow = isset($_GET['od_escrow']) ? addslashes(clean_xss_tags(stripslashes($_GET['od_escrow']), 1, 1)) : '';
$tot_itemcount = $tot_orderprice = $tot_receiptprice = $tot_ordercancel = $tot_misu = $tot_couponprice = 0;
$sql_search = "";
@@ -350,7 +350,7 @@ if( function_exists('pg_setting_check') ){
if($default['de_escrow_use'] && $row['od_escrow'])
$od_paytype .= '<span class="list_escrow">에스크로</span>';
$uid = md5($row['od_id'].$row['od_time'].$row['od_ip']);
$uid = function_exists('get_shop_uid') ? get_shop_uid('order', $row['od_id'], $row['od_time'], $row['od_ip']) : md5($row['od_id'].$row['od_time'].$row['od_ip']);
$invoice_time = is_null_time($row['od_invoice_time']) ? G5_TIME_YMDHIS : $row['od_invoice_time'];
$delivery_company = $row['od_delivery_company'] ? $row['od_delivery_company'] : $default['de_delivery_company'];
+3 -1
View File
@@ -71,7 +71,9 @@ $listall = '<a href="'.$_SERVER['SCRIPT_NAME'].'" class="ov_listall">전체목
$len = strlen($row1['ca_id']) / 2 - 1;
$nbsp = "";
for ($i=0; $i<$len; $i++) $nbsp .= "&nbsp;&nbsp;&nbsp;";
echo "<option value='{$row1['ca_id']}'".get_selected($row1['ca_id'], $sel_ca_id).">$nbsp{$row1['ca_name']}\n";
// 전체 카테고리 경로 표시
$category_path = function_exists('get_shop_category_path') ? get_shop_category_path($row1['ca_id']) : $row1['ca_name'];
echo "<option value='{$row1['ca_id']}'".get_selected($row1['ca_id'], $sel_ca_id).">$nbsp{$category_path}\n";
}
?>
</select>
+1 -1
View File
@@ -4,7 +4,7 @@ include_once ('../../common.php');
include_once(G5_ADMIN_PATH.'/admin.lib.php');
include_once(G5_SMS5_PATH.'/sms5.lib.php');
if (!strstr($_SERVER['SCRIPT_NAME'], 'install.php')) {
if (strpos($_SERVER['SCRIPT_NAME'], 'install.php') === false) {
// SMS5 테이블 G5_TABLE_PREFIX 적용
if($g5['sms5_prefix'] != 'sms5_' && sql_num_rows(sql_query("show tables like 'sms5_config'")))
{
+7 -7
View File
@@ -10,14 +10,14 @@ check_admin_token();
$g5['title'] = "SMS 기본설정";
$cf_phone = isset($_REQUEST['cf_phone']) ? clean_xss_tags($_REQUEST['cf_phone'], 1, 1) : '';
$cf_sms_use = isset($_REQUEST['cf_sms_use']) ? clean_xss_tags($_REQUEST['cf_sms_use'], 1, 1) : '';
$cf_sms_type = isset($_REQUEST['cf_sms_type']) ? clean_xss_tags($_REQUEST['cf_sms_type'], 1, 1) : '';
$cf_icode_id = isset($_REQUEST['cf_icode_id']) ? clean_xss_tags($_REQUEST['cf_icode_id'], 1, 1) : '';
$cf_icode_pw = isset($_REQUEST['cf_icode_pw']) ? clean_xss_tags($_REQUEST['cf_icode_pw'], 1, 1) : '';
$cf_icode_server_ip = isset($_REQUEST['cf_icode_server_ip']) ? clean_xss_tags($_REQUEST['cf_icode_server_ip'], 1, 1) : '';
$cf_phone = isset($_REQUEST['cf_phone']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_phone']), 1, 1)) : '';
$cf_sms_use = isset($_REQUEST['cf_sms_use']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_sms_use']), 1, 1)) : '';
$cf_sms_type = isset($_REQUEST['cf_sms_type']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_sms_type']), 1, 1)) : '';
$cf_icode_id = isset($_REQUEST['cf_icode_id']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_icode_id']), 1, 1)) : '';
$cf_icode_pw = isset($_REQUEST['cf_icode_pw']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_icode_pw']), 1, 1)) : '';
$cf_icode_server_ip = isset($_REQUEST['cf_icode_server_ip']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_icode_server_ip']), 1, 1)) : '';
$cf_icode_server_port = isset($_REQUEST['cf_icode_server_port']) ? clean_xss_tags($_REQUEST['cf_icode_server_port'], 1, 1) : '';
$cf_icode_token_key = isset($_REQUEST['cf_icode_token_key']) ? clean_xss_tags($_REQUEST['cf_icode_token_key'], 1, 1) : '';
$cf_icode_token_key = isset($_REQUEST['cf_icode_token_key']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['cf_icode_token_key']), 1, 1)) : '';
// 회신번호 체크
if(!check_vaild_callback($cf_phone))
+1 -1
View File
@@ -114,7 +114,7 @@ function multi_update(sel)
<div class="local_sch01 local_sch sms_preset_sch">
<form>
<label for="fg_no" class="sound_only">그룹명</label>
<select name="fg_no" id="fg_no" onchange="location.href='<?php echo $_SERVER['SCRIPT_NAME']?>?fg_no='+this.value;">
<select name="fg_no" id="fg_no" onchange="location.href='<?php echo htmlspecialchars($_SERVER['SCRIPT_NAME'])?>?fg_no='+this.value;">
<option value="" <?php echo $fg_no?'':'selected'?>> 전체 </option>
<option value="0" <?php echo $fg_no=='0'?'selected':''?>> 미분류 (<?php echo number_format($no_count)?>) </option>
<?php for($i=0; $i<count($group); $i++) {?>
+1 -1
View File
@@ -130,7 +130,7 @@ function no_hp_click(val)
<form name="search_form" class="local_sch01 local_sch">
<label for="bg_no" class="sound_only">그룹명</label>
<select name="bg_no" id="bg_no" onchange="location.href='<?php echo $_SERVER['SCRIPT_NAME']?>?bg_no='+this.value;">
<select name="bg_no" id="bg_no" onchange="location.href='<?php echo htmlspecialchars($_SERVER['SCRIPT_NAME'])?>?bg_no='+this.value;">
<option value=""<?php echo get_selected('', $bg_no); ?>> 전체 </option>
<option value="<?php echo $no_group['bg_no']?>"<?php echo get_selected($no_group['bg_no'], $bg_no); ?>> <?php echo $no_group['bg_name']?> (<?php echo number_format($no_group['bg_count'])?> 명) </option>
<?php for($i=0; $i<count($group); $i++) {?>
+1 -1
View File
@@ -125,7 +125,7 @@ while ($row = array_shift($send_list))
continue 2;
}
array_push($list, array('bk_hp' => $hp, 'bk_name' => $name));
array_push($list, array('bk_hp' => $hp, 'bk_name' => $name, 'mb_id' => '', 'bg_no' => '', 'bk_no' => ''));
array_push($hps, $hp);
break;
+2 -1
View File
@@ -7,7 +7,8 @@ $content = isset($_POST['content']) ? strip_tags($_POST['content']) : '';
//$filter = explode(",", strtolower(trim($config['cf_filter'])));
// strtolower 에 의한 한글 변형으로 아래 코드로 대체 (곱슬최씨님이 알려 주셨습니다.)
$filter = explode(",", trim($config['cf_filter']));
for ($i=0; $i<count($filter); $i++) {
$filter_cnt = count($filter);
for ($i=0; $i<$filter_cnt; $i++) {
$str = $filter[$i];
// 제목 필터링 (찾으면 중지)
+5
View File
@@ -2,6 +2,11 @@
include_once('./_common.php');
include_once(G5_LIB_PATH.'/register.lib.php');
// 회원 ID 열거 공격 방지를 위한 Rate Limit (분당 30회)
if (function_exists('check_rate_limit') && !check_rate_limit('ajax_mb_id_check', 30, 60)) {
die('너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해 주세요.');
}
$mb_id = isset($_POST['reg_mb_id']) ? trim($_POST['reg_mb_id']) : '';
set_session('ss_check_mb_id', '');
+5
View File
@@ -2,6 +2,11 @@
include_once('./_common.php');
include_once(G5_LIB_PATH.'/register.lib.php');
// 회원 닉네임 열거 공격 방지를 위한 Rate Limit (분당 30회)
if (function_exists('check_rate_limit') && !check_rate_limit('ajax_mb_nick_check', 30, 60)) {
die('너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해 주세요.');
}
$mb_nick = isset($_POST['reg_mb_nick']) ? trim($_POST['reg_mb_nick']) : '';
$mb_id = isset($_POST['reg_mb_id']) ? trim($_POST['reg_mb_id']) : '';
+5
View File
@@ -2,6 +2,11 @@
include_once("./_common.php");
include_once(G5_LIB_PATH."/register.lib.php");
// 추천인 ID 열거 공격 방지를 위한 Rate Limit (분당 30회)
if (function_exists('check_rate_limit') && !check_rate_limit('ajax_mb_recommend_check', 30, 60)) {
die('너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해 주세요.');
}
$mb_recommend = isset($_POST["reg_mb_recommend"]) ? trim($_POST["reg_mb_recommend"]) : '';
if ($msg = valid_mb_id($mb_recommend)) {
+2
View File
@@ -1,6 +1,8 @@
<?php
include_once('./_common.php');
if (function_exists('check_request_origin')) check_request_origin(G5_BBS_URL);
$count = (isset($_POST['chk_wr_id']) && is_array($_POST['chk_wr_id'])) ? count($_POST['chk_wr_id']) : 0;
$post_btn_submit = isset($_POST['btn_submit']) ? clean_xss_tags($_POST['btn_submit'], 1, 1) : '';
+1 -1
View File
@@ -1,7 +1,7 @@
<?php
include_once('./_common.php');
$mb_id = isset($_REQUEST['mb_id']) ? clean_xss_tags($_REQUEST['mb_id'], 1, 1) : '';
$mb_id = isset($_REQUEST['mb_id']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['mb_id']), 1, 1)) : '';
$sql = " select mb_id, mb_email, mb_datetime from {$g5['member_table']} where mb_id = '{$mb_id}' ";
$row = sql_fetch($sql);
+24 -1
View File
@@ -9,6 +9,13 @@ if (!$config['cf_email_use'])
if (!$is_member && $config['cf_formmail_is_member'])
alert_close('회원만 이용하실 수 있습니다.');
// 발송 횟수 제한 (직접 POST 우회 방지)
$sendmail_count = (int)get_session('ss_sendmail_count') + 1;
set_session('ss_sendmail_count', $sendmail_count);
if ($sendmail_count > 3) {
alert_close('폼메일 발송 횟수를 초과하였습니다.');
}
$email_enc = new str_encrypt();
$to = $email_enc->decrypt($to);
@@ -40,7 +47,23 @@ if ($type) {
else
$mail_content = $content;
mailer($fnick, $fmail, $to, $subject, $mail_content, $type, $file);
// 발신자 정보 서버측 강제 설정
$reply_to_email = '';
if ($is_member) {
$fnick = (isset($member['mb_nick']) && $member['mb_nick']) ? $member['mb_nick'] : $member['mb_name'];
$fmail = $member['mb_email'];
} else {
// 비회원: From을 관리자 이메일로 고정 (발신자 위장 방지)
// 비회원이 입력한 이메일은 Reply-To로 설정하여 회신 기능 유지
$user_email = get_email_address(trim($fmail));
$fnick = strip_tags(trim($fnick));
$fmail = $config['cf_admin_email'];
if ($user_email) {
$reply_to_email = $user_email;
}
}
mailer($fnick, $fmail, $to, $subject, $mail_content, $type, $file, '', '', $reply_to_email);
// 임시 첨부파일 삭제
if(!empty($file)) {
+19 -8
View File
@@ -76,16 +76,22 @@ if(isset($_POST['js']) && $_POST['js'] === "on") {
}
else
{
// 추천(찬성), 비추천(반대) 카운트 증가
// 레이스 컨디션 방지: g5_board_good 테이블의 UNIQUE KEY(bo_table, wr_id, mb_id)를
// 이용해 INSERT IGNORE를 먼저 수행하고, 성공한 경우에만 카운터를 증가시킨다.
sql_query(" insert ignore into {$g5['board_good_table']} set bo_table = '{$bo_table}', wr_id = '{$wr_id}', mb_id = '{$member['mb_id']}', bg_flag = '{$good}', bg_datetime = '".G5_TIME_YMDHIS."' ");
if (get_sql_affected_rows() <= 0) {
$error = '이미 추천 또는 비추천 하신 글 입니다.';
print_result($error, $count);
}
// INSERT 성공 시에만 카운터 증가
sql_query(" update {$g5['write_prefix']}{$bo_table} set wr_{$good} = wr_{$good} + 1 where wr_id = '{$wr_id}' ");
// 내역 생성
sql_query(" insert {$g5['board_good_table']} set bo_table = '{$bo_table}', wr_id = '{$wr_id}', mb_id = '{$member['mb_id']}', bg_flag = '{$good}', bg_datetime = '".G5_TIME_YMDHIS."' ");
$sql = " select wr_{$good} as count from {$g5['write_prefix']}{$bo_table} where wr_id = '$wr_id' ";
$row = sql_fetch($sql);
$count = $row['count'];
run_event('bbs_increase_good_json', $bo_table, $wr_id, $good);
print_result($error, $count);
@@ -140,10 +146,15 @@ if(isset($_POST['js']) && $_POST['js'] === "on") {
}
else
{
// 추천(찬성), 비추천(반대) 카운트 증가
// 레이스 컨디션 방지: g5_board_good 테이블의 UNIQUE KEY(bo_table, wr_id, mb_id)를
// 이용해 INSERT IGNORE를 먼저 수행하고, 성공한 경우에만 카운터를 증가시킨다.
sql_query(" insert ignore into {$g5['board_good_table']} set bo_table = '{$bo_table}', wr_id = '{$wr_id}', mb_id = '{$member['mb_id']}', bg_flag = '{$good}', bg_datetime = '".G5_TIME_YMDHIS."' ");
if (get_sql_affected_rows() <= 0) {
alert('이미 추천 또는 비추천 하신 글 입니다.');
}
// INSERT 성공 시에만 카운터 증가
sql_query(" update {$g5['write_prefix']}{$bo_table} set wr_{$good} = wr_{$good} + 1 where wr_id = '{$wr_id}' ");
// 내역 생성
sql_query(" insert {$g5['board_good_table']} set bo_table = '{$bo_table}', wr_id = '{$wr_id}', mb_id = '{$member['mb_id']}', bg_flag = '{$good}', bg_datetime = '".G5_TIME_YMDHIS."' ");
if ($good == 'good')
$status = '추천';
@@ -151,7 +162,7 @@ if(isset($_POST['js']) && $_POST['js'] === "on") {
$status = '비추천';
$href = get_pretty_url($bo_table, $wr_id);
run_event('bbs_increase_good_html', $bo_table, $wr_id, $good, $href);
alert("이 글을 $status 하셨습니다.", '', false);
+87 -43
View File
@@ -14,7 +14,8 @@ if ($board['bo_use_category']) {
$category_option .= '>전체</a></li>';
$categories = explode('|', $board['bo_category_list']); // 구분자가 , 로 되어 있음
for ($i=0; $i<count($categories); $i++) {
$categories_cnt = count($categories);
for ($i=0; $i<$categories_cnt; $i++) {
$category = trim($categories[$i]);
if ($category=='') continue;
$category_option .= '<li><a href="'.(get_pretty_url($bo_table,'','sca='.urlencode($category))).'"';
@@ -89,15 +90,31 @@ if (!$is_search_bbs) {
$from_notice_idx = ($page - 1) * $page_rows;
if($from_notice_idx < 0)
$from_notice_idx = 0;
$board_notice_count = count($arr_notice);
for ($k=0; $k<$board_notice_count; $k++) {
if (trim($arr_notice[$k]) == '') continue;
// 유효한 공지 wr_id만 수집 (정수화로 SQL injection 방지)
$notice_ids_int = array();
foreach ($arr_notice as $k_idx => $nid_raw) {
$nid_int = (int) trim($nid_raw);
if ($nid_int > 0) {
$notice_ids_int[$k_idx] = $nid_int;
}
}
$row = sql_fetch(" select * from {$write_table} where wr_id = '{$arr_notice[$k]}' ");
// N+1 쿼리 제거: 공지 전체를 단일 IN 쿼리로 일괄 조회
$notice_rows_by_id = array();
if (!empty($notice_ids_int)) {
$sql = " select * from {$write_table} where wr_id in (" . implode(',', $notice_ids_int) . ") ";
$result = sql_query($sql);
while ($nrow = sql_fetch_array($result)) {
$notice_rows_by_id[$nrow['wr_id']] = $nrow;
}
}
if (!isset($row['wr_id']) || !$row['wr_id']) continue;
// 원래 bo_notice 순서대로 처리 (페이징 로직의 $k 인덱스 의미 보존)
foreach ($notice_ids_int as $k => $nid) {
if (!isset($notice_rows_by_id[$nid])) continue;
$row = $notice_rows_by_id[$nid];
$notice_array[] = $row['wr_id'];
if($k < $from_notice_idx) continue;
@@ -106,7 +123,7 @@ if (!$is_search_bbs) {
$list[$i]['is_notice'] = true;
$list[$i]['list_content'] = $list[$i]['wr_content'];
// 비밀글인 경우 리스트에서 내용이 출력되지 않게 글 내용을 지웁니다.
// 비밀글인 경우 리스트에서 내용이 출력되지 않게 글 내용을 지웁니다.
if (strstr($list[$i]['wr_option'], "secret")) {
$list[$i]['wr_content'] = '';
}
@@ -178,45 +195,72 @@ if ($sst) {
$sql_order = " order by {$sst} {$sod} ";
}
if ($is_search_bbs) {
$sql = " select distinct wr_parent from {$write_table} where {$sql_search} {$sql_order} limit {$from_record}, $page_rows ";
} else {
$sql = " select * from {$write_table} where wr_is_comment = 0 ";
if(!empty($notice_array))
$sql .= " and wr_id not in (".implode(', ', $notice_array).") ";
$sql .= " {$sql_order} limit {$from_record}, $page_rows ";
// 페이지의 공지개수가 목록수 보다 작을 때만 실행
$rows_to_process = array();
if ($page_rows > 0) {
if ($is_search_bbs) {
// 1차: 검색 결과의 wr_parent ID 목록 조회
$sql = " select distinct wr_parent from {$write_table} where {$sql_search} {$sql_order} limit {$from_record}, $page_rows ";
$result = sql_query($sql);
$parent_ids_in_order = array();
while ($id_row = sql_fetch_array($result)) {
$pid = (int) $id_row['wr_parent'];
if ($pid > 0) {
$parent_ids_in_order[] = $pid;
}
}
// 2차: N+1 쿼리 제거 - 단일 IN 쿼리로 전체 행 일괄 조회
if (!empty($parent_ids_in_order)) {
$batch_sql = " select * from {$write_table} where wr_id in (" . implode(',', $parent_ids_in_order) . ") ";
$batch_result = sql_query($batch_sql);
$rows_by_id = array();
while ($br = sql_fetch_array($batch_result)) {
$rows_by_id[$br['wr_id']] = $br;
}
// DISTINCT 쿼리의 원래 정렬 순서대로 재구성
foreach ($parent_ids_in_order as $pid) {
if (isset($rows_by_id[$pid])) {
$rows_to_process[] = $rows_by_id[$pid];
}
}
}
} else {
$sql = " select * from {$write_table} where wr_is_comment = 0 ";
if(!empty($notice_array))
$sql .= " and wr_id not in (".implode(', ', $notice_array).") ";
$sql .= " {$sql_order} limit {$from_record}, $page_rows ";
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$rows_to_process[] = $row;
}
}
}
// 페이지의 공지개수가 목록수 보다 작을 때만 실행
if($page_rows > 0) {
$result = sql_query($sql);
$k = 0;
while ($row = sql_fetch_array($result))
{
// 검색일 경우 wr_id만 얻었으므로 다시 한행을 얻는다
if ($is_search_bbs)
$row = sql_fetch(" select * from {$write_table} where wr_id = '{$row['wr_parent']}' ");
$list[$i] = get_list($row, $board, $board_skin_url, G5_IS_MOBILE ? $board['bo_mobile_subject_len'] : $board['bo_subject_len']);
if (strstr($sfl, 'subject')) {
$list[$i]['subject'] = search_font($stx, $list[$i]['subject']);
}
$list[$i]['is_notice'] = false;
$list[$i]['list_content'] = $list[$i]['wr_content'];
// 비밀글인 경우 리스트에서 내용이 출력되지 않게 글 내용을 지웁니다.
if (strstr($list[$i]['wr_option'], "secret")) {
$list[$i]['wr_content'] = '';
}
$list_num = $total_count - ($page - 1) * $list_page_rows - $notice_count;
$list[$i]['num'] = $list_num - $k;
$i++;
$k++;
$k = 0;
foreach ($rows_to_process as $row)
{
$list[$i] = get_list($row, $board, $board_skin_url, G5_IS_MOBILE ? $board['bo_mobile_subject_len'] : $board['bo_subject_len']);
if (strstr($sfl, 'subject')) {
$list[$i]['subject'] = search_font($stx, $list[$i]['subject']);
}
$list[$i]['is_notice'] = false;
$list[$i]['list_content'] = $list[$i]['wr_content'];
// 비밀글인 경우 리스트에서 내용이 출력되지 않게 글 내용을 지웁니다.
if (strstr($list[$i]['wr_option'], "secret")) {
$list[$i]['wr_content'] = '';
}
$list_num = $total_count - ($page - 1) * $list_page_rows - $notice_count;
$list[$i]['num'] = $list_num - $k;
$i++;
$k++;
}
g5_latest_cache_data($board['bo_table'], $list);
+27 -4
View File
@@ -54,7 +54,7 @@ if ($mb['mb_leave_date'] && $mb['mb_leave_date'] <= date("Ymd", G5_SERVER_TIME))
// 메일인증 설정이 되어 있다면
if ( is_use_email_certify() && !preg_match("/[1-9]/", $mb['mb_email_certify'])) {
$ckey = md5($mb['mb_ip'].$mb['mb_datetime']);
$ckey = function_exists('get_email_cert_key') ? get_email_cert_key($mb_id, $mb['mb_datetime']) : md5($mb['mb_ip'].$mb['mb_datetime']);
confirm("{$mb['mb_email']} 메일로 메일인증을 받으셔야 로그인 가능합니다. 다른 메일주소로 변경하여 인증하시려면 취소를 클릭하시기 바랍니다.", G5_URL, G5_BBS_URL.'/register_email.php?mb_id='.$mb_id.'&ckey='.$ckey);
}
@@ -90,10 +90,33 @@ if($config['cf_use_point']) {
if (isset($auto_login) && $auto_login) {
// 3.27
// 자동로그인 ---------------------------
// 쿠키 한달간 저장
$key = md5($_SERVER['SERVER_ADDR'] . $_SERVER['SERVER_SOFTWARE'] . $_SERVER['HTTP_USER_AGENT'] . $mb['mb_password']);
// 예측 불가능한 랜덤 토큰을 생성하여 g5_member_auto_login 테이블에 저장.
// 다중 디바이스 지원: 디바이스마다 별도의 토큰 행이 생성되며 서로 독립적으로 동작.
$auto_token = get_random_token_string(32); // 64자 hex - 쿠키에는 원본
$auto_token_hash = hash('sha256', $auto_token); // 64자 hex - DB에는 해시
$auto_ua = isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 255) : '';
$auto_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
$auto_now = G5_TIME_YMDHIS;
$auto_expire = date('Y-m-d H:i:s', G5_SERVER_TIME + 86400 * 31);
// 만료된 토큰 정리 (해당 회원 한정)
sql_query(" delete from {$g5['member_auto_login_table']}
where mb_id = '{$mb['mb_id']}'
and al_expire < '{$auto_now}' ");
// 새 토큰 INSERT (해시값만 저장)
sql_query(" insert into {$g5['member_auto_login_table']}
set mb_id = '{$mb['mb_id']}',
al_token = '{$auto_token_hash}',
al_user_agent = '".addslashes($auto_ua)."',
al_ip = '".addslashes($auto_ip)."',
al_created = '{$auto_now}',
al_last_used = '{$auto_now}',
al_expire = '{$auto_expire}' ");
set_cookie('ck_mb_id', $mb['mb_id'], 86400 * 31);
set_cookie('ck_auto', $key, 86400 * 31);
set_cookie('ck_auto', $auto_token, 86400 * 31); // 쿠키에는 원본 토큰
// 자동로그인 end ---------------------------
} else {
set_cookie('ck_mb_id', '', 0);
+12
View File
@@ -11,6 +11,18 @@ session_unset(); // 모든 세션변수를 언레지스터 시켜줌
session_destroy(); // 세션해제함
// 자동로그인 해제 --------------------------------
// DB에 저장된 해당 디바이스의 자동 로그인 토큰만 무효화
// (다른 디바이스의 자동 로그인은 유지)
// DB에는 토큰의 SHA256 해시가 저장되어 있으므로 비교 시에도 해시값 사용
$logout_token = get_cookie('ck_auto');
$logout_mb_id = get_cookie('ck_mb_id');
if (isset($g5['member_auto_login_table']) && $logout_token && $logout_mb_id && preg_match('/^[a-f0-9]{64}$/', $logout_token)) {
$logout_mb_id = substr(preg_replace("/[^a-zA-Z0-9_]*/", "", $logout_mb_id), 0, 20);
$logout_token_hash = hash('sha256', $logout_token);
sql_query(" delete from {$g5['member_auto_login_table']}
where mb_id = '{$logout_mb_id}'
and al_token = '{$logout_token_hash}' ");
}
set_cookie('ck_mb_id', '', 0);
set_cookie('ck_auto', '', 0);
// 자동로그인 해제 end --------------------------------
+7 -1
View File
@@ -3,13 +3,19 @@ define('G5_CERT_IN_PROG', true);
include_once('./_common.php');
global $g5;
// 로그인 검증 (본인인증 갱신은 로그인 필수)
if (!$is_member) {
alert('로그인 후 이용해 주십시오.', G5_BBS_URL.'/login.php');
}
if (!($w == '' || $w == 'u')) {
alert('w 값이 제대로 넘어오지 않았습니다.');
}
$url = urldecode($url);
if($w == '') {
$mb_id = isset($_POST['mb_id']) ? trim($_POST['mb_id']) : '';
// 본인 계정만 수정 가능 (POST mb_id 무시, 세션 회원 ID 사용)
$mb_id = $member['mb_id'];
$mb_name = isset($_POST['mb_name']) ? trim($_POST['mb_name']) : '';
$mb_hp = isset($_POST['mb_hp']) ? trim($_POST['mb_hp']) : '';
} else
+2 -2
View File
@@ -13,8 +13,8 @@ if (!$member['mb_open'] && $is_admin != 'super' && $member['mb_id'] != $mb_id) {
}
$content = "";
$me_recv_mb_id = isset($_REQUEST['me_recv_mb_id']) ? clean_xss_tags($_REQUEST['me_recv_mb_id'], 1, 1) : '';
$me_id = isset($_REQUEST['me_id']) ? clean_xss_tags($_REQUEST['me_id'], 1, 1) : '';
$me_recv_mb_id = isset($_REQUEST['me_recv_mb_id']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['me_recv_mb_id']), 1, 1)) : '';
$me_id = isset($_REQUEST['me_id']) ? addslashes(clean_xss_tags(stripslashes($_REQUEST['me_id']), 1, 1)) : '';
// 탈퇴한 회원에게 쪽지 보낼 수 없음
if ($me_recv_mb_id)
+6 -2
View File
@@ -2,6 +2,8 @@
include_once('./_common.php');
include_once(G5_CAPTCHA_PATH.'/captcha.lib.php');
if (function_exists('check_request_origin')) check_request_origin(G5_BBS_URL);
if ($is_guest)
alert('회원만 이용하실 수 있습니다.');
@@ -18,7 +20,8 @@ $me_memo = isset($_POST['me_memo']) ? preg_replace("#[\\\]+$#", "", substr(trim(
run_event('memo_form_update_before', $recv_list);
for ($i=0; $i<count($recv_list); $i++) {
$recv_list_cnt = count($recv_list);
for ($i=0; $i<$recv_list_cnt; $i++) {
$recv_list_id = substr(preg_replace("/[^a-zA-Z0-9_]*/", "", $recv_list[$i]), 0, 20);
@@ -63,7 +66,8 @@ if (!$is_admin) {
}
}
for ($i=0; $i<count($member_list['id']); $i++) {
$member_list_cnt = count($member_list['id']);
for ($i=0; $i<$member_list_cnt; $i++) {
$tmp_row = sql_fetch(" select max(me_id) as max_me_id from {$g5['memo_table']} ");
$me_id = $tmp_row['max_me_id'] + 1;
+9 -5
View File
@@ -61,7 +61,7 @@ while ($row = sql_fetch_array($result))
$nick = cut_str($member['mb_nick'], $config['cf_cut_name']);
if (!$row2['wr_is_comment'] && $config['cf_use_copy_log']) {
if(strstr($row2['wr_option'], 'html')) {
if(strpos($row2['wr_option'], 'html') !== false) {
$log_tag1 = '<div class="content_'.$sw.'">';
$log_tag2 = '</div>';
} else {
@@ -233,10 +233,12 @@ delete_cache_latest($bo_table);
if ($sw == 'move')
{
for ($i=0; $i<count($save); $i++)
$save_cnt = count($save);
for ($i=0; $i<$save_cnt; $i++)
{
if( isset($save[$i]['bf_file']) && $save[$i]['bf_file'] ){
for ($k=0; $k<count($save[$i]['bf_file']); $k++) {
$bf_file_cnt = count($save[$i]['bf_file']);
for ($k=0; $k<$bf_file_cnt; $k++) {
$del_file = run_replace('delete_file_path', clean_relative_paths($save[$i]['bf_file'][$k]), $save[$i]);
if ( is_file($del_file) && file_exists($del_file) ){
@@ -248,7 +250,8 @@ if ($sw == 'move')
}
}
for ($k=0; $k<count($save[$i]['wr_contents']); $k++){
$wr_contents_cnt = count($save[$i]['wr_contents']);
for ($k=0; $k<$wr_contents_cnt; $k++){
delete_editor_thumbnail($save[$i]['wr_contents'][$k]);
}
@@ -262,7 +265,8 @@ if ($sw == 'move')
$sql = " select bo_notice from {$g5['board_table']} where bo_table = '{$bo_table}' ";
$row = sql_fetch($sql);
$arr_notice = explode(',', $row['bo_notice']);
for ($i=0; $i<count($arr_notice); $i++) {
$arr_notice_cnt = count($arr_notice);
for ($i=0; $i<$arr_notice_cnt; $i++) {
$move_id = (int)$arr_notice[$i];
// 게시판에 wr_id 가 있다면 이동한게 아니므로 bo_notice 에 다시 넣음
$row2 = sql_fetch(" select count(*) as cnt from $write_table where wr_id = '{$move_id}' ");
+2 -1
View File
@@ -97,7 +97,8 @@ for($i=0;$i<$count_chk_bn_id;$i++)
$notice_array = explode(",", trim($board['bo_notice']));
$bo_notice = "";
$lf = '';
for ($k=0; $k<count($notice_array); $k++) {
$notice_array_cnt = count($notice_array);
for ($k=0; $k<$notice_array_cnt; $k++) {
if ((int)$write['wr_id'] != (int)$notice_array[$k])
$bo_notice .= $lf.$notice_array[$k];
+18 -11
View File
@@ -16,25 +16,32 @@ $email = get_email_address(trim($_POST['mb_email']));
if (!$email)
alert_close('메일주소 오류입니다.');
// OWASP 권장: 이메일 존재 여부와 무관하게 동일한 응답 메시지 사용
// (이메일 열거 공격 방지)
$generic_message = $email.' 메일로 회원아이디와 비밀번호를 인증할 수 있는 메일이 발송 되었습니다.\\n\\n메일을 확인하여 주십시오.';
$sql = " select count(*) as cnt from {$g5['member_table']} where mb_email = '$email' ";
$row = sql_fetch($sql);
if ($row['cnt'] > 1)
alert('동일한 메일주소가 2개 이상 존재합니다.\\n\\n관리자에게 문의하여 주십시오.');
if ($row['cnt'] > 1) {
// 시스템 데이터 무결성 이슈 - 운영자 로그에만 기록하고 사용자에겐 일반 메시지
@error_log("[g5 password_lost2] Duplicate email detected: $email (count={$row['cnt']})");
alert_close($generic_message);
}
$sql = " select mb_no, mb_id, mb_name, mb_nick, mb_email, mb_datetime, mb_leave_date from {$g5['member_table']} where mb_email = '$email' ";
$mb = sql_fetch($sql);
if (empty($mb['mb_id']) || $mb['mb_leave_date']) {
alert('존재하지 않는 회원입니다.');
} elseif (is_admin($mb['mb_id'])) {
alert('관리자 아이디는 접근 불가합니다.');
// 회원이 없거나 탈퇴했거나 관리자이면 메일 발송 없이 동일한 메시지로 응답
if (empty($mb['mb_id']) || $mb['mb_leave_date'] || is_admin($mb['mb_id'])) {
alert_close($generic_message);
}
// 임시비밀번호 발급
$change_password = rand(100000, 999999);
// 임시비밀번호 발급 (CSPRNG 사용)
$change_password = get_random_token_string(5); // 10자리 hex (0-9, a-f)
$mb_lost_certify = get_encrypt_string($change_password);
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용
$mb_nonce = md5(pack('V*', rand(), rand(), rand(), rand()));
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
$mb_nonce = get_random_token_string(16);
// 임시비밀번호와 난수를 mb_lost_certify 필드에 저장
$sql = " update {$g5['member_table']} set mb_lost_certify = '$mb_nonce $mb_lost_certify' where mb_id = '{$mb['mb_id']}' ";
@@ -74,4 +81,4 @@ mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $mb['mb_email'
run_event('password_lost2_after', $mb, $mb_nonce, $mb_lost_certify);
alert_close($email.' 메일로 회원아이디와 비밀번호를 인증할 수 있는 메일이 발송 되었습니다.\\n\\n메일을 확인하여 주십시오.');
alert_close($generic_message);
+14 -2
View File
@@ -2,16 +2,28 @@
include_once('./_common.php');
include_once(G5_LIB_PATH.'/mailer.lib.php');
if (function_exists('check_request_origin')) check_request_origin(G5_BBS_URL);
if ($w == '')
{
$po_id = isset($_POST['po_id']) ? (int) $_POST['po_id'] : '';
$pc_name = isset($_POST['pc_name']) ? clean_xss_tags($_POST['pc_name'], 1, 1) : '';
$pc_idea = isset($_POST['pc_idea']) ? clean_xss_tags($_POST['pc_idea'], 1, 1) : '';
$po = sql_fetch(" select * from {$g5['poll_table']} where po_id = '{$po_id}' ");
if (!$po['po_id'])
alert('po_id 값이 제대로 넘어오지 않았습니다.');
// 기타의견 사용 여부 검사
if (!$po['po_etc'])
alert('기타의견이 비활성화되어 있습니다.');
// 접근 권한 검사
if ($member['mb_level'] < $po['po_level'])
alert('권한이 없습니다.');
$pc_name = isset($_POST['pc_name']) ? addslashes(clean_xss_tags(stripslashes($_POST['pc_name']), 1, 1)) : '';
$pc_name = preg_replace("#[\\\]+$#", "", $pc_name);
$pc_idea = isset($_POST['pc_idea']) ? addslashes(clean_xss_tags(stripslashes($_POST['pc_idea']), 1, 1)) : '';
$tmp_row = sql_fetch(" select max(pc_id) as max_pc_id from {$g5['poll_etc_table']} ");
$pc_id = $tmp_row['max_pc_id'] + 1;
+1
View File
@@ -3,6 +3,7 @@ include_once('./_common.php');
include_once(G5_CAPTCHA_PATH.'/captcha.lib.php');
$po_id = isset($_REQUEST['po_id']) ? (int) $_REQUEST['po_id'] : '';
$skin_dir = isset($skin_dir) ? clean_relative_paths(strip_tags($skin_dir)) : '';
$po = sql_fetch(" select * from {$g5['poll_table']} where po_id = '{$po_id}' ");
if (!$po['po_id'])
+21 -37
View File
@@ -1,6 +1,8 @@
<?php
include_once('./_common.php');
if (function_exists('check_request_origin')) check_request_origin(G5_BBS_URL);
$po_id = isset($_POST['po_id']) ? preg_replace('/[^0-9]/', '', $_POST['po_id']) : 0;
$po = sql_fetch(" select * from {$g5['poll_table']} where po_id = '$po_id' ");
@@ -14,50 +16,32 @@ $gb_poll = isset($_POST['gb_poll']) ? preg_replace('/[^0-9]/', '', $_POST['gb_po
if(!$gb_poll)
alert_close('항목을 선택하세요.');
$search_mb_id = false;
$search_ip = false;
if($is_member) {
// 투표했던 회원아이디들 중에서 찾아본다
$ids = explode(',', trim($po['mb_ids']));
for ($i=0; $i<count($ids); $i++) {
if ($member['mb_id'] == trim($ids[$i])) {
$search_mb_id = true;
break;
}
}
} else {
// 투표했던 ip들 중에서 찾아본다
$ips = explode(',', trim($po['po_ips']));
for ($i=0; $i<count($ips); $i++) {
if ($_SERVER['REMOTE_ADDR'] == trim($ips[$i])) {
$search_ip = true;
break;
}
}
}
$post_skin_dir = isset($_POST['skin_dir']) ? clean_xss_tags($_POST['skin_dir'], 1, 1) : '';
$result_url = G5_BBS_URL."/poll_result.php?po_id=$po_id&skin_dir={$post_skin_dir}";
// 없다면 선택한 투표항목을 1증가 시키고 ip, id를 저장
if (!($search_ip || $search_mb_id)) {
$po_ips = $po['po_ips'] . $_SERVER['REMOTE_ADDR'].",";
$mb_ids = $po['mb_ids'];
if ($is_member) { // 회원일 때는 id만 추가
$mb_ids .= $member['mb_id'].',';
$sql = " update {$g5['poll_table']} set po_cnt{$gb_poll} = po_cnt{$gb_poll} + 1, mb_ids = '$mb_ids' where po_id = '$po_id' ";
} else {
$sql = " update {$g5['poll_table']} set po_cnt{$gb_poll} = po_cnt{$gb_poll} + 1, po_ips = '$po_ips' where po_id = '$po_id' ";
}
sql_query($sql);
// 레이스 컨디션 방지: MyISAM은 트랜잭션을 지원하지 않으므로,
// WHERE 조건에 중복 검증(FIND_IN_SET)을 포함한 원자적 UPDATE로 처리한다.
// 기존 투표자 목록에 없을 때만 갱신되며, 동시 요청이 와도 DB 서버 레벨에서 직렬화된다.
if ($is_member) {
$sql = " update {$g5['poll_table']}
set po_cnt{$gb_poll} = po_cnt{$gb_poll} + 1,
mb_ids = concat(ifnull(mb_ids, ''), '".$member['mb_id'].",')
where po_id = '$po_id'
and find_in_set('".$member['mb_id']."', ifnull(mb_ids, '')) = 0 ";
} else {
$sql = " update {$g5['poll_table']}
set po_cnt{$gb_poll} = po_cnt{$gb_poll} + 1,
po_ips = concat(ifnull(po_ips, ''), '".$_SERVER['REMOTE_ADDR'].",')
where po_id = '$po_id'
and find_in_set('".$_SERVER['REMOTE_ADDR']."', ifnull(po_ips, '')) = 0 ";
}
sql_query($sql);
if (get_sql_affected_rows() <= 0) {
alert(addcslashes($po['po_subject'], '"\\/').'에 이미 참여하셨습니다.', $result_url);
}
if (!$search_mb_id)
insert_point($member['mb_id'], $po['po_point'], $po['po_id'] . '. ' . cut_str($po['po_subject'],20) . ' 투표 참여 ', '@poll', $po['po_id'], '투표');
insert_point($member['mb_id'], $po['po_point'], $po['po_id'] . '. ' . cut_str($po['po_subject'],20) . ' 투표 참여 ', '@poll', $po['po_id'], '투표');
//goto_url($g5['bbs_url'].'/poll_result.php?po_id='.$po_id.'&amp;skin_dir='.$skin_dir);
goto_url($result_url);
+2 -1
View File
@@ -29,7 +29,8 @@ if ($qaconfig['qa_category']) {
$category_option .= '>전체</a></li>';
$categories = explode('|', $qaconfig['qa_category']); // 구분자가 | 로 되어 있음
for ($i=0; $i<count($categories); $i++) {
$categories_cnt = count($categories);
for ($i=0; $i<$categories_cnt; $i++) {
$category = trim($categories[$i]);
if ($category=='') continue;
$category_msg = '';
+2 -1
View File
@@ -54,7 +54,8 @@ if(is_file($skin_file)) {
$category_option = '';
if(trim($qaconfig['qa_category'])) {
$category = explode('|', $qaconfig['qa_category']);
for($i=0; $i<count($category); $i++) {
$category_cnt = count($category);
for($i=0; $i<$category_cnt; $i++) {
$category_option .= option_selected($category[$i], $write['qa_category']);
}
} else {
+1 -1
View File
@@ -219,7 +219,7 @@ for ($i=1; $i<=$upload_count; $i++) {
$upload[$i]['filesize'] = $filesize;
// 아래의 문자열이 들어간 파일은 -x 를 붙여서 웹경로를 알더라도 실행을 하지 못하도록 함
$filename = preg_replace("/\.(php|pht|phtm|htm|cgi|pl|exe|jsp|asp|inc|phar)/i", "$0-x", $filename);
$filename = preg_replace("/\.(php|pht|phtm|htm|shtml|shtm|cgi|pl|exe|jsp|asp|inc|phar|svg|svgz)/i", "$0-x", $filename);
shuffle($chars_array);
$shuffle = implode('', $chars_array);
+4 -3
View File
@@ -5,7 +5,7 @@ include_once(G5_CAPTCHA_PATH.'/captcha.lib.php');
$g5['title'] = '메일인증 메일주소 변경';
include_once('./_head.php');
$mb_id = isset($_GET['mb_id']) ? substr(clean_xss_tags($_GET['mb_id']), 0, 20) : '';
$mb_id = isset($_GET['mb_id']) ? substr(addslashes(clean_xss_tags(stripslashes($_GET['mb_id']), 1, 1)), 0, 20) : '';
$sql = " select mb_email, mb_datetime, mb_ip, mb_email_certify, mb_id from {$g5['member_table']} where mb_id = '{$mb_id}' ";
$mb = sql_fetch($sql);
@@ -18,9 +18,9 @@ if (substr($mb['mb_email_certify'],0,1)!=0) {
}
$ckey = isset($_GET['ckey']) ? trim($_GET['ckey']) : '';
$key = md5($mb['mb_ip'].$mb['mb_datetime']);
$key = get_email_cert_key($mb_id, $mb['mb_datetime']);
if(!$ckey || $ckey != $key)
if(!$ckey || $ckey !== $key)
alert('올바른 방법으로 이용해 주십시오.', G5_URL);
?>
@@ -28,6 +28,7 @@ if(!$ckey || $ckey != $key)
<form method="post" name="fregister_email" action="<?php echo G5_HTTPS_BBS_URL.'/register_email_update.php'; ?>" onsubmit="return fregister_email_submit(this);">
<input type="hidden" name="mb_id" value="<?php echo $mb_id; ?>">
<input type="hidden" name="ckey" value="<?php echo $key; ?>">
<div class="tbl_frm01 tbl_frm rg_em">
<table>
+22 -7
View File
@@ -3,13 +3,28 @@ include_once('./_common.php');
include_once(G5_CAPTCHA_PATH.'/captcha.lib.php');
include_once(G5_LIB_PATH.'/mailer.lib.php');
$mb_id = isset($_POST['mb_id']) ? substr(clean_xss_tags($_POST['mb_id']), 0, 20) : '';
// 로그인 검증
if (!$is_member) {
alert('로그인 후 이용해 주십시오.', G5_BBS_URL.'/login.php');
}
// 본인 계정만 수정 가능 (POST mb_id 무시, 세션 회원 ID 사용)
$mb_id = $member['mb_id'];
$mb_email = isset($_POST['mb_email']) ? get_email_address(trim($_POST['mb_email'])) : '';
if(!$mb_id || !$mb_email)
alert('올바른 방법으로 이용해 주십시오.', G5_URL);
$sql = " select mb_name from {$g5['member_table']} where mb_id = '{$mb_id}' and substring(mb_email_certify, 1, 1) = '0' ";
// ckey 검증 (register_email.php 폼에서 전달받은 인증키)
$ckey = isset($_POST['ckey']) ? trim($_POST['ckey']) : '';
$key = get_email_cert_key($mb_id, $member['mb_datetime']);
if (!$ckey || $ckey !== $key) {
alert('올바른 방법으로 이용해 주십시오.', G5_URL);
}
$esc_mb_id = sql_real_escape_string($mb_id);
$sql = " select mb_name from {$g5['member_table']} where mb_id = '{$esc_mb_id}' and substring(mb_email_certify, 1, 1) = '0' ";
$mb = sql_fetch($sql);
if (!$mb) {
alert("이미 메일인증 하신 회원입니다.", G5_URL);
@@ -19,7 +34,7 @@ if (!chk_captcha()) {
alert('자동등록방지 숫자가 틀렸습니다.');
}
$sql = " select count(*) as cnt from {$g5['member_table']} where mb_id <> '{$mb_id}' and mb_email = '$mb_email' ";
$sql = " select count(*) as cnt from {$g5['member_table']} where mb_id <> '{$esc_mb_id}' and mb_email = '$mb_email' ";
$row = sql_fetch($sql);
if ($row['cnt']) {
alert("{$mb_email} 메일은 이미 존재하는 메일주소 입니다.\\n\\n다른 메일주소를 입력해 주십시오.");
@@ -30,10 +45,10 @@ $subject = '['.$config['cf_title'].'] 인증확인 메일입니다.';
$mb_name = $mb['mb_name'];
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용
$mb_md5 = md5(pack('V*', rand(), rand(), rand(), rand()));
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
$mb_md5 = get_random_token_string(16);
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
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.'&amp;mb_md5='.$mb_md5;
@@ -44,7 +59,7 @@ ob_end_clean();
mailer($config['cf_admin_email_name'], $config['cf_admin_email'], $mb_email, $subject, $content, 1);
$sql = " update {$g5['member_table']} set mb_email = '$mb_email' where mb_id = '$mb_id' ";
$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);
+1 -1
View File
@@ -6,7 +6,7 @@ include_once(G5_LIB_PATH.'/register.lib.php');
run_event('register_form_before');
// 불법접근을 막도록 토큰생성
$token = md5(uniqid(rand(), true));
$token = get_random_token_string(16);
set_session("ss_token", $token);
set_session("ss_cert_no", "");
set_session("ss_cert_hash", "");
+28 -14
View File
@@ -63,20 +63,34 @@ $mb_7 = isset($_POST['mb_7']) ? trim($_POST['mb_7'])
$mb_8 = isset($_POST['mb_8']) ? trim($_POST['mb_8']) : "";
$mb_9 = isset($_POST['mb_9']) ? trim($_POST['mb_9']) : "";
$mb_10 = isset($_POST['mb_10']) ? trim($_POST['mb_10']) : "";
$mb_name = clean_xss_tags($mb_name, 1, 1);
$mb_name = addslashes(clean_xss_tags(stripslashes($mb_name), 1, 1));
$mb_email = get_email_address($mb_email);
$mb_homepage = clean_xss_tags($mb_homepage, 1, 1);
$mb_tel = clean_xss_tags($mb_tel, 1, 1);
$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);
$mb_zip2 = preg_replace('/[^0-9]/', '', $mb_zip2);
$mb_addr1 = clean_xss_tags($mb_addr1, 1, 1);
$mb_addr2 = clean_xss_tags($mb_addr2, 1, 1);
$mb_addr3 = clean_xss_tags($mb_addr3, 1, 1);
$mb_addr1 = addslashes(clean_xss_tags(stripslashes($mb_addr1), 1, 1));
$mb_addr2 = addslashes(clean_xss_tags(stripslashes($mb_addr2), 1, 1));
$mb_addr3 = addslashes(clean_xss_tags(stripslashes($mb_addr3), 1, 1));
$mb_addr_jibeon = preg_match("/^(N|R)$/", $mb_addr_jibeon) ? $mb_addr_jibeon : '';
$mb_marketing_agree = isset($_POST['mb_marketing_agree']) ? trim($_POST['mb_marketing_agree']) : "0";
$mb_thirdparty_agree = isset($_POST['mb_thirdparty_agree']) ? trim($_POST['mb_thirdparty_agree']) : "0";
// _default 변수 명시적 초기화 (폼에 해당 섹션이 없으면 null)
$mb_marketing_agree_default = isset($_POST['mb_marketing_agree_default']) ? trim($_POST['mb_marketing_agree_default']) : null;
$mb_mailling_default = isset($_POST['mb_mailling_default']) ? trim($_POST['mb_mailling_default']) : null;
$mb_sms_default = isset($_POST['mb_sms_default']) ? trim($_POST['mb_sms_default']) : null;
$mb_thirdparty_agree_default = isset($_POST['mb_thirdparty_agree_default']) ? trim($_POST['mb_thirdparty_agree_default']) : null;
// 폼에 수신설정 섹션이 없는 경우 기존 DB 값 유지 (회원정보 수정 시)
if ($w == 'u') {
if ($mb_marketing_agree_default === null) $mb_marketing_agree = $member['mb_marketing_agree'];
if ($mb_mailling_default === null) $mb_mailling = $member['mb_mailling'];
if ($mb_sms_default === null) $mb_sms = $member['mb_sms'];
if ($mb_thirdparty_agree_default === null) $mb_thirdparty_agree = $member['mb_thirdparty_agree'];
}
run_event('register_form_update_before', $mb_id, $w);
if ($w == '' || $w == 'u') {
@@ -305,9 +319,9 @@ if ($w == '') {
if ($config['cf_email_mb_member']) {
$subject = '['.$config['cf_title'].'] 회원가입을 축하드립니다.';
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
if ($config['cf_use_email_certify']) {
$mb_md5 = md5(pack('V*', rand(), rand(), rand(), rand()));
$mb_md5 = get_random_token_string(16);
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.'&amp;mb_md5='.$mb_md5;
}
@@ -386,28 +400,28 @@ if ($w == '') {
// 마케팅 목적의 개인정보 수집 및 이용
$sql_marketing_date = "";
if ($mb_marketing_agree_default !== $mb_marketing_agree) {
if ($mb_marketing_agree_default !== null && $mb_marketing_agree_default !== $mb_marketing_agree) {
$sql_marketing_date .= " , mb_marketing_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "마케팅 목적의 개인정보 수집 및 이용(" . ($mb_marketing_agree == 1 ? "동의" : "철회") . ")";
}
// 광고성 이메일 수신
$sql_mailling_date = "";
if ($mb_mailling_default !== $mb_mailling) {
if ($mb_mailling_default !== null && $mb_mailling_default !== $mb_mailling) {
$sql_mailling_date .= " , mb_mailling_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 이메일 수신(" . ($mb_mailling == 1 ? "동의" : "철회") . ")";
}
// 광고성 SMS/카카오톡 수신
$sql_sms_date = "";
if ($mb_sms_default !== $mb_sms) {
if ($mb_sms_default !== null && $mb_sms_default !== $mb_sms) {
$sql_sms_date .= " , mb_sms_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "광고성 SMS/카카오톡 수신(" . ($mb_sms == 1 ? "동의" : "철회") . ")";
}
// 개인정보 제3자 제공
$sql_thirdparty_date = "";
if ($mb_thirdparty_agree_default !== $mb_thirdparty_agree) {
if ($mb_thirdparty_agree_default !== null && $mb_thirdparty_agree_default !== $mb_thirdparty_agree) {
$sql_thirdparty_date .= " , mb_thirdparty_date = '".G5_TIME_YMDHIS."' ";
$agree_items[] = "개인정보 제3자 제공(" . ($mb_thirdparty_agree == 1 ? "동의" : "철회") . ")";
}
@@ -590,8 +604,8 @@ if( $config['cf_member_img_size'] && $config['cf_member_img_width'] && $config['
if ($config['cf_use_email_certify'] && $old_email != $mb_email) {
$subject = '['.$config['cf_title'].'] 인증확인 메일입니다.';
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용
$mb_md5 = md5(pack('V*', rand(), rand(), rand(), rand()));
// 어떠한 회원정보도 포함되지 않은 일회용 난수를 생성하여 인증에 사용 (CSPRNG 사용)
$mb_md5 = get_random_token_string(16);
sql_query(" update {$g5['member_table']} set mb_email_certify2 = '$mb_md5' where mb_id = '$mb_id' ");
+1 -1
View File
@@ -60,7 +60,7 @@ $result = sql_query($sql);
for ($i=0; $row=sql_fetch_array($result); $i++) {
$file = '';
if (strstr($row['wr_option'], 'html'))
if (strpos($row['wr_option'], 'html') !== false)
$html = 1;
else
$html = 0;
+11 -7
View File
@@ -72,7 +72,9 @@ if ($stx) {
$field = explode('||', trim($sfl));
$str = '(';
for ($i=0; $i<count($s); $i++) {
$s_cnt = count($s);
$field_cnt = count($field);
for ($i=0; $i<$s_cnt; $i++) {
if (trim($s[$i]) == '') continue;
$search_str = $s[$i];
@@ -85,7 +87,7 @@ if ($stx) {
$op2 = '';
// 필드의 수만큼 다중 필드 검색 가능 (필드1+필드2...)
for ($k=0; $k<count($field); $k++) {
for ($k=0; $k<$field_cnt; $k++) {
$str .= $op2;
switch ($field[$k]) {
case 'mb_id' :
@@ -119,7 +121,8 @@ if ($stx) {
$time1 = get_microtime();
$total_count = 0;
for ($i=0; $i<count($g5_search['tables']); $i++) {
$tables_cnt = count($g5_search['tables']);
for ($i=0; $i<$tables_cnt; $i++) {
$tmp_write_table = $g5['write_prefix'] . $g5_search['tables'][$i];
$sql = " select wr_id from {$tmp_write_table} where {$sql_search} ";
@@ -148,7 +151,8 @@ if ($stx) {
if ($page < 1) { $page = 1; } // 페이지가 없으면 첫 페이지 (1 페이지)
$from_record = ($page - 1) * $rows; // 시작 열을 구함
for ($i=0; $i<count($search_table); $i++) {
$search_table_cnt = count($search_table);
for ($i=0; $i<$search_table_cnt; $i++) {
if ($from_record < $search_table_count[$i]) {
$table_index = $i;
$from_record = $from_record - ($i > 0 ? $search_table_count[$i-1] : 0);
@@ -183,11 +187,11 @@ if ($stx) {
}
// 비밀글은 검색 불가
if (strstr($row['wr_option'].(isset($row2['wr_option']) ? $row2['wr_option'] : ''), 'secret'))
if (strpos($row['wr_option'].(isset($row2['wr_option']) ? $row2['wr_option'] : ''), 'secret') !== false)
$row['wr_content'] = '[비밀글 입니다.]';
$subject = get_text($row['wr_subject']);
if (strstr($sfl, 'wr_subject'))
if (strpos($sfl, 'wr_subject') !== false)
$subject = search_font($stx, $subject);
if ($read_level[$idx] <= $member['mb_level'])
@@ -199,7 +203,7 @@ if ($stx) {
$content = str_replace('&nbsp;', '', $content);
$content = cut_str($content, 300, "…");
if (strstr($sfl, 'wr_content'))
if (strpos($sfl, 'wr_content') !== false)
$content = search_font($stx, $content);
}
else
+4 -4
View File
@@ -111,17 +111,17 @@ if ($is_member) {
$view = get_view($write, $board, $board_skin_path);
if (strstr($sfl, 'subject'))
if (strpos($sfl, 'subject') !== false)
$view['subject'] = search_font($stx, $view['subject']);
$html = 0;
if (strstr($view['wr_option'], 'html1'))
if (strpos($view['wr_option'], 'html1') !== false)
$html = 1;
else if (strstr($view['wr_option'], 'html2'))
else if (strpos($view['wr_option'], 'html2') !== false)
$html = 2;
$view['content'] = conv_content($view['wr_content'], $html);
if (strstr($sfl, 'content'))
if (strpos($sfl, 'content') !== false)
$view['content'] = search_font($stx, $view['content']);
//$view['rich_content'] = preg_replace("/{이미지\:([0-9]+)[:]?([^}]*)}/ie", "view_image(\$view, '\\1', '\\2')", $view['content']);
+1 -1
View File
@@ -40,7 +40,7 @@ for ($i=0; $row=sql_fetch_array($result); $i++)
//$list[$i]['content'] = eregi_replace("[^ \n<>]{130}", "\\0\n", $row['wr_content']);
$list[$i]['content'] = $list[$i]['content1']= '비밀글 입니다.';
if (!strstr($row['wr_option'], 'secret') ||
if (strpos($row['wr_option'], 'secret') === false ||
$is_admin ||
($write['mb_id']===$member['mb_id'] && $member['mb_id']) ||
($row['mb_id']===$member['mb_id'] && $member['mb_id'])) {
+3 -1
View File
@@ -346,8 +346,10 @@ if ($w == '') {
}
for($i=0;$i<$file_count;$i++){
if(! isset($file[$i])) {
if (!isset($file[$i])) {
$file[$i] = array('file'=>null, 'source'=>null, 'size'=>null, 'bf_content' => null);
} else {
$file[$i]['bf_content'] = isset($file[$i]['bf_content']) ? htmlspecialchars($file[$i]['bf_content'], ENT_QUOTES, 'UTF-8', false) : null;
}
}
+8 -6
View File
@@ -16,8 +16,9 @@ if (substr_count($wr_content, "&#") > 50) {
}
$w = isset($_POST['w']) ? clean_xss_tags($_POST['w']) : '';
$wr_name = isset($_POST['wr_name']) ? clean_xss_tags(trim($_POST['wr_name'])) : '';
$wr_secret = isset($_POST['wr_secret']) ? clean_xss_tags($_POST['wr_secret']) : '';
$wr_name = isset($_POST['wr_name']) ? addslashes(clean_xss_tags(stripslashes(trim($_POST['wr_name'])))) : '';
$wr_name = preg_replace("#[\\\]+$#", "", $wr_name);
$wr_secret = isset($_POST['wr_secret']) ? addslashes(clean_xss_tags(stripslashes($_POST['wr_secret']))) : '';
$wr_email = $wr_subject = '';
$reply_array = array();
@@ -31,9 +32,9 @@ $wr_7 = isset($_POST['wr_7']) ? $_POST['wr_7'] : '';
$wr_8 = isset($_POST['wr_8']) ? $_POST['wr_8'] : '';
$wr_9 = isset($_POST['wr_9']) ? $_POST['wr_9'] : '';
$wr_10 = isset($_POST['wr_10']) ? $_POST['wr_10'] : '';
$wr_facebook_user = isset($_POST['wr_facebook_user']) ? clean_xss_tags($_POST['wr_facebook_user'], 1, 1) : '';
$wr_twitter_user = isset($_POST['wr_twitter_user']) ? clean_xss_tags($_POST['wr_twitter_user'], 1, 1) : '';
$wr_homepage = isset($_POST['wr_homepage']) ? clean_xss_tags($_POST['wr_homepage'], 1, 1) : '';
$wr_facebook_user = isset($_POST['wr_facebook_user']) ? addslashes(clean_xss_tags(stripslashes($_POST['wr_facebook_user']), 1, 1)) : '';
$wr_twitter_user = isset($_POST['wr_twitter_user']) ? addslashes(clean_xss_tags(stripslashes($_POST['wr_twitter_user']), 1, 1)) : '';
$wr_homepage = isset($_POST['wr_homepage']) ? addslashes(clean_xss_tags(stripslashes($_POST['wr_homepage']), 1, 1)) : '';
if (!empty($_POST['wr_email']))
$wr_email = get_email_address(trim($_POST['wr_email']));
@@ -255,7 +256,8 @@ if ($w == 'c') // 댓글 입력
// 중복된 메일 주소는 제거
$unique_email = array_unique($array_email);
$unique_email = array_values($unique_email);
for ($i=0; $i<count($unique_email); $i++) {
$unique_email_cnt = count($unique_email);
for ($i=0; $i<$unique_email_cnt; $i++) {
mailer($wr_name, $wr_email, $unique_email[$i], $subject, $content, 1);
}
}
+21 -14
View File
@@ -241,12 +241,13 @@ if ($w == '' || $w == 'r') {
} else {
$mb_id = '';
// 비회원의 경우 이름이 누락되는 경우가 있음
$wr_name = clean_xss_tags(trim($_POST['wr_name']));
$wr_name = addslashes(clean_xss_tags(stripslashes(trim($_POST['wr_name']))));
$wr_name = preg_replace("#[\\\]+$#", "", $wr_name);
if (!$wr_name)
alert('이름은 필히 입력하셔야 합니다.');
$wr_password = get_encrypt_string($wr_password);
$wr_email = get_email_address(trim($_POST['wr_email']));
$wr_homepage = clean_xss_tags($wr_homepage);
$wr_homepage = addslashes(clean_xss_tags(stripslashes($wr_homepage)));
}
if ($w == 'r') {
@@ -362,16 +363,17 @@ if ($w == '' || $w == 'r') {
$wr_homepage = addslashes(clean_xss_tags($member['mb_homepage']));
} else {
$mb_id = $wr['mb_id'];
if(isset($_POST['wr_name']) && $_POST['wr_name'])
$wr_name = clean_xss_tags(trim($_POST['wr_name']));
else
if(isset($_POST['wr_name']) && $_POST['wr_name']) {
$wr_name = addslashes(clean_xss_tags(stripslashes(trim($_POST['wr_name']))));
$wr_name = preg_replace("#[\\\]+$#", "", $wr_name);
} else
$wr_name = addslashes(clean_xss_tags($wr['wr_name']));
if(isset($_POST['wr_email']) && $_POST['wr_email'])
$wr_email = get_email_address(trim($_POST['wr_email']));
else
$wr_email = addslashes($wr['wr_email']);
if(isset($_POST['wr_homepage']) && $_POST['wr_homepage'])
$wr_homepage = addslashes(clean_xss_tags($_POST['wr_homepage']));
$wr_homepage = addslashes(clean_xss_tags(stripslashes($_POST['wr_homepage'])));
else
$wr_homepage = addslashes(clean_xss_tags($wr['wr_homepage']));
}
@@ -379,7 +381,8 @@ if ($w == '' || $w == 'r') {
$mb_id = "";
// 비회원의 경우 이름이 누락되는 경우가 있음
if (!trim($wr_name)) alert("이름은 필히 입력하셔야 합니다.");
$wr_name = clean_xss_tags(trim($_POST['wr_name']));
$wr_name = addslashes(clean_xss_tags(stripslashes(trim($_POST['wr_name']))));
$wr_name = preg_replace("#[\\\]+$#", "", $wr_name);
$wr_email = get_email_address(trim($_POST['wr_email']));
}
@@ -430,7 +433,8 @@ if ($w == '' || $w == 'r') {
}
} else {
$bo_notice = '';
for ($i=0; $i<count($notice_array); $i++)
$notice_array_cnt = count($notice_array);
for ($i=0; $i<$notice_array_cnt; $i++)
if ((int)$wr_id != (int)$notice_array[$i])
$bo_notice .= $notice_array[$i] . ',';
$bo_notice = trim($bo_notice);
@@ -480,7 +484,8 @@ $file_upload_msg = '';
$upload = array();
if(isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) {
for ($i=0; $i<count($_FILES['bf_file']['name']); $i++) {
$bf_file_cnt = count($_FILES['bf_file']['name']);
for ($i=0; $i<$bf_file_cnt; $i++) {
$upload[$i]['file'] = '';
$upload[$i]['source'] = '';
$upload[$i]['filesize'] = 0;
@@ -572,7 +577,7 @@ if(isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) {
$upload[$i]['filesize'] = $filesize;
// 아래의 문자열이 들어간 파일은 -x 를 붙여서 웹경로를 알더라도 실행을 하지 못하도록 함
$filename = preg_replace("/\.(php|pht|phtm|htm|cgi|pl|exe|jsp|asp|inc|phar)/i", "$0-x", $filename);
$filename = preg_replace("/\.(php|pht|phtm|htm|shtml|shtm|cgi|pl|exe|jsp|asp|inc|phar|svg|svgz)/i", "$0-x", $filename);
shuffle($chars_array);
$shuffle = implode('', $chars_array);
@@ -595,7 +600,8 @@ if(isset($_FILES['bf_file']['name']) && is_array($_FILES['bf_file']['name'])) {
} // end if
// 나중에 테이블에 저장하는 이유는 $wr_id 값을 저장해야 하기 때문입니다.
for ($i=0; $i<count($upload); $i++)
$upload_cnt = count($upload);
for ($i=0; $i<$upload_cnt; $i++)
{
$upload[$i]['source'] = sql_real_escape_string($upload[$i]['source']);
$bf_content[$i] = isset($bf_content[$i]) ? sql_real_escape_string($bf_content[$i]) : '';
@@ -704,9 +710,9 @@ if (!($w == 'u' || $w == 'cu') && $config['cf_email_use'] && $board['bo_use_emai
$wr_subject = get_text(stripslashes($wr_subject));
$tmp_html = 0;
if (strstr($html, 'html1'))
if (strpos($html, 'html1') !== false)
$tmp_html = 1;
else if (strstr($html, 'html2'))
else if (strpos($html, 'html2') !== false)
$tmp_html = 2;
$wr_content = conv_content(conv_unescape_nl(stripslashes($wr_content)), $tmp_html);
@@ -751,7 +757,8 @@ if (!($w == 'u' || $w == 'cu') && $config['cf_email_use'] && $board['bo_use_emai
$unique_email = array_unique($array_email);
$unique_email = run_replace('write_update_mail_list', array_values($unique_email), $board, $wr_id);
for ($i=0; $i<count($unique_email); $i++) {
$unique_email_cnt = count($unique_email);
for ($i=0; $i<$unique_email_cnt; $i++) {
mailer($wr_name, $wr_email, $unique_email[$i], $subject, $content, 1);
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class G5CloudflareRequestHandler {
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/12',
'104.16.0.0/13',
'104.24.0.0/14',
'172.64.0.0/13',
'131.0.72.0/22',
+31 -11
View File
@@ -165,6 +165,11 @@ if (file_exists($dbconfig_file)) {
sql_set_charset(G5_DB_CHARSET, $connect_db);
if(defined('G5_MYSQL_SET_MODE') && G5_MYSQL_SET_MODE) sql_query("SET SESSION sql_mode = ''");
if (defined('G5_TIMEZONE')) sql_query(" set time_zone = '".G5_TIMEZONE."'");
// 자동 로그인 토큰 테이블 - 기존 dbconfig.php에 정의되지 않은 경우 fallback
if (!isset($g5['member_auto_login_table'])) {
$g5['member_auto_login_table'] = G5_TABLE_PREFIX.'member_auto_login';
}
} else {
?>
@@ -556,17 +561,30 @@ if (isset($_SESSION['ss_mb_id']) && $_SESSION['ss_mb_id']) { // 로그인중이
$tmp_mb_id = substr(preg_replace("/[^a-zA-Z0-9_]*/", "", $tmp_mb_id), 0, 20);
// 최고관리자는 자동로그인 금지
if (strtolower($tmp_mb_id) !== strtolower($config['cf_admin'])) {
$sql = " select mb_password, mb_intercept_date, mb_leave_date, mb_email_certify, mb_datetime from {$g5['member_table']} where mb_id = '{$tmp_mb_id}' ";
$row = sql_fetch($sql);
if($row['mb_password']){
$key = md5($_SERVER['SERVER_ADDR'] . $_SERVER['SERVER_SOFTWARE'] . $_SERVER['HTTP_USER_AGENT'] . $row['mb_password']);
// 쿠키에 저장된 키와 같다면
$tmp_key = get_cookie('ck_auto');
if ($tmp_key === $key && $tmp_key) {
// 쿠키 값을 g5_member_auto_login 테이블의 토큰과 비교
// 다중 디바이스 지원을 위해 회원당 여러 토큰을 별도 테이블에 저장
$tmp_key = get_cookie('ck_auto');
// 토큰 형식 검증 (64자 hex) - 빈 값/잘못된 형식 차단
if ($tmp_key && preg_match('/^[a-f0-9]{64}$/', $tmp_key)) {
$tmp_key_hash = hash('sha256', $tmp_key); // 쿠키 값을 해시해서 DB와 비교
$sql = " select al.al_id, al.al_expire,
m.mb_intercept_date, m.mb_leave_date, m.mb_email_certify, m.mb_datetime
from {$g5['member_auto_login_table']} al
inner join {$g5['member_table']} m on m.mb_id = al.mb_id
where al.mb_id = '{$tmp_mb_id}'
and al.al_token = '{$tmp_key_hash}'
and al.al_expire > '".G5_TIME_YMDHIS."' ";
$row = sql_fetch($sql);
if (isset($row['al_id']) && $row['al_id']) {
// 차단, 탈퇴가 아니고 메일인증이 사용이면서 인증을 받았다면
if ($row['mb_intercept_date'] == '' &&
$row['mb_leave_date'] == '' &&
(!$config['cf_use_email_certify'] || preg_match('/[1-9]/', $row['mb_email_certify'])) ) {
// 마지막 사용 시간 갱신
sql_query(" update {$g5['member_auto_login_table']}
set al_last_used = '".G5_TIME_YMDHIS."'
where al_id = '{$row['al_id']}' ");
// 세션에 회원아이디를 저장하여 로그인으로 간주
set_session('ss_mb_id', $tmp_mb_id);
if(function_exists('update_auth_session_token')) update_auth_session_token($row['mb_datetime']);
@@ -576,9 +594,9 @@ if (isset($_SESSION['ss_mb_id']) && $_SESSION['ss_mb_id']) { // 로그인중이
exit;
}
}
// $row 배열변수 해제
unset($row);
}
// $row 배열변수 해제
unset($row);
}
}
// 자동로그인 end ---------------------------------------
@@ -591,7 +609,8 @@ if (!(isset($member['mb_id']) && $config['cf_admin'] === $member['mb_id'])) {
if ($cf_possible_ip) {
$is_possible_ip = false;
$pattern = explode("\n", $cf_possible_ip);
for ($i=0; $i<count($pattern); $i++) {
$pattern_cnt = count($pattern);
for ($i=0; $i<$pattern_cnt; $i++) {
$pattern[$i] = trim($pattern[$i]);
if (empty($pattern[$i]))
continue;
@@ -610,7 +629,8 @@ if (!(isset($member['mb_id']) && $config['cf_admin'] === $member['mb_id'])) {
// 접근차단 IP
$is_intercept_ip = false;
$pattern = explode("\n", trim($config['cf_intercept_ip']));
for ($i=0; $i<count($pattern); $i++) {
$pattern_cnt = count($pattern);
for ($i=0; $i<$pattern_cnt; $i++) {
$pattern[$i] = trim($pattern[$i]);
if (empty($pattern[$i]))
continue;
+2 -2
View File
@@ -173,7 +173,7 @@ define('G5_DIR_PERMISSION', 0755); // 디렉토리 생성시 퍼미션
define('G5_FILE_PERMISSION', 0644); // 파일 생성시 퍼미션
// 모바일 인지 결정 $_SERVER['HTTP_USER_AGENT']
define('G5_MOBILE_AGENT', 'phone|samsung|lgtel|mobile|[^A]skt|nokia|blackberry|BB10|android|sony');
define('G5_MOBILE_AGENT', 'phone|samsung.*mobile|lgtel|mobile|[^A]skt|nokia|blackberry|BB10|android|sony');
// SMTP
// lib/mailer.lib.php 에서 사용
@@ -235,4 +235,4 @@ define('G5_VISIT_BROWSCAP_USE', false);
define('G5_IP_DISPLAY', '\\1.♡.\\3.\\4');
// KAKAO 우편번호 서비스 CDN
define('G5_POSTCODE_JS', '<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js" async></script>');
define('G5_POSTCODE_JS', '<script src="//t1.kakaocdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js" async></script>');
+5
View File
@@ -0,0 +1,5 @@
<FilesMatch "\.(htaccess|htpasswd|[Pp][Hh][Pp]|[Pp][Hh][Tt]|[Ss]?[Pp]?[Hh][Tt][Mm][Ll]?|[Ii][Nn][Cc]|[Cc][Gg][Ii]|[Pp][Ll]|[Pp][Hh][Aa][Rr]|[Ss][Vv][Gg][Zz]?)">
Order allow,deny
Deny from all
</FilesMatch>
RedirectMatch 403 /session/.*
+1 -1
View File
@@ -28,7 +28,7 @@ $g5['lo_location'] = addslashes($g5['title']);
if (!$g5['lo_location'])
$g5['lo_location'] = addslashes(clean_xss_tags($_SERVER['REQUEST_URI']));
$g5['lo_url'] = addslashes(clean_xss_tags($_SERVER['REQUEST_URI']));
if (strstr($g5['lo_url'], '/'.G5_ADMIN_DIR.'/') || $is_admin == 'super') $g5['lo_url'] = '';
if (strpos($g5['lo_url'], '/'.G5_ADMIN_DIR.'/') !== false || $is_admin == 'super') $g5['lo_url'] = '';
/*
// 만료된 페이지로 사용하시는 경우
+22
View File
@@ -554,6 +554,28 @@ CREATE TABLE IF NOT EXISTS `g5_member` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `g5_member_auto_login`
-- KVE-2026-0610: 자동 로그인 토큰 저장 (다중 디바이스 지원)
--
DROP TABLE IF EXISTS `g5_member_auto_login`;
CREATE TABLE IF NOT EXISTS `g5_member_auto_login` (
`al_id` int(11) NOT NULL auto_increment,
`mb_id` varchar(20) NOT NULL default '',
`al_token` varchar(64) NOT NULL default '',
`al_user_agent` varchar(255) NOT NULL default '',
`al_ip` varchar(45) NOT NULL default '',
`al_created` datetime DEFAULT NULL,
`al_last_used` datetime DEFAULT NULL,
`al_expire` datetime DEFAULT NULL,
PRIMARY KEY (`al_id`),
UNIQUE KEY `al_token` (`al_token`),
KEY `mb_id` (`mb_id`),
KEY `al_expire` (`al_expire`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `g5_memo`
--
+1
View File
@@ -169,6 +169,7 @@ CREATE TABLE IF NOT EXISTS `g5_shop_coupon_log` (
`cp_price` int(11) NOT NULL DEFAULT '0',
`cl_datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`cl_id`),
UNIQUE KEY `idx_coupon_use` (`cp_id`, `mb_id`),
KEY `mb_id` (`mb_id`),
KEY `od_id` (`od_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+3 -2
View File
@@ -158,7 +158,7 @@ if ($g5_install || $is_install === false) {
cf_theme = 'basic',
cf_admin = '$admin_id',
cf_admin_email = '$admin_email',
cf_admin_email_name = '".G5_VERSION."',
cf_admin_email_name = '".G5_VERSION.'_'.substr(base_convert(mt_rand(), 10, 36), 0, 6)."',
cf_use_point = '1',
cf_use_copy_log = '1',
cf_login_point = '100',
@@ -596,6 +596,7 @@ fwrite($f, "\$g5['board_new_table'] = G5_TABLE_PREFIX.'board_new'; // 게시판
fwrite($f, "\$g5['login_table'] = G5_TABLE_PREFIX.'login'; // 로그인 테이블 (접속자수)\n");
fwrite($f, "\$g5['mail_table'] = G5_TABLE_PREFIX.'mail'; // 회원메일 테이블\n");
fwrite($f, "\$g5['member_table'] = G5_TABLE_PREFIX.'member'; // 회원 테이블\n");
fwrite($f, "\$g5['member_auto_login_table'] = G5_TABLE_PREFIX.'member_auto_login'; // 자동 로그인 토큰 테이블\n");
fwrite($f, "\$g5['memo_table'] = G5_TABLE_PREFIX.'memo'; // 메모 테이블\n");
fwrite($f, "\$g5['poll_table'] = G5_TABLE_PREFIX.'poll'; // 투표 테이블\n");
fwrite($f, "\$g5['poll_etc_table'] = G5_TABLE_PREFIX.'poll_etc'; // 투표 기타의견 테이블\n");
@@ -659,7 +660,7 @@ fclose($f);
// data 디렉토리 및 하위 디렉토리에서는 .htaccess .htpasswd .php .phtml .html .htm .inc .cgi .pl .phar 파일을 실행할수 없게함.
$f = fopen($data_path.'/.htaccess', 'w');
$str = <<<EOD
<FilesMatch "\.(htaccess|htpasswd|[Pp][Hh][Pp]|[Pp][Hh][Tt]|[Pp]?[Hh][Tt][Mm][Ll]?|[Ii][Nn][Cc]|[Cc][Gg][Ii]|[Pp][Ll]|[Pp][Hh][Aa][Rr])">
<FilesMatch "\.(htaccess|htpasswd|[Pp][Hh][Pp]|[Pp][Hh][Tt]|[Ss]?[Pp]?[Hh][Tt][Mm][Ll]?|[Ii][Nn][Cc]|[Cc][Gg][Ii]|[Pp][Ll]|[Pp][Hh][Aa][Rr]|[Ss][Vv][Gg][Zz]?)">
Order allow,deny
Deny from all
</FilesMatch>
+5 -5
View File
@@ -423,7 +423,7 @@ var win_zip = function(frm_name, frm_zip, frm_addr1, frm_addr2, frm_addr3, frm_j
element_wrap = document.createElement("div");
element_wrap.setAttribute("id", daum_pape_id);
element_wrap.style.cssText = 'display:none;border:1px solid;left:0;width:100%;height:300px;margin:5px 0;position:relative;-webkit-overflow-scrolling:touch;';
element_wrap.innerHTML = '<img src="//t1.daumcdn.net/postcode/resource/images/close.png" id="btnFoldWrap" style="cursor:pointer;position:absolute;right:0px;top:-21px;z-index:1" class="close_daum_juso" alt="접기 버튼">';
element_wrap.innerHTML = '<img src="//t1.kakaocdn.net/postcode/resource/images/close.png" id="btnFoldWrap" style="cursor:pointer;position:absolute;right:0px;top:-21px;z-index:1" class="close_daum_juso" alt="접기 버튼">';
jQuery('form[name="'+frm_name+'"]').find('input[name="'+frm_addr1+'"]').before(element_wrap);
jQuery("#"+daum_pape_id).off("click", ".close_daum_juso").on("click", ".close_daum_juso", function(e){
e.preventDefault();
@@ -432,7 +432,7 @@ var win_zip = function(frm_name, frm_zip, frm_addr1, frm_addr2, frm_addr3, frm_j
});
}
new daum.Postcode({
new kakao.Postcode({
oncomplete: function(data) {
complete_fn(data);
// iframe을 넣은 element를 안보이게 한다.
@@ -454,7 +454,7 @@ var win_zip = function(frm_name, frm_zip, frm_addr1, frm_addr2, frm_addr3, frm_j
element_wrap.style.display = 'block';
break;
case 2 : //새창으로 띄우기
new daum.Postcode({
new kakao.Postcode({
oncomplete: function(data) {
complete_fn(data);
}
@@ -467,7 +467,7 @@ var win_zip = function(frm_name, frm_zip, frm_addr1, frm_addr2, frm_addr3, frm_j
element_layer = document.createElement("div");
element_layer.setAttribute("id", rayer_id);
element_layer.style.cssText = 'display:none;border:5px solid;position:fixed;width:300px;height:460px;left:50%;margin-left:-155px;top:50%;margin-top:-235px;overflow:hidden;-webkit-overflow-scrolling:touch;z-index:10000';
element_layer.innerHTML = '<img src="//i1.daumcdn.net/localimg/localimages/07/postcode/320/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" class="close_daum_juso" alt="닫기 버튼">';
element_layer.innerHTML = '<img src="//t1.kakaocdn.net/localimg/localimages/07/postcode/320/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" class="close_daum_juso" alt="닫기 버튼">';
document.body.appendChild(element_layer);
jQuery("#"+rayer_id).off("click", ".close_daum_juso").on("click", ".close_daum_juso", function(e){
e.preventDefault();
@@ -476,7 +476,7 @@ var win_zip = function(frm_name, frm_zip, frm_addr1, frm_addr2, frm_addr3, frm_j
});
}
new daum.Postcode({
new kakao.Postcode({
oncomplete: function(data) {
complete_fn(data);
// iframe을 넣은 element를 안보이게 한다.
+1 -1
View File
@@ -25,7 +25,7 @@ class G5_URI {
$ext = end($script_names);
/* if extension is found in URL, eliminate it */
if(strstr($uri,".")) {
if(strpos($uri,".") !== false) {
$arr_uri = explode('.', $uri);
/* get last part */
$last = end($arr_uri);
+560 -127
View File
@@ -257,22 +257,42 @@ function url_auto_link($str)
{
global $g5;
global $config;
if ($replace_str = run_replace('url_auto_link_before', '', $str)) {
return $replace_str;
}
$ori_str = $str;
// 140326 유창화님 제안코드로 수정
// http://sir.kr/pg_lecture/461
// http://sir.kr/pg_lecture/463
// 각 패턴의 존재 여부를 빠르게 체크하여 preg_replace 호출을 최소화한다.
// 일반 텍스트 게시글(URL/이메일이 없는 대부분의 경우)에서는 fast path로 즉시 반환.
// strpos(x5) ~= 1~2ms vs preg_replace(x3) ~= 20~50ms 이므로 핫패스에서 큰 이득.
$has_url_scheme = strpos($str, '://') !== false;
$has_www = stripos($str, 'www.') !== false;
$has_at = strpos($str, '@') !== false;
$has_amp = strpos($str, '&') !== false;
$has_quote = strpos($str, "'") !== false;
if (!$has_url_scheme && !$has_www && !$has_at && !$has_amp && !$has_quote) {
return run_replace('url_auto_link', $str, $ori_str);
}
$attr_nofollow = (function_exists('check_html_link_nofollow') && check_html_link_nofollow('url_auto_link')) ? ' rel="nofollow"' : '';
$str = str_replace(array("&lt;", "&gt;", "&amp;", "&quot;", "&nbsp;", "&#039;"), array("\t_lt_\t", "\t_gt_\t", "&", "\"", "\t_nbsp_\t", "'"), $str);
//$str = preg_replace("`(?:(?:(?:href|src)\s*=\s*(?:\"|'|)){0})((http|https|ftp|telnet|news|mms)://[^\"'\s()]+)`", "<A HREF=\"\\1\" TARGET='{$config['cf_link_target']}'>\\1</A>", $str);
$str = preg_replace("/([^(href=\"?'?)|(src=\"?'?)]|\(|^)((http|https|ftp|telnet|news|mms):\/\/[a-zA-Z0-9\.-]+\.[가-힣\xA1-\xFEa-zA-Z0-9\.:&#!=_\?\/~\+%@;\-\|\,\(\)]+)/i", "\\1<A HREF=\"\\2\" TARGET=\"{$config['cf_link_target']}\" $attr_nofollow>\\2</A>", $str);
$str = preg_replace("/(^|[\"'\s(])(www\.[^\"'\s()]+)/i", "\\1<A HREF=\"http://\\2\" TARGET=\"{$config['cf_link_target']}\" $attr_nofollow>\\2</A>", $str);
$str = preg_replace("/[0-9a-z_-]+@[a-z0-9._-]{4,}/i", "<a href=\"mailto:\\0\" $attr_nofollow>\\0</a>", $str);
if ($has_url_scheme) {
$str = preg_replace("/([^(href=\"?'?)|(src=\"?'?)]|\(|^)((http|https|ftp|telnet|news|mms):\/\/[a-zA-Z0-9\.-]+\.[가-힣\xA1-\xFEa-zA-Z0-9\.:&#!=_\?\/~\+%@;\-\|\,\(\)]+)/i", "\\1<A HREF=\"\\2\" TARGET=\"{$config['cf_link_target']}\" $attr_nofollow>\\2</A>", $str);
}
if ($has_www) {
$str = preg_replace("/(^|[\"'\s(])(www\.[^\"'\s()]+)/i", "\\1<A HREF=\"http://\\2\" TARGET=\"{$config['cf_link_target']}\" $attr_nofollow>\\2</A>", $str);
}
if ($has_at) {
$str = preg_replace("/[0-9a-z_-]+@[a-z0-9._-]{4,}/i", "<a href=\"mailto:\\0\" $attr_nofollow>\\0</a>", $str);
}
$str = str_replace(array("\t_nbsp_\t", "\t_lt_\t", "\t_gt_\t", "'"), array("&nbsp;", "&lt;", "&gt;", "&#039;"), $str);
/*
@@ -442,9 +462,9 @@ function get_list($write_row, $board, $skin_url, $subject_len=40)
if ($board['bo_use_list_content'])
{
$html = 0;
if (strstr($list['wr_option'], 'html1'))
if (strpos($list['wr_option'], 'html1') !== false)
$html = 1;
else if (strstr($list['wr_option'], 'html2'))
else if (strpos($list['wr_option'], 'html2') !== false)
$html = 2;
$list['content'] = conv_content($list['wr_content'], $html);
@@ -505,7 +525,7 @@ function get_list($write_row, $board, $skin_url, $subject_len=40)
$list['icon_hot'] = '<i class="fa fa-heart" aria-hidden="true"></i> ';
$list['icon_secret'] = '';
if (strstr($list['wr_option'], 'secret'))
if (strpos($list['wr_option'], 'secret') !== false)
$list['icon_secret'] = '<i class="fa fa-lock" aria-hidden="true"></i> ';
// 링크
@@ -552,7 +572,8 @@ function search_font($stx, $str)
// "/(검색1|검색2)/i" 와 같은 패턴을 만듬
$pattern = '';
$bar = '';
for ($m=0; $m<count($s); $m++) {
$s_cnt = count($s);
for ($m=0; $m<$s_cnt; $m++) {
if (trim($s[$m]) == '') continue;
// 태그는 포함하지 않아야 하는데 잘 안되는군. ㅡㅡa
//$pattern .= $bar . '([^<])(' . quotemeta($s[$m]) . ')';
@@ -645,20 +666,51 @@ function html_purifier($html)
{
global $is_admin, $write;
$f = file(G5_PLUGIN_PATH . '/htmlpurifier/safeiframe.txt');
$domains = array();
foreach ($f as $domain) {
// 첫행이 # 이면 주석 처리
if (!preg_match("/^#/", $domain)) {
$domain = trim($domain);
if ($domain) {
array_push($domains, $domain);
// 요청 단위로 HTMLPurifier 인스턴스를 캐싱한다.
// HTMLPurifier 인스턴스 하나는 약 10~15MB의 메모리를 사용하므로,
// 같은 요청 내에서 여러 번 호출되는 경우(예: 게시글 목록의 첨부파일 설명)
// 매번 새 인스턴스를 만드는 것은 메모리 사용량을 수십~수백 MB까지 증가시킨다.
//
// config에 영향을 주는 변수는 "글쓴이가 관리자인지" 하나뿐이므로
// 캐시 키를 admin/normal 두 가지로 분리하여 최대 2개의 인스턴스만 유지한다.
//
// 캐싱이 동작 변경을 일으키는 경우(html_purifier_config / html_purifier_safeiframes
// hook이 $html 내용에 따라 동적으로 config를 바꾸는 플러그인을 사용하는 경우)
// G5_HTMLPURIFIER_NO_CACHE 상수를 정의하여 비활성화할 수 있다.
static $purifier_cache = array();
static $domains_cache = null;
$cache_enabled = !(defined('G5_HTMLPURIFIER_NO_CACHE') && G5_HTMLPURIFIER_NO_CACHE);
$is_admin_post = (isset($write['mb_id']) && $write['mb_id'] && is_admin($write['mb_id']));
$cache_key = $is_admin_post ? 'admin' : 'normal';
// 캐시 적중 시 인스턴스 재사용
if ($cache_enabled && isset($purifier_cache[$cache_key])) {
$purifier = $purifier_cache[$cache_key];
return run_replace('html_purifier_result', $purifier->purify($html), $purifier, $html);
}
// safeiframe.txt 파싱 결과를 요청 단위로 캐싱 (파일 I/O + 정규식 비용 절감)
if ($domains_cache === null) {
$domains_cache = array();
$f = @file(G5_PLUGIN_PATH . '/htmlpurifier/safeiframe.txt');
if ($f !== false) {
foreach ($f as $domain) {
// 첫행이 # 이면 주석 처리
if (preg_match("/^#/", $domain)) continue;
$domain = trim($domain);
if ($domain !== '') {
$domains_cache[] = $domain;
}
}
unset($f);
}
}
$domains = $domains_cache;
// 글쓴이가 관리자인 경우에만 현재 사이트 도메인을 허용
if (isset($write['mb_id']) && $write['mb_id'] && is_admin($write['mb_id'])) {
array_push($domains, $_SERVER['HTTP_HOST'] . '/');
if ($is_admin_post) {
$domains[] = $_SERVER['HTTP_HOST'] . '/';
}
$safeiframe = implode('|', run_replace('html_purifier_safeiframes', $domains, $html));
@@ -682,7 +734,10 @@ function html_purifier($html)
/*
* HTMLPurifier 설정을 변경할 수 있는 Event hook
* 리스너에서는 첫번째 인자($config)로 `HTMLPurifier_Config` 객체를 받을 수 있다
* 리스너에서는 첫번째 인자($config)로 `HTMLPurifier_Config` 객체를 받을 수 있다.
* NB: 인스턴스 캐싱이 활성화된 경우(기본값) 이 hook은 캐시 미스 시점에만 실행됨
* (요청당 최대 2회). $html 내용에 따라 동적으로 config를 변경해야 한다면
* G5_HTMLPURIFIER_NO_CACHE 상수를 정의해 캐시를 비활성화하라.
*/
run_event('html_purifier_config', $config, array(
'html' => $html,
@@ -697,6 +752,11 @@ function html_purifier($html)
$purifier = new HTMLPurifier($config);
// 인스턴스 캐싱
if ($cache_enabled) {
$purifier_cache[$cache_key] = $purifier;
}
return run_replace('html_purifier_result', $purifier->purify($html), $purifier, $html);
}
@@ -740,7 +800,9 @@ function get_sql_search($search_ca_name, $search_field, $search_text, $search_op
$not_comment = $tmp[1];
$str .= "(";
for ($i=0; $i<count($s); $i++) {
$s_cnt = count($s);
$field_cnt = count($field);
for ($i=0; $i<$s_cnt; $i++) {
// 검색어
$search_str = trim($s[$i]);
if ($search_str == "") continue;
@@ -752,7 +814,7 @@ function get_sql_search($search_ca_name, $search_field, $search_text, $search_op
$str .= "(";
$op2 = "";
for ($k=0; $k<count($field); $k++) { // 필드의 수만큼 다중 필드 검색 가능 (필드1+필드2...)
for ($k=0; $k<$field_cnt; $k++) { // 필드의 수만큼 다중 필드 검색 가능 (필드1+필드2...)
// SQL Injection 방지
// 필드값에 a-z A-Z 0-9 _ , | 이외의 값이 있다면 검색필드를 wr_subject 로 설정한다.
@@ -997,7 +1059,8 @@ function get_category_option($bo_table='', $ca_name='')
$categories = explode("|", $board['bo_category_list'].($is_admin?"|공지":"")); // 구분자가 | 로 되어 있음
$str = "";
for ($i=0; $i<count($categories); $i++) {
$categories_cnt = count($categories);
for ($i=0; $i<$categories_cnt; $i++) {
$category = trim($categories[$i]);
if (!$category) continue;
@@ -1083,16 +1146,24 @@ function insert_point($mb_id, $point, $content='', $rel_table='', $rel_id='', $r
$mb_point = get_point_sum($mb_id);
// 이미 등록된 내역이라면 건너뜀
// 레이스 컨디션 방지: MyISAM은 트랜잭션을 지원하지 않으므로 MySQL named lock(GET_LOCK)으로
// 검증/INSERT 구간을 직렬화한다. rel 키가 없는 일반 포인트 지급은 락 대상이 아니다.
$point_lock_name = '';
if ($rel_table || $rel_id || $rel_action)
{
$point_lock_name = 'g5pt_' . md5($mb_id.'|'.$rel_table.'|'.$rel_id.'|'.$rel_action);
sql_fetch(" select get_lock('$point_lock_name', 5) as got_lock ");
$sql = " select count(*) as cnt from {$g5['point_table']}
where mb_id = '$mb_id'
and po_rel_table = '$rel_table'
and po_rel_id = '$rel_id'
and po_rel_action = '$rel_action' ";
$row = sql_fetch($sql);
if ($row['cnt'])
if ($row['cnt']) {
sql_query(" select release_lock('$point_lock_name') ");
return -1;
}
}
// 포인트 건별 생성
@@ -1134,6 +1205,11 @@ function insert_point($mb_id, $point, $content='', $rel_table='', $rel_id='', $r
$sql = " update {$g5['member_table']} set mb_point = '$po_mb_point' where mb_id = '$mb_id' ";
sql_query($sql);
// named lock 해제
if ($point_lock_name) {
sql_query(" select release_lock('$point_lock_name') ");
}
return 1;
}
@@ -1141,43 +1217,71 @@ function insert_point($mb_id, $point, $content='', $rel_table='', $rel_id='', $r
function insert_use_point($mb_id, $point, $po_id='')
{
global $g5, $config;
if ($replace_insert = run_replace('insert_use_point_before', '', $mb_id, $point, $po_id)) {
return $replace_insert;
}
// 레이스 컨디션 방지: 매 단계마다 가장 오래된 행 1개를 SELECT한 후
// WHERE 절에 사전 검증 조건을 포함한 원자적 UPDATE로 차감한다.
// affected_rows로 성공/실패를 판별하고 실패 시 재시도하므로 락 없이 무결성 보장.
// (MyISAM/InnoDB 모두 호환, FOR UPDATE 불필요)
if($config['cf_point_term'])
$sql_order = " order by po_expire_date asc, po_id asc ";
else
$sql_order = " order by po_id asc ";
$point1 = abs($point);
$sql = " select po_id, po_point, po_use_point
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_id <> '$po_id'
and po_expired = '0'
and po_point > po_use_point
$sql_order ";
$result = sql_query($sql);
for($i=0; $row=sql_fetch_array($result); $i++) {
$point2 = $row['po_point'];
$point3 = $row['po_use_point'];
$remaining = abs($point);
$max_iter = 1000; // 무한루프 안전장치 (정상 케이스는 차감 행 수만큼만 반복)
if(($point2 - $point3) > $point1) {
while ($remaining > 0 && $max_iter-- > 0) {
// 사용 가능한 가장 오래된 행 1개 조회
$sql = " select po_id, po_point, po_use_point
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_id <> '$po_id'
and po_expired = '0'
and po_point > po_use_point
$sql_order
limit 1 ";
$row = sql_fetch($sql);
if (!$row || empty($row['po_id'])) {
break; // 더 이상 사용 가능한 포인트 없음
}
$available = $row['po_point'] - $row['po_use_point'];
if ($available > $remaining) {
// 이 행만으로 충분 (부분 차감)
// WHERE 절에서 "현재도 충분한 잔여가 있는가"를 원자적으로 재검증
$sql = " update {$g5['point_table']}
set po_use_point = po_use_point + '$point1'
where po_id = '{$row['po_id']}' ";
set po_use_point = po_use_point + '$remaining'
where po_id = '{$row['po_id']}'
and po_expired = '0'
and (po_point - po_use_point) > '$remaining' ";
sql_query($sql);
break;
if (get_sql_affected_rows() > 0) {
$remaining = 0;
break;
}
// 0건 = 다른 프로세스가 이 행을 먼저 차감 → 다시 SELECT로 재시도
} else {
$point4 = $point2 - $point3;
// 이 행을 완전 소진 + expired 마킹
// WHERE 절에서 SELECT 시점의 po_use_point 값과 일치할 때만 UPDATE
$consume = $available;
$sql = " update {$g5['point_table']}
set po_use_point = po_use_point + '$point4',
set po_use_point = po_use_point + '$consume',
po_expired = '100'
where po_id = '{$row['po_id']}' ";
where po_id = '{$row['po_id']}'
and po_use_point = '{$row['po_use_point']}'
and po_expired = '0' ";
sql_query($sql);
$point1 -= $point4;
if (get_sql_affected_rows() > 0) {
$remaining -= $consume;
}
// 0건 = 다른 프로세스가 이 행을 먼저 변경 → 다시 SELECT로 재시도
}
}
}
@@ -1187,41 +1291,68 @@ function delete_use_point($mb_id, $point)
{
global $g5, $config;
// 레이스 컨디션 방지: 매 단계마다 사용된 가장 최근 행 1개를 조회한 후
// WHERE 절에 SELECT 시점의 상태를 검증하는 원자적 UPDATE로 환원한다.
if($config['cf_point_term'])
$sql_order = " order by po_expire_date desc, po_id desc ";
else
$sql_order = " order by po_id desc ";
$point1 = abs($point);
$sql = " select po_id, po_use_point, po_expired, po_expire_date
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_expired <> '1'
and po_use_point > 0
$sql_order ";
$result = sql_query($sql);
for($i=0; $row=sql_fetch_array($result); $i++) {
$point2 = $row['po_use_point'];
$remaining = abs($point);
$max_iter = 1000;
$po_expired = $row['po_expired'];
if($row['po_expired'] == 100 && ($row['po_expire_date'] == '9999-12-31' || $row['po_expire_date'] >= G5_TIME_YMD))
$po_expired = 0;
if($point2 > $point1) {
$sql = " update {$g5['point_table']}
set po_use_point = po_use_point - '$point1',
po_expired = '$po_expired'
where po_id = '{$row['po_id']}' ";
sql_query($sql);
while ($remaining > 0 && $max_iter-- > 0) {
// 사용분이 있는 가장 최근 행 1개 조회
$sql = " select po_id, po_use_point, po_expired, po_expire_date
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_expired <> '1'
and po_use_point > 0
$sql_order
limit 1 ";
$row = sql_fetch($sql);
if (!$row || empty($row['po_id'])) {
break;
}
$row_used = $row['po_use_point'];
// 만료 마커(100) 복구 여부 결정
$po_expired_new = $row['po_expired'];
if ($row['po_expired'] == 100 && ($row['po_expire_date'] == '9999-12-31' || $row['po_expire_date'] >= G5_TIME_YMD))
$po_expired_new = 0;
if ($row_used > $remaining) {
// 부분 환원
// WHERE에 현재 po_use_point가 차감 가능한 수준인지와 expired 상태를 함께 검증
$sql = " update {$g5['point_table']}
set po_use_point = po_use_point - '$remaining',
po_expired = '$po_expired_new'
where po_id = '{$row['po_id']}'
and po_use_point >= '$remaining'
and po_expired = '{$row['po_expired']}' ";
sql_query($sql);
if (get_sql_affected_rows() > 0) {
$remaining = 0;
break;
}
// 0건 = 다른 프로세스가 이 행을 먼저 변경 → 재시도
} else {
// 사용분 전체 환원
$consume = $row_used;
$sql = " update {$g5['point_table']}
set po_use_point = '0',
po_expired = '$po_expired'
where po_id = '{$row['po_id']}' ";
po_expired = '$po_expired_new'
where po_id = '{$row['po_id']}'
and po_use_point = '{$row['po_use_point']}'
and po_expired = '{$row['po_expired']}' ";
sql_query($sql);
$point1 -= $point2;
if (get_sql_affected_rows() > 0) {
$remaining -= $consume;
}
// 0건 = 다른 프로세스가 이 행을 먼저 변경 → 재시도
}
}
}
@@ -1231,39 +1362,63 @@ function delete_expire_point($mb_id, $point)
{
global $g5, $config;
$point1 = abs($point);
$sql = " select po_id, po_use_point, po_expired, po_expire_date
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_expired = '1'
and po_point >= 0
and po_use_point > 0
order by po_expire_date desc, po_id desc ";
$result = sql_query($sql);
for($i=0; $row=sql_fetch_array($result); $i++) {
$point2 = $row['po_use_point'];
$po_expired = '0';
// 레이스 컨디션 방지: 매 단계마다 소멸된 가장 최근 행 1개를 조회한 후
// WHERE 절에 SELECT 시점의 상태를 검증하는 원자적 UPDATE로 환원한다.
$remaining = abs($point);
$max_iter = 1000;
while ($remaining > 0 && $max_iter-- > 0) {
// 소멸 처리된 가장 최근 행 1개 조회
$sql = " select po_id, po_use_point, po_expired, po_expire_date
from {$g5['point_table']}
where mb_id = '$mb_id'
and po_expired = '1'
and po_point >= 0
and po_use_point > 0
order by po_expire_date desc, po_id desc
limit 1 ";
$row = sql_fetch($sql);
if (!$row || empty($row['po_id'])) {
break;
}
$row_used = $row['po_use_point'];
$po_expire_date = '9999-12-31';
if($config['cf_point_term'] > 0)
if ($config['cf_point_term'] > 0)
$po_expire_date = date('Y-m-d', strtotime('+'.($config['cf_point_term'] - 1).' days', G5_SERVER_TIME));
if($point2 > $point1) {
if ($row_used > $remaining) {
// 부분 환원
$sql = " update {$g5['point_table']}
set po_use_point = po_use_point - '$point1',
po_expired = '$po_expired',
set po_use_point = po_use_point - '$remaining',
po_expired = '0',
po_expire_date = '$po_expire_date'
where po_id = '{$row['po_id']}' ";
sql_query($sql);
break;
} else {
$sql = " update {$g5['point_table']}
set po_use_point = '0',
po_expired = '$po_expired',
po_expire_date = '$po_expire_date'
where po_id = '{$row['po_id']}' ";
where po_id = '{$row['po_id']}'
and po_expired = '1'
and po_use_point >= '$remaining' ";
sql_query($sql);
$point1 -= $point2;
if (get_sql_affected_rows() > 0) {
$remaining = 0;
break;
}
// 0건 = 다른 프로세스가 이 행을 먼저 변경 → 재시도
} else {
// 사용분 전체 환원
$consume = $row_used;
$sql = " update {$g5['point_table']}
set po_use_point = '0',
po_expired = '0',
po_expire_date = '$po_expire_date'
where po_id = '{$row['po_id']}'
and po_expired = '1'
and po_use_point = '{$row['po_use_point']}' ";
sql_query($sql);
if (get_sql_affected_rows() > 0) {
$remaining -= $consume;
}
// 0건 = 다른 프로세스가 이 행을 먼저 변경 → 재시도
}
}
}
@@ -1612,18 +1767,27 @@ function view_link($view, $number, $attribute)
function cut_str($str, $len, $suffix="…")
{
$arr_str = preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
$str_len = count($arr_str);
if ($str_len >= $len) {
$slice_str = array_slice($arr_str, 0, $len);
$str = join("", $slice_str);
return $str . ($str_len > $len ? $suffix : '');
} else {
$str = join("", $arr_str);
// 빠른 경로: 바이트 길이가 이미 제한 이내라면 문자 수 계산 불필요
// UTF-8의 모든 문자는 최소 1바이트이므로 bytes <= len이면 chars <= len이 보장됨
if (strlen($str) <= $len) {
return $str;
}
// mbstring 확장 사용 (PHP 4.0.6+ 기본 내장): 문자 배열 생성 없이 길이/슬라이스 처리
if (function_exists('mb_strlen')) {
if (mb_strlen($str, 'UTF-8') > $len) {
return mb_substr($str, 0, $len, 'UTF-8') . $suffix;
}
return $str;
}
// mbstring 미설치 환경 폴백 (기존 동작 유지)
$arr_str = preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
$str_len = count($arr_str);
if ($str_len > $len) {
return join("", array_slice($arr_str, 0, $len)) . $suffix;
}
return join("", $arr_str);
}
@@ -1754,7 +1918,8 @@ function sql_query($sql, $error=G5_DISPLAY_SQL_ERROR, $link=null)
$sql = trim($sql);
// union의 사용을 허락하지 않습니다.
//$sql = preg_replace("#^select.*from.*union.*#i", "select 1", $sql);
$sql = preg_replace("#^select.*from.*[\s\(]+union[\s\)]+.*#i ", "select 1", $sql);
//$sql = preg_replace("#^select.*from.*[\s\(]+union[\s\)]+.*#i ", "select 1", $sql);
$sql = preg_replace("#^select.*from.*([\s\(]+union[\s\)]+|/\*.*union.*\*/).*#i", "select 1", $sql);
// `information_schema` DB로의 접근을 허락하지 않습니다.
$sql = preg_replace("#^select.*from.*where.*`?information_schema`?.*#i", "select 1", $sql);
@@ -1764,7 +1929,24 @@ function sql_query($sql, $error=G5_DISPLAY_SQL_ERROR, $link=null)
if(function_exists('mysqli_query') && G5_MYSQLI_USE) {
if ($error) {
$result = @mysqli_query($link, $sql) or die("<p>$sql<p>" . mysqli_errno($link) . " : " . mysqli_error($link) . "<p>error file : {$_SERVER['SCRIPT_NAME']}");
$result = @mysqli_query($link, $sql);
if (!$result) {
$err_no = mysqli_errno($link);
$err_msg = mysqli_error($link);
$err_file = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '';
// 서버 로그에는 항상 상세 기록 (운영자가 추적 가능하도록)
@error_log("[g5 sql_query] {$err_no}: {$err_msg} | SQL: {$sql} | file: {$err_file}");
if ($is_debug) {
// 디버그 모드: 상세 표시 (XSS 방지를 위해 escape)
die("<p>" . htmlspecialchars($sql, ENT_QUOTES, 'UTF-8')
. "<p>" . (int)$err_no . " : " . htmlspecialchars($err_msg, ENT_QUOTES, 'UTF-8')
. "<p>error file : " . htmlspecialchars($err_file, ENT_QUOTES, 'UTF-8'));
}
// 운영 환경: 일반 메시지로만 처리하여 DB 구조/경로 정보 노출 방지
die('데이터베이스 처리 중 오류가 발생했습니다.');
}
} else {
try {
$result = @mysqli_query($link, $sql);
@@ -1774,7 +1956,21 @@ function sql_query($sql, $error=G5_DISPLAY_SQL_ERROR, $link=null)
}
} else {
if ($error) {
$result = @mysql_query($sql, $link) or die("<p>$sql<p>" . mysql_errno() . " : " . mysql_error() . "<p>error file : {$_SERVER['SCRIPT_NAME']}");
$result = @mysql_query($sql, $link);
if (!$result) {
$err_no = mysql_errno();
$err_msg = mysql_error();
$err_file = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '';
@error_log("[g5 sql_query] {$err_no}: {$err_msg} | SQL: {$sql} | file: {$err_file}");
if ($is_debug) {
die("<p>" . htmlspecialchars($sql, ENT_QUOTES, 'UTF-8')
. "<p>" . (int)$err_no . " : " . htmlspecialchars($err_msg, ENT_QUOTES, 'UTF-8')
. "<p>error file : " . htmlspecialchars($err_file, ENT_QUOTES, 'UTF-8'));
}
die('데이터베이스 처리 중 오류가 발생했습니다.');
}
} else {
$result = @mysql_query($sql, $link);
}
@@ -2314,24 +2510,192 @@ function _callback_normalizeString($matches){
// 토큰 생성
function _token()
{
return md5(uniqid(rand(), true));
return get_random_token_string(16);
}
// 불법접근을 막도록 토큰을 생성하면서 토큰값을 리턴
// HMAC 기반 — 세션 내부 비밀값 사용, 타임스탬프 포함, 다중 탭 호환
// 토큰 형식: 타임스탬프.HMAC
function get_token()
{
$token = md5(uniqid(rand(), true));
set_session('ss_token', $token);
$key = _get_token_key();
$secret = _get_token_secret();
$time = time();
return $token;
$hmac = hash_hmac('sha256', $secret . '|csrf_token|' . $time, $key);
return $time . '.' . $hmac;
}
// POST로 넘어온 토큰과 세션에 저장된 토큰 비교
function check_token()
// POST로 넘어온 토큰의 HMAC 및 만료 시간 검증
// 기본 만료: 7200초(2시간)
function check_token($expire = 7200)
{
set_session('ss_token', '');
$token = isset($_POST['token']) ? $_POST['token'] : '';
$dot = strpos($token, '.');
if (!$token || $dot === false) {
alert('올바른 방법으로 이용해 주십시오.');
return false;
}
$time = (int)substr($token, 0, $dot);
$hmac = substr($token, $dot + 1);
// 만료 검증
if (abs(time() - $time) > $expire) {
alert('토큰이 만료되었습니다. 페이지를 새로고침 해주십시오.');
return false;
}
// HMAC 검증
$key = _get_token_key();
$secret = _get_token_secret();
$expected = hash_hmac('sha256', $secret . '|csrf_token|' . $time, $key);
if ($hmac !== $expected) {
alert('올바른 방법으로 이용해 주십시오.');
return false;
}
return true;
}
// 토큰 암호화 키 반환 (내부용)
function _get_token_key()
{
return (defined('G5_TOKEN_ENCRYPTION_KEY') && G5_TOKEN_ENCRYPTION_KEY)
? G5_TOKEN_ENCRYPTION_KEY
: (defined('G5_TABLE_PREFIX') ? G5_TABLE_PREFIX : '');
}
// 세션 내부 비밀값 반환 (내부용, 서버측에만 존재)
function _get_token_secret()
{
$secret = get_session('ss_token_secret');
if (!$secret) {
$secret = get_random_token_string(16);
set_session('ss_token_secret', $secret);
}
return $secret;
}
/**
* 세션 기반 단순 Rate Limit. 자동화 도구의 무차별 호출을 늦추기 위한 용도.
*
* 같은 키에 대해 $window 초 동안 $max 회까지만 허용. 초과 시 false 반환.
* 세션 쿠키를 무시하는 정교한 봇은 우회 가능하지만, 세션 생성 비용으로 attack rate가 떨어지고
* 가장 흔한 form-only enumeration 시나리오는 효과적으로 차단된다.
*
* @param string $key 레이트 리밋 식별자 (예: 'ajax_mb_id_check')
* @param int $max 윈도우당 최대 허용 횟수
* @param int $window 윈도우 크기 (초)
* @return bool true = 허용, false = 차단
*/
function check_rate_limit($key, $max = 30, $window = 60)
{
$session_key = 'ss_rate_' . $key;
$now = time();
$data = get_session($session_key);
if (!is_array($data) || !isset($data['reset']) || $data['reset'] < $now) {
$data = array('count' => 0, 'reset' => $now + $window);
}
$data['count']++;
set_session($session_key, $data);
return $data['count'] <= $max;
}
/**
* 이메일 미인증 회원의 메일주소 변경 페이지 접근 토큰을 생성한다.
*
* HMAC-SHA256 + 서버 시크릿(G5_TOKEN_ENCRYPTION_KEY)
* @param string $mb_id
* @param string $mb_datetime
* @return string 64자 hex
*/
function get_email_cert_key($mb_id, $mb_datetime)
{
$key = (defined('G5_TOKEN_ENCRYPTION_KEY') && G5_TOKEN_ENCRYPTION_KEY)
? G5_TOKEN_ENCRYPTION_KEY
: (defined('G5_TABLE_PREFIX') ? G5_TABLE_PREFIX : '');
$payload = 'email_cert|' . $mb_id . '|' . $mb_datetime;
return hash_hmac('sha256', $payload, $key);
}
/**
* CSRF 방지용 Origin/Referer 검증 (OWASP 권장 패턴).
*
* 브라우저가 자동으로 보내는 Origin 헤더를 우선 확인하고, 없으면 Referer를 사용해
* 요청이 현재 사이트에서 발생했는지 검증한다. 크로스 오리진에서 유입된 요청은
* 두 헤더 모두 공격자가 제어할 수 없으므로 위조가 불가능하다.
*
* 폼 파일(스킨) 수정 없이 처리 파일에만 호출을 추가하면 되므로 적용 범위가 넓고
* 커스텀 테마 호환성에 영향이 없다.
*
* 특수 환경(프록시 등)에서 헤더가 유실되는 경우 G5_DISABLE_ORIGIN_CHECK 상수로
* 비활성화할 수 있다.
*
* @param string $redirect_url 검증 실패 시 리다이렉트할 URL (기본: G5_URL)
* @return bool
*/
function check_request_origin($redirect_url = '')
{
// 환경에 따라 opt-out 가능
if (defined('G5_DISABLE_ORIGIN_CHECK') && G5_DISABLE_ORIGIN_CHECK) {
return true;
}
// GET 요청은 검증하지 않음 (CSRF는 상태 변경 요청에만 의미 있음)
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '';
if ($method === 'GET' || $method === 'HEAD') {
return true;
}
if (!$redirect_url) {
$redirect_url = defined('G5_URL') ? G5_URL : '/';
}
// Origin 우선, 없으면 Referer 사용
$origin = isset($_SERVER['HTTP_ORIGIN']) ? trim($_SERVER['HTTP_ORIGIN']) : '';
$referer = isset($_SERVER['HTTP_REFERER']) ? trim($_SERVER['HTTP_REFERER']) : '';
$source = $origin !== '' ? $origin : $referer;
if ($source === '') {
alert('올바른 경로로 접근해 주십시오.', $redirect_url);
}
$source_host = @parse_url($source, PHP_URL_HOST);
if (!$source_host) {
alert('올바른 경로로 접근해 주십시오.', $redirect_url);
}
// config.php의 G5_URL에서 호스트 추출 (HTTP_HOST보다 신뢰할 수 있음)
$server_host = defined('G5_URL') ? @parse_url(G5_URL, PHP_URL_HOST) : '';
if (!$server_host) {
$server_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$colon = strpos($server_host, ':');
if ($colon !== false) {
$server_host = substr($server_host, 0, $colon);
}
}
// www. 접두사 제거하여 비교 (www.example.com == example.com)
$source_host = preg_replace('/^www\./i', '', $source_host);
$server_host = preg_replace('/^www\./i', '', $server_host);
if (!$server_host || strcasecmp($source_host, $server_host) !== 0) {
alert('올바른 경로로 접근해 주십시오.', $redirect_url);
}
return true;
}
@@ -2654,7 +3018,8 @@ function delete_editor_thumbnail($contents)
if(!$matchs)
return;
for($i=0; $i<count($matchs[1]); $i++) {
$matchs_cnt = count($matchs[1]);
for($i=0; $i<$matchs_cnt; $i++) {
// 이미지 path 구함
$imgurl = @parse_url($matchs[1][$i]);
// $srcfile = dirname(G5_PATH).$imgurl['path'];
@@ -2879,7 +3244,11 @@ class html_process {
$tmp_sql = " select count(*) as cnt from {$g5['login_table']} where lo_ip = '{$_SERVER['REMOTE_ADDR']}' ";
$tmp_row = sql_fetch($tmp_sql);
$http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
if (!isset($member['mb_id'])) {
$member['mb_id'] = '';
}
if ($tmp_row['cnt']) {
$tmp_sql = " update {$g5['login_table']} set mb_id = '{$member['mb_id']}', lo_datetime = '".G5_TIME_YMDHIS."', lo_location = '{$g5['lo_location']}', lo_url = '{$g5['lo_url']}' where lo_ip = '{$_SERVER['REMOTE_ADDR']}' ";
sql_query($tmp_sql, FALSE);
@@ -3269,7 +3638,8 @@ function module_exec_check($exe, $type)
break;
}
for($i=0; $i<count($out); $i++) {
$out_cnt = count($out);
for($i=0; $i<$out_cnt; $i++) {
if(strpos($out[$i], 'KCP ENC') !== false) {
$search = true;
break;
@@ -3284,7 +3654,8 @@ function module_exec_check($exe, $type)
break;
}
for($i=0; $i<count($out); $i++) {
$out_cnt = count($out);
for($i=0; $i<$out_cnt; $i++) {
if(strpos($out[$i], 'CLIENT') !== false) {
$search = true;
break;
@@ -3299,7 +3670,8 @@ function module_exec_check($exe, $type)
break;
}
for($i=0; $i<count($out); $i++) {
$out_cnt = count($out);
for($i=0; $i<$out_cnt; $i++) {
if(strpos(strtolower($out[$i]), 'ret code') !== false) {
$search = true;
break;
@@ -3959,10 +4331,16 @@ class str_encrypt
function __construct($salt='')
{
if(!$salt)
$this->salt = md5(preg_replace('/[^0-9A-Za-z]/', substr(G5_MYSQL_USER, -1), $_SERVER['SERVER_SOFTWARE'].$_SERVER['DOCUMENT_ROOT']));
else
global $config;
if (!$salt) {
$config_hash = md5(serialize(array($config['cf_title'], $config['cf_theme'], $config['cf_admin_email_name'], $config['cf_login_point'], $config['cf_memo_send_point'])));
//$this->salt = md5(preg_replace('/[^0-9A-Za-z]/', substr($config_hash, -1), $_SERVER['SERVER_SOFTWARE'].$config_hash.$_SERVER['DOCUMENT_ROOT']));
$this->salt = hash('sha256', preg_replace('/[^0-9A-Za-z]/', substr($config_hash, -1), $_SERVER['SERVER_SOFTWARE'].$config_hash.$_SERVER['DOCUMENT_ROOT']));
} else {
$this->salt = $salt;
}
$this->length = strlen($this->salt);
}
@@ -3998,10 +4376,26 @@ class str_encrypt
}
}
// 26년도에 너무 늦게 만들어서 기존의 사용자들과 충돌을 피하기 위해 get_sql_affected_rows 이라 네이밍함
function get_sql_affected_rows($link=null)
{
global $g5;
if (!$link) {
$link = $g5['connect_db'];
}
if (function_exists('mysqli_affected_rows') && G5_MYSQLI_USE) {
return @mysqli_affected_rows($link);
} else {
return @mysql_affected_rows($link);
}
}
// 불법접근을 막도록 토큰을 생성하면서 토큰값을 리턴
function get_write_token($bo_table)
{
$token = md5(uniqid(rand(), true));
$token = get_random_token_string(16);
set_session('ss_write_'.$bo_table.'_token', $token);
return $token;
@@ -4304,10 +4698,49 @@ function get_token_encryption_key($str=''){
function get_random_token_string($length=6)
{
if(function_exists('random_bytes')){
return bin2hex(random_bytes($length));
// 사용 가능한 가장 안전한 CSPRNG를 우선순위대로 시도하여 $length 바이트의 무작위 값을 얻는다.
// PHP 5.2.17 ~ 8.x 호환. 모든 단계는 function_exists/defined로 가드되어 구버전에서도 안전.
$bytes = false;
// 1순위. PHP 7.0+ : random_bytes() — OS의 CSPRNG 직접 사용 (가장 안전)
if (function_exists('random_bytes')) {
$bytes = random_bytes($length);
}
// 2순위. PHP 5.3+ : openssl_random_pseudo_bytes() — OpenSSL 확장이 있으면 사용
elseif (function_exists('openssl_random_pseudo_bytes')) {
$strong = false;
$tmp = openssl_random_pseudo_bytes($length, $strong);
if ($tmp !== false && $strong === true) {
$bytes = $tmp;
}
}
// 3순위. Linux/Unix : /dev/urandom 직접 읽기 (PHP 5.2 호환, OS 차원의 CSPRNG)
if ($bytes === false && DIRECTORY_SEPARATOR === '/' && @is_readable('/dev/urandom')) {
$fp = @fopen('/dev/urandom', 'rb');
if ($fp !== false) {
$tmp = @fread($fp, $length);
@fclose($fp);
if ($tmp !== false && strlen($tmp) === $length) {
$bytes = $tmp;
}
}
}
// 4순위. mcrypt 확장 (PHP 5.2 호환, PHP 7.2에서 제거됨) — Windows 등에서 fallback
if ($bytes === false && function_exists('mcrypt_create_iv') && defined('MCRYPT_DEV_URANDOM')) {
$tmp = @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($tmp !== false && strlen($tmp) === $length) {
$bytes = $tmp;
}
}
if ($bytes !== false) {
return bin2hex($bytes);
}
// 최후 수단: 약한 RNG (암호학적 안전성 없음, 기존 동작 호환을 위해 유지)
// 이 경로에 도달하면 시스템에 안전한 난수 소스가 전혀 없는 비정상 환경이므로 점검 필요.
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$output = substr(str_shuffle($characters), 0, $length);
+1 -1
View File
@@ -64,7 +64,7 @@ function latest($skin_dir='', $bo_table='', $rows=10, $subject_len=40, $cache_ti
} catch (Exception $e) {
}
$row['wr_email'] = ''; //이메일 저장 안함
if (strstr($row['wr_option'], 'secret')){ // 비밀글일 경우 내용, 링크, 파일 저장 안함
if (strpos($row['wr_option'], 'secret') !== false){ // 비밀글일 경우 내용, 링크, 파일 저장 안함
$row['wr_content'] = $row['wr_link1'] = $row['wr_link2'] = '';
$row['file'] = array('count'=>0);
}
+3 -1
View File
@@ -5,7 +5,7 @@ include_once(G5_PHPMAILER_PATH.'/PHPMailerAutoload.php');
// 메일 보내기 (파일 여러개 첨부 가능)
// type : text=0, html=1, text+html=2
function mailer($fname, $fmail, $to, $subject, $content, $type=0, $file="", $cc="", $bcc="")
function mailer($fname, $fmail, $to, $subject, $content, $type=0, $file="", $cc="", $bcc="", $reply_to="")
{
global $config;
global $g5;
@@ -43,6 +43,8 @@ function mailer($fname, $fmail, $to, $subject, $content, $type=0, $file="", $cc=
$mail->addCC($cc);
if ($bcc)
$mail->addBCC($bcc);
if ($reply_to)
$mail->addReplyTo($reply_to);
//print_r2($file); exit;
if ($file != "") {
foreach ($file as $f) {
+2 -1
View File
@@ -89,7 +89,8 @@ class naverpay_register
$send_cost_limit = explode(";", $default['de_send_cost_limit']);
$send_cost_list = explode(";", $default['de_send_cost_list']);
for ($k=0; $k<count($send_cost_limit); $k++) {
$send_cost_limit_cnt = count($send_cost_limit);
for ($k=0; $k<$send_cost_limit_cnt; $k++) {
// 총판매금액이 배송비 상한가 보다 작다면
if ($total_price < preg_replace('/[^0-9]/', '', $send_cost_limit[$k])) {
$diff_cost = preg_replace('/[^0-9]/', '', $send_cost_list[$k]);
+135 -13
View File
@@ -3,6 +3,79 @@
// 쇼핑몰 라이브러리 모음 시작
//==============================================================================
/**
* 쇼핑몰 주문/개인결제 조회용 식별자(uid)를 생성한다.
*
* 결정적(deterministic) 생성 방식은 유지되므로 여러 파일에서 독립적으로 호출해도
* 동일한 값을 얻을 수 있어 기존 검증 구조를 그대로 사용 가능하다.
*
* @param string $type 'order' 또는 'personalpay' 등 네임스페이스 구분자
* @param string $id 주문번호 / 개인결제번호 등
* @param string $time 생성 시각 (od_time / pp_time)
* @param string $ip 생성 시 IP (od_ip / pp_ip)
* @return string 64자 hex
*/
function get_shop_uid($type, $id, $time, $ip)
{
$key = (defined('G5_TOKEN_ENCRYPTION_KEY') && G5_TOKEN_ENCRYPTION_KEY)
? G5_TOKEN_ENCRYPTION_KEY
: (defined('G5_TABLE_PREFIX') ? G5_TABLE_PREFIX : '');
$payload = $type . '|' . $id . '|' . $time . '|' . $ip;
return hash_hmac('sha256', $payload, $key);
}
/**
* 현금영수증 발급 또는 조회에 대한 검증
*
* 다음 셋 중 하나여야 접근 허용:
* 1. 관리자
* 2. 본인 주문/개인결제 (로그인 회원이고 mb_id 일치)
* 3. 비회원이지만 정당한 세션 uid 보유 (orderinquiry.php에서 비밀번호 검증 통과 후
* 또는 주문 완료 직후 세션에 저장된 ss_orderview_uid / ss_personalpay_uid가
* 현재 주문/개인결제의 HMAC uid와 일치)
*
* @param array $od 주문 또는 개인결제 행 (DB에서 fetch한 결과)
* @param string $type 'order' (g5_shop_order) 또는 'personalpay' (g5_shop_personalpay)
* @return bool
*/
function is_shop_order_owner($od, $type = 'order')
{
global $is_admin, $is_member, $member;
if (!is_array($od)) {
return false;
}
// 관리자
if ($is_admin) {
return true;
}
// 본인 주문/개인결제 (로그인 회원)
if ($is_member && isset($od['mb_id']) && $od['mb_id'] !== '' && $od['mb_id'] === $member['mb_id']) {
return true;
}
// 세션 uid 검증
if ($type === 'personalpay') {
if (empty($od['pp_id'])) {
return false;
}
$expected = get_shop_uid('personalpay', $od['pp_id'], $od['pp_time'], $_SERVER['REMOTE_ADDR']);
$session_uid = get_session('ss_personalpay_uid');
} else {
if (empty($od['od_id'])) {
return false;
}
$expected = get_shop_uid('order', $od['od_id'], $od['od_time'], $od['od_ip']);
$session_uid = get_session('ss_orderview_uid');
}
return ($session_uid !== '' && $expected === $session_uid);
}
/*
간편 사용법 : 상품유형을 1~5 사이로 지정합니다.
$disp = new item_list(1);
@@ -1796,7 +1869,8 @@ function get_sendcost($cart_id, $selected=1)
$send_cost_limit = explode(";", $default['de_send_cost_limit']);
$send_cost_list = explode(";", $default['de_send_cost_list']);
$send_cost = 0;
for ($k=0; $k<count($send_cost_limit); $k++) {
$send_cost_limit_cnt = count($send_cost_limit);
for ($k=0; $k<$send_cost_limit_cnt; $k++) {
// 총판매금액이 배송비 상한가 보다 작다면
if ($total_price < preg_replace('/[^0-9]/', '', $send_cost_limit[$k])) {
$send_cost = preg_replace('/[^0-9]/', '', $send_cost_list[$k]);
@@ -1871,7 +1945,8 @@ function get_item_sendcost2($it_id, $price, $qty)
$send_cost_limit = explode(";", $default['de_send_cost_limit']);
$send_cost_list = explode(";", $default['de_send_cost_list']);
for ($k=0; $k<count($send_cost_limit); $k++) {
$send_cost_limit_cnt = count($send_cost_limit);
for ($k=0; $k<$send_cost_limit_cnt; $k++) {
// 총판매금액이 배송비 상한가 보다 작다면
if ($price < preg_replace('/[^0-9]/', '', $send_cost_limit[$k])) {
$sendcost = preg_replace('/[^0-9]/', '', $send_cost_list[$k]);
@@ -2111,8 +2186,9 @@ function get_delivery_inquiry($company, $invoice, $class='')
$dlcomp = explode(")", str_replace("(", "", G5_DELIVERY_COMPANY));
for($i=0; $i<count($dlcomp); $i++) {
if(strstr($dlcomp[$i], $company)) {
$dlcomp_cnt = count($dlcomp);
for($i=0; $i<$dlcomp_cnt; $i++) {
if(strpos($dlcomp[$i], $company) !== false) {
list($com, $url, $tel) = explode("^", $dlcomp[$i]);
break;
}
@@ -2327,12 +2403,12 @@ function exists_inicis_shop_order($oid, $pp=array(), $od_time='', $od_ip='')
//개인결제
if( $pp ) {
$hash_data = md5($pp['pp_id'].$pp['pp_price'].$pp['pp_time']);
if( $hash_data == get_session('ss_personalpay_hash') ){
if( $hash_data === get_session('ss_personalpay_hash') ){
// 개인결제번호제거
set_session('ss_personalpay_id', '');
set_session('ss_personalpay_hash', '');
$uid = md5($pp['pp_id'].$pp['pp_time'].$od_ip);
$uid = get_shop_uid('personalpay', $pp['pp_id'], $pp['pp_time'], $od_ip);
set_session('ss_personalpay_uid', $uid);
goto_url(G5_SHOP_URL.'/personalpayresult.php?pp_id='.$pp['pp_id'].'&amp;uid='.$uid.'&amp;ini_noti=1');
@@ -2346,7 +2422,7 @@ function exists_inicis_shop_order($oid, $pp=array(), $od_time='', $od_ip='')
if( $oid == get_session('ss_order_id') ){
// orderview 에서 사용하기 위해 session에 넣고
$uid = md5($oid.$od_time.$od_ip);
$uid = get_shop_uid('order', $oid, $od_time, $od_ip);
set_session('ss_orderview_uid', $uid);
goto_url(G5_SHOP_URL.'/orderinquiryview.php?od_id='.$oid.'&amp;uid='.$uid.'&amp;ini_noti=1');
} else {
@@ -2388,7 +2464,8 @@ function get_delivery_company($company)
$option .= '<option value="자체배송" '.get_selected($company, '자체배송').'>자체배송</option>'.PHP_EOL;
$dlcomp = explode(")", str_replace("(", "", G5_DELIVERY_COMPANY));
for ($i=0; $i<count($dlcomp); $i++) {
$dlcomp_cnt = count($dlcomp);
for ($i=0; $i<$dlcomp_cnt; $i++) {
if (trim($dlcomp[$i])=="") continue;
list($value, $url, $tel) = explode("^", $dlcomp[$i]);
$option .= '<option value="'.$value.'" '.get_selected($company, $value).'>'.$value.'</option>'.PHP_EOL;
@@ -2406,7 +2483,8 @@ function get_itemuse_thumb($contents, $thumb_width, $thumb_height, $is_create=fa
$matches = get_editor_image($contents, false);
for($i=0; $i<count($matches[1]); $i++)
$matches_cnt = count($matches[1]);
for($i=0; $i<$matches_cnt; $i++)
{
// 이미지 path 구함
$p = parse_url($matches[1][$i]);
@@ -2461,7 +2539,7 @@ function get_itemuselist_thumbnail($it_id, $contents, $thumb_width, $thumb_heigh
}
function shop_is_taxsave($od, $is_view_receipt=false){
global $default, $is_memeber;
global $default, $is_member;
$od_pay_type = '';
@@ -2474,12 +2552,12 @@ function shop_is_taxsave($od, $is_view_receipt=false){
}
if( $od_pay_type ) {
if( $default['de_taxsave_use'] && strstr( $default['de_taxsave_types'], $od_pay_type ) ){
if( $default['de_taxsave_use'] && strpos( $default['de_taxsave_types'], $od_pay_type ) !== false ){
return 1;
}
// 아직 현금영수증 받기전 상태일때만
if( $is_view_receipt && ! $od['od_cash'] && in_array($od['od_settle_case'], array('계좌이체', '가상계좌')) && ! strstr( $default['de_taxsave_types'], $od_pay_type ) ){
if( $is_view_receipt && ! $od['od_cash'] && in_array($od['od_settle_case'], array('계좌이체', '가상계좌')) && strpos( $default['de_taxsave_types'], $od_pay_type ) === false ){
return 2;
}
}
@@ -2770,6 +2848,50 @@ function get_item_images_info($it, $size=array(), $image_width=0, $image_height=
return $images;
}
// 카테고리 전체 경로를 가져오는 함수 (예: 남성의류 > 상의 > 셔츠)
function get_shop_category_path($ca_id, $separator = ' &gt; ')
{
global $g5;
static $category_cache = array(); // 카테고리명 캐시
static $path_cache = array(); // 경로 캐시
if (!$ca_id) return '';
// 동일한 separator로 이미 조회한 경로가 있으면 캐시에서 반환
$cache_key = $ca_id . '|' . $separator;
if (isset($path_cache[$cache_key])) {
return $path_cache[$cache_key];
}
$path_arr = array();
$ca_id_len = strlen($ca_id);
// 카테고리 ID를 2자리씩 분할하여 각 단계의 카테고리명을 조회
for ($i = 2; $i <= $ca_id_len; $i += 2) {
$current_ca_id = substr($ca_id, 0, $i);
// 캐시에 없으면 DB 조회
if (!isset($category_cache[$current_ca_id])) {
$sql = " select ca_name from {$g5['g5_shop_category_table']} where ca_id = '$current_ca_id' ";
$row = sql_fetch($sql);
if ($row) {
$category_cache[$current_ca_id] = $row['ca_name'];
} else {
$category_cache[$current_ca_id] = '';
}
}
if ($category_cache[$current_ca_id]) {
$path_arr[] = $category_cache[$current_ca_id];
}
}
$result = implode($separator, $path_arr);
$path_cache[$cache_key] = $result; // 결과를 캐시에 저장
return $result;
}
function check_payment_method($od_settle_case) {
global $default;
+7 -6
View File
@@ -15,7 +15,7 @@ function get_list_thumbnail($bo_table, $wr_id, $thumb_width, $thumb_height, $is_
$write = get_thumbnail_find_cache($bo_table, $wr_id, 'content');
// 비밀글이면 썸네일을 노출하지 않습니다.
if (isset($write['wr_option']) && strstr($write['wr_option'], "secret")) {
if (isset($write['wr_option']) && strpos($write['wr_option'], "secret") !== false) {
return run_replace('is_secret_list_thumbnail', $empty_array, $bo_table, $write);
}
@@ -29,7 +29,8 @@ function get_list_thumbnail($bo_table, $wr_id, $thumb_width, $thumb_height, $is_
$edt = true;
if( $matches = get_editor_image($write['wr_content'], false) ){
for($i=0; $i<count($matches[1]); $i++)
$matches_cnt = count($matches[1]);
for($i=0; $i<$matches_cnt; $i++)
{
// 이미지 path 구함
$p = parse_url($matches[1][$i]);
@@ -117,7 +118,8 @@ function get_view_thumbnail($contents, $thumb_width=0)
$extensions = array(1=>'gif', 2=>'jpg', 3=>'png', 18=>'webp');
for($i=0; $i<count($matches[1]); $i++) {
$matches_cnt = count($matches[1]);
for($i=0; $i<$matches_cnt; $i++) {
$img = $matches[1][$i];
$img_tag = isset($matches[0][$i]) ? $matches[0][$i] : '';
@@ -275,11 +277,10 @@ function thumbnail($filename, $source_path, $target_path, $thumb_width, $thumb_h
$thumb_filename = preg_replace("/\.[^\.]+$/i", "", $filename); // 확장자제거
// $thumb_file = "$target_path/thumb-{$thumb_filename}_{$thumb_width}x{$thumb_height}.".$ext[$size[2]];
$thumb_file = "$target_path/thumb-{$thumb_filename}_{$thumb_width}x{$thumb_height}.".$file_ext;
$thumb_time = @filemtime($thumb_file);
$source_time = @filemtime($source_file);
if (file_exists($thumb_file)) {
$thumb_time = @filemtime($thumb_file);
$source_time = @filemtime($source_file);
if ($is_create == false && $source_time < $thumb_time) {
return basename($thumb_file);
}
+4 -4
View File
@@ -132,10 +132,10 @@ if(isset($data['pp_id']) && !empty($data['pp_id'])) {
}
$good_mny = isset($PAY['P_AMT']) ? $PAY['P_AMT'] : 0;
$pp_name = clean_xss_tags($data['pp_name']);
$pp_email = clean_xss_tags($data['pp_email']);
$pp_hp = clean_xss_tags($data['pp_hp']);
$pp_settle_case = clean_xss_tags($data['pp_settle_case']);
$pp_name = addslashes(clean_xss_tags($data['pp_name']));
$pp_email = addslashes(clean_xss_tags($data['pp_email']));
$pp_hp = addslashes(clean_xss_tags($data['pp_hp']));
$pp_settle_case = addslashes(clean_xss_tags($data['pp_settle_case']));
$_POST['P_HASH'] = $hash;
$_POST['P_AUTH_NO'] = isset($PAY['P_AUTH_NO']) ? $PAY['P_AUTH_NO'] : '';
+4 -4
View File
@@ -124,10 +124,10 @@ if($PGIP == "211.219.96.165" || $PGIP == "118.129.210.25" || $PGIP == "183.109.7
}
$good_mny = $P_AMT;
$pp_name = clean_xss_tags($data['pp_name']);
$pp_email = clean_xss_tags($data['pp_email']);
$pp_hp = clean_xss_tags($data['pp_hp']);
$pp_settle_case = clean_xss_tags($data['pp_settle_case']);
$pp_name = addslashes(clean_xss_tags($data['pp_name']));
$pp_email = addslashes(clean_xss_tags($data['pp_email']));
$pp_hp = addslashes(clean_xss_tags($data['pp_hp']));
$pp_settle_case = addslashes(clean_xss_tags($data['pp_settle_case']));
set_session('P_TID', $P_TID);
set_session('P_AMT', $P_AMT);
+5
View File
@@ -7,6 +7,11 @@ include_once(G5_MSHOP_PATH.'/_head.php');
$sql_common = " from `{$g5['g5_shop_item_qa_table']}` a join `{$g5['g5_shop_item_table']}` b on (a.it_id=b.it_id) ";
$sql_search = " where (1) ";
if( isset($sfl) && ! in_array($sfl, array('b.it_name', 'a.it_id', 'a.iq_subject', 'a.iq_question', 'a.iq_name', 'a.mb_id')) ){
//다른값이 들어가있다면 초기화
$sfl = '';
}
if(!$sfl)
$sfl = 'b.it_name';
+1 -1
View File
@@ -7,7 +7,7 @@ if (!$is_member)
alert_close('회원만 메일을 발송할 수 있습니다.');
// 스팸을 발송할 수 없도록 세션에 아무값이나 저장하여 hidden 으로 넘겨서 다음 페이지에서 비교함
$token = md5(uniqid(rand(), true));
$token = get_random_token_string(16);
set_session("ss_token", $token);
$it = get_shop_item($it_id, true);
+5
View File
@@ -7,6 +7,11 @@ include_once(G5_MSHOP_PATH.'/_head.php');
$sql_common = " from `{$g5['g5_shop_item_use_table']}` a join `{$g5['g5_shop_item_table']}` b on (a.it_id=b.it_id) ";
$sql_search = " where a.is_confirm = '1' ";
if( isset($sfl) && ! in_array($sfl, array('b.it_name', 'a.it_id', 'a.is_subject', 'a.is_content', 'a.is_name', 'a.mb_id')) ){
//다른값이 들어가있다면 초기화
$sfl = '';
}
if(!$sfl)
$sfl = 'b.it_name';
+1 -1
View File
@@ -16,7 +16,7 @@ $txTid = isset($_POST['TxTid']) ? clean_xss_tags($_POST['TxTid']) : ''; //
$authToken = isset($_POST['AuthToken']) ? clean_xss_tags($_POST['AuthToken']) : ''; // authentication TOKEN
$payMethod = isset($_POST['PayMethod']) ? clean_xss_tags($_POST['PayMethod']) : ''; // payment method
$mid = isset($_POST['MID']) ? clean_xss_tags($_POST['MID']) : ''; // merchant id
$moid = isset($_POST['Moid']) ? clean_xss_tags($_POST['Moid']) : ''; // order number
$moid = isset($_POST['Moid']) ? addslashes(clean_xss_tags(stripslashes($_POST['Moid']))) : ''; // order number
$amt = isset($_POST['Amt']) ? (int) preg_replace('/[^0-9]/', '', $_POST['Amt']) : 0; // Amount of payment
$reqReserved = isset($_POST['ReqReserved']) ? clean_xss_tags($_POST['ReqReserved']) : ''; // mall custom field
$netCancelURL = isset($_POST['NetCancelURL']) ? clean_xss_tags($_POST['NetCancelURL']) : ''; // netCancelURL

Some files were not shown because too many files have changed in this diff Show More