flawopen.com/Swift의 XSS/loadHTMLString
iOS의 WKWebView.loadHTMLString은 신뢰할 수 없는 콘텐츠에 안전한가요?
자동으로 안전해지는 것은 아닙니다. WKWebView는 전달된 문자열을 HTML로 파싱하므로 콘텐츠를 신뢰할 수 있거나 올바른 문맥에 맞게 처리하지 않으면 태그와 스크립트를 실행할 수 있는 마크업이 활성 상태로 남습니다. 일반 텍스트와 사용자가 작성한 리치 HTML에는 서로 다른 통제가 필요합니다.
안전하게 사용하기
let html = "\(untrustedText)
" webView.loadHTMLString(html, baseURL: nil)
func escapeHTMLText(_ value: String) -> String {
value
.replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "<", with: "<")
.replacingOccurrences(of: ">", with: ">")
.replacingOccurrences(of: "\"", with: """)
.replacingOccurrences(of: "'", with: "'")
}
let html = "\(escapeHTMLText(untrustedText))
"
webView.loadHTMLString(html, baseURL: nil)
추가로 확인할 위험
페이지 월드의 메시지 핸들러는 페이지 스크립트가 앱 코드에 접근하는 경로를 만듭니다. `WKContentWorld`는 JavaScript 환경을 분리할 수 있지만 페이지와 앱 스크립트는 DOM을 공유합니다. 신뢰할 수 없는 HTML과 브리지를 함께 사용한다면 필요한 최소 기능만 노출하고 메시지와 frame을 검증하며 각 작업을 승인하세요. 월드 분리를 권한 부여로 혼동하지 마세요.
baseURL이 제어하는 것
Apple은 `baseURL`을 전달된 HTML의 상대 URL을 해석하는 기준으로 설명합니다. 마크업을 정화하거나 페이지 JavaScript를 끄지 않습니다. `nil`은 XSS 방어나 샌드박스 설정이 아니므로 리소스와 탐색 정책은 따로 정해야 합니다.
경계마다 알맞은 방어를 선택하세요
HTML 텍스트 노드에 일반 문자열을 넣을 때는 아래처럼 해당 문맥 전용 인코더를 사용하고 속성, URL, CSS, JavaScript에는 재사용하지 마세요. 사용자가 작성한 리치 HTML에는 요소, 속성, URL 스킴 허용 목록을 명시한 유지 관리되는 새니타이저를 사용하세요. 앱 JavaScript에는 값을 소스 문자열에 보간하지 말고 `callAsyncJavaScript` 인수로 전달하세요. 페이지에서 JavaScript가 필요하지 않다면 `allowsContentJavaScript`로 추가 방어를 적용할 수 있지만 올바른 출력 처리를 대신하지는 않습니다.
자주 묻는 질문
아닙니다. 같은 위험이 있으며 추가 정제 기능도 없습니다. WKWebView로 이전하는 것은 기술 현대화일 뿐, 그 자체로 보안 문제를 해결하지 않습니다.
아닙니다. Apple은 `baseURL`을 상대 URL 해석용으로 설명합니다. HTML 새니타이저, 스크립트 스위치, 샌드박스 보장이 아닙니다.
아닙니다. JavaScript 환경은 분리하지만 DOM은 공유합니다. DOM 데이터와 메시지를 신뢰하지 말고 각 네이티브 작업을 별도로 승인하세요.
참고 자료
- Apple Developer — WKWebView
- flawopen.com — Swift의 XSS
- Apple — WKWebView.callAsyncJavaScript(_:arguments:in:in:completionHandler:)
- Apple — WKContentWorld
- Apple — WKUserContentController.add(_:contentWorld:name:)
- Apple — WKWebpagePreferences.allowsContentJavaScript
- OWASP — Cross Site Scripting 방지
- Apple — WKWebView.loadHTMLString(_:baseURL:)