설명
AngularJS의 SCE(Strict Contextual Escaping)는 HTML이나 민감한 URL 바인딩에 컨텍스트별 신뢰 검사를 적용합니다. HTML은 $sanitize가 있으면 정화하고, 없으면 신뢰되지 않은 값을 거부합니다. 모든 CSS나 JavaScript를 자동으로 정화하는 기능은 아닙니다. $sceProvider.enabled(false)로 SCE를 전역 비활성화하면, 신뢰되지 않은 값을 ng-bind-html 등에 넣을 때 XSS로 이어질 수 있습니다. 공용 모듈에서 끄는 경우에도 애플리케이션 전체에 영향을 줍니다.
AngularJS의 공식 지원은 2022년 1월에 종료되었습니다. 아래 내용은 기존 AngularJS 애플리케이션의 유지보수를 위한 안내입니다.
잠재적 영향
- XSS에 의한 임의 스크립트 실행(Arbitrary JavaScript Execution): 사용자가 본 문서 컨텍스트에서 악성 스크립트를 실행하여 UI 조작, 추가 공격을 수행할 수 있음
- 세션 탈취(Session Hijacking): 쿠키/토큰을 탈취해 사용자 계정을 가로챌 수 있음
- 민감정보 유출(Data Exfiltration): 페이지 내 표시되거나 접근 가능한 데이터(개인정보, CSRF 토큰 등)를 외부로 전송 가능
- 페이지 변조 및 피싱(Defacement/Phishing): DOM을 조작해 가짜 입력폼, 결제창을 띄워 사용자 기만
- 추가 공격으로의 발판(Defense Bypass/CSRF Chain): SCE 비활성화와 입력 검증·CSP 등의 방어 부족이 겹치면 피해가 커질 수 있음
해결 방법
$sceProvider.enabled(false)를 사용하지 말고 기본값(SCE 활성화)을 유지하세요.- 동적 HTML이 꼭 필요하면 다음 방식으로 처리하세요:
- ng-bind(텍스트 바인딩) 사용으로 HTML 해석을 차단하세요.
- ngSanitize 모듈의 $sanitize로 정화한 뒤, 꼭 필요한 경우에만 $sce.trustAsHtml(cleanHtml)로 필요한 컨텍스트에서만 신뢰하도록 표시하세요.
- 사용자 생성 컨텐츠(댓글, 소개글 등)는 서버/클라이언트 모두에서 허용 목록 기반의 정화(whitelist sanitization) 수행한 뒤 저장하거나 표시하세요.
- 템플릿에서 ng-bind-html 사용 시 입력 출처를 제한하고, 신뢰할 수 있는 데이터만 바인딩하세요.
- 서드파티 스크립트/모듈이 SCE를 끄지 않는지 코드 리뷰와 정적 분석(SAST)으로 점검하세요.
- CSP(Content-Security-Policy)를 적용해 인라인 스크립트와 임의 스크립트 로딩을 제한하여 피해 범위를 줄이세요.
예시
변경 전
각 블록은 별도 app.js와 index.html의 일부를 함께 보여 줍니다.
javascript
<!-- app.js -->
(function(){
angular.module('shopApp', [])
.config(['$sceProvider', function($sceProvider){
// BAD: 전역 SCE 비활성화
$sceProvider.enabled(false);
}])
.controller('CommentCtrl', ['$location', function($location){
var vm = this;
// 공격자가 URL ?c=<img src=x onerror=alert(1)> 로 주입 가능
vm.comment = $location.search().c || '';
}]);
})();
<!-- index.html -->
<div ng-app="shopApp" ng-controller="CommentCtrl as vm">
<!-- BAD: SCE 꺼진 상태에서 HTML 그대로 실행됨 -->
<div ng-bind-html="vm.comment"></div>
</div>
변경 후
javascript
<!-- app.js -->
(function(){
angular.module('shopApp', ['ngSanitize'])
// 주의: SCE 비활성화 설정 없음(기본 활성화 유지)
.controller('CommentCtrl', ['$location', '$sanitize', '$sce', function($location, $sanitize, $sce){
var vm = this;
var raw = $location.search().c || '';
// 1) 가장 안전: 텍스트로만 표시
vm.commentText = raw;
// 2) 동적 HTML이 꼭 필요할 때만: 정화 후 한정 신뢰
var clean = $sanitize(raw);
vm.commentHtml = $sce.trustAsHtml(clean);
}]);
})();
<!-- index.html -->
<div ng-app="shopApp" ng-controller="CommentCtrl as vm">
<!-- 안전: HTML 해석 없이 텍스트로 렌더링 -->
<div ng-bind="vm.commentText"></div>
<!-- 필요 시에만 사용: $sanitize + trustAsHtml 결과만 바인딩 -->
<div ng-bind-html="vm.commentHtml"></div>
</div>
설명:
- 변경 전:
$sceProvider.enabled(false)로 SCE 검사를 끈 상태에서 URL 입력을ng-bind-html에 전달해 위험한 HTML이 렌더링될 수 있습니다. - 변경 후: SCE를 유지하고 기본적으로
ng-bind로 텍스트를 표시합니다. HTML이 필요한 경우에는 먼저$sanitize를 적용합니다.$sce.trustAsHtml자체는 정화 함수가 아니라 해당 값을 신뢰한다고 표시하는 API입니다.