fix(core,extensions): 동적 스크립트 주입 경로 전부에 출처 게이트 적용

레이아웃 scripts[] 에만 있던 원격 스크립트 차단 게이트가 다른 주입 경로에는
없어, 저장측(SafeLayoutExpressions·NoExternalUrls)이 외부 URL 저장을 422 로
막아도 런타임 디스패치 한 번으로 임의 원격 코드가 로드됐다. 저장 검증이
통째로 무의미해지는 상태였고 오류도 경고도 남지 않았다.

판정식을 support/scriptSrcPolicy.ts 로 분리해 런타임 SSoT 를 하나로 두고,
loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · 편집기 자산 매니페스트 ·
G7Core.asset.loadScript · PG 플러그인 SDK 주입이 모두 그것을 경유하게 했다.
사본을 두지 않은 이유는 차집합이 그대로 우회로가 되기 때문이다.

- 게이트는 캐시 검사보다 앞에 둔다 (뒤에 두면 이미 로드된 미신뢰 스크립트가
 캐시 히트로 통과한다)
- NoExternalUrls 순회를 9키로 확대 — 번들 레이아웃 601개 사전 스캔으로
 정당한 외부 URL 0건을 확인한 뒤 넓혔다 (저장 회귀 없음)
- callExternal 은 생성자를 참조 동일성으로 거부하고(별칭 전역 포함),
 프로토타입 경로 세그먼트를 읽기·매핑 키 양쪽에서 차단한다
- PG SDK 는 서비스 SDK 라 자체 호스팅이 불가능하므로 manifest 선언 +
 주입 직전 호스트 확인이 게이트다. tosspayments SDK URL 은 확장자가 없어
 정적 검사에 걸리지 않아 그 런타임 확인이 유일한 게이트다
- 함께 드러난 결함: 동시 loadScript 가 로드 전에 완료 처리되던 문제,
 확장 재로드에서 script 기존재가 CSS 까지 건너뛰던 문제, loadCSS 의
 in-flight 미공유
This commit is contained in:
HeuJung
2026-09-02 21:43:13 +09:00
parent 621cd28376
commit 1e89335258
82 changed files with 2706 additions and 240 deletions
+5
View File
@@ -37,11 +37,16 @@
### Security
- 화면이 새 스크립트를 불러오는 모든 경로에 같은 출처 확인을 적용했습니다. 종전에는 레이아웃에 적어 둔 스크립트만 확인 대상이어서, 화면 동작(액션)으로 스크립트를 불러오거나 확장을 활성화할 때 자산을 불러오는 경로, 레이아웃 편집기의 미리보기 화면은 확인 없이 외부 주소를 그대로 불러왔습니다. 이제 모든 경로가 사이트 자신의 주소이거나 확장이 미리 선언한 주소만 허용하며, 그 밖의 주소는 불러오지 않습니다. (#127 @glitter-gim 님께서 건의해주셨습니다.)
- 레이아웃을 저장할 때 외부 주소를 검사하는 범위를 넓혔습니다. 종전에는 검사가 컴포넌트 속성·동작과 화면 진입 동작에만 미쳐, 모달·이름 붙인 동작·오류 처리·컴포넌트 생명주기·슬롯·반응형 설정에 적어 넣은 외부 주소는 그대로 저장되었습니다. 예시·안내용 주소를 담는 데이터 항목은 종전처럼 검사하지 않습니다. (#127 @glitter-gim 님께서 건의해주셨습니다.)
- 화면 동작으로 외부 라이브러리를 호출하는 기능에 안전장치를 더했습니다. 임의 코드 실행에 쓰일 수 있는 내장 함수 호출과, 호출 결과를 화면 값에 옮길 때 프로그램 내부 구조를 건드리는 이름은 거부됩니다. (#127 @glitter-gim 님께서 건의해주셨습니다.)
- 주소에 마침표처럼 보이는 특수문자(전각·표의문자 마침표 등)를 섞으면 서버가 내부 주소로 요청을 보내도록 유도할 수 있던 문제를 수정했습니다. 검사할 때와 실제로 연결할 때 주소를 읽는 방식이 달라 생긴 문제로, 이제 두 시점이 같은 방식으로 주소를 해석합니다. 스케줄의 URL 호출, 주소로 언어팩 설치, 외부 배송비 계산 API 등 서버가 대신 외부로 요청을 보내는 모든 지점이 함께 보호됩니다. 정상적인 국제화 도메인(한글·일본어 도메인 등)은 그대로 사용할 수 있습니다. (KISA 측에서 제보해주셨습니다 — KVE-2026-2010)
- 2단계 인증을 켠 상태에서 계정 잠금을 우회할 수 있던 문제를 수정했습니다. 잠기기 전에 받아 둔 인증 단계를 잠긴 뒤에 마치면 로그인이 되고 잠금까지 풀렸습니다. 이제 인증번호 확인 단계에서도 잠금 여부를 다시 확인하며, 잠긴 계정은 로그인 화면과 동일한 안내를 받습니다. 잠긴 계정은 기존 로그인 상태로도 인증 기간을 연장할 수 없습니다. (KISA 측에서 제보해주셨습니다 — KVE-2026-2011)
### Fixed
- 같은 스크립트를 거의 동시에 두 번 불러오면, 두 번째 요청이 첫 번째 로드가 끝나기 전에 완료된 것으로 처리되어 그 뒤 동작이 아무 반응 없이 끝나던 문제를 수정했습니다. 이제 두 요청 모두 실제 로드가 끝난 뒤에 이어집니다.
- 확장을 활성화할 때 스크립트가 이미 있으면 그 확장의 스타일(CSS)까지 함께 건너뛰던 문제를 수정했습니다. 스타일만 제공하는 확장은 활성화해도 스타일이 적용되지 않았습니다.
- 실제 화면 동작에 쓰이는 라이브러리(axios·laravel-echo·pusher-js)가 개발용으로 분류돼 있어 보안 점검에서 빠지던 문제를 수정했습니다. 이제 점검 대상에 포함되며, 함께 확인된 axios 취약점도 1.20.0 으로 올려 해소했습니다. (#126 @jiwonpapa 님께서 제보해주셨습니다.)
- 글을 쓰다 브라우저 창 크기가 바뀌면 저장 시 본문이 사라지던 문제를 수정했습니다. 새 글은 「내용은 필수입니다」로 저장에 실패했고, 글 수정에서는 저장에 성공한 것처럼 보이면서 그때까지 고친 내용이 사라졌습니다. 창 크기를 조금만 바꿔도(20픽셀 이내) 발생했으므로, 휴대폰에서 주소창이 숨겨지거나 키보드가 올라오거나 화면을 돌리는 것도 같은 상황입니다. 게시판 글쓰기(사용자·관리자), 페이지 본문, 상품 상세설명, 상품 공통정보 화면이 대상입니다. (#130 @jiwonpapa 님께서 제보해주셨습니다.)
- 창 크기가 바뀐 뒤 본문을 고치고 제목 등 다른 입력칸을 건드리면, 저장 시 본문이 고치기 전 내용으로 되돌아가던 문제를 수정했습니다. 새 글은 「내용은 필수입니다」로 저장에 실패했고, 글 수정에서는 저장에 성공한 것처럼 보이면서 그때까지 고친 내용이 사라졌습니다. 편집기에는 고친 내용이 그대로 보였기 때문에 저장 후 다시 열어보기 전까지는 알 수 없었습니다. 게시판 글쓰기(사용자·관리자), 페이지 본문, 상품 상세설명, 상품 공통정보 화면이 대상입니다.