WebView – 네이티브 브리지

OWASP 카테고리: MASVS-PLATFORM: 플랫폼 상호작용

개요

네이티브 브리지는 JavaScript 브리지라고도 하며 WebView와 네이티브 Android 코드 간의 통신을 용이하게 하는 메커니즘으로, addJavascriptInterface 메서드를 사용하여 구현됩니다. 이를 통해 WebView에서 실행되는 JavaScript 코드와 Android 애플리케이션의 자바 코드 간에 양방향 통신이 가능합니다. addJavascriptInterface 메서드는 Java 객체를 WebView의 모든 프레임에 노출하며 모든 프레임은 객체 이름에 액세스하고 메서드를 호출할 수 있습니다. 그러나 애플리케이션이 WebView 내에서 호출 프레임의 출처를 확인할 수 있는 메커니즘이 없으므로 콘텐츠의 신뢰성이 불확실한 상태로 유지되어 보안 문제가 발생합니다.

네이티브 브리지는 HTML 메시지 채널을 사용하여 Android's WebViewCompat.postWebMessage 또는 WebMessagePort.postMessage를 사용하여 JavaScript Window.postMessage와 통신하도록 구현할 수도 있습니다. WebViewCompat.postWebMessageWebMessagePort.postMessage는 WebView 내에서 실행되는 Window.postMessage를 통해 전송된 JavaScript 메시지를 수락할 수 있습니다.

네이티브 브리지와 관련된 여러 위험이 있습니다.

  • JavascriptInterface 기반 브리지:
    • addJavascriptInterface 메서드는 iframe을 포함하여 WebView의 모든 프레임에 제공된 Java 객체를 삽입합니다. 즉, 합법적인 웹사이트에 프레임을 삽입하는 악의적인 서드 파티의 공격에 취약합니다. API 수준 16 이하를 타겟팅하는 애플리케이션은 특히 공격 위험이 높습니다. 이 메서드를 사용하여 JavaScript가 호스트 애플리케이션을 제어하도록 허용할 수 있기 때문입니다.
    • 네이티브 브리지가 사용 설정된 WebView에서 신뢰할 수 없는 사용자가 제공한 콘텐츠를 반영하면 교차 사이트 스크립팅 (XSS) 공격이 가능합니다.
  • MessageChannel 기반 브리지:
    • 메시지 채널 엔드포인트에 출처 검사가 없으면 악성 코드가 포함된 메시지를 비롯하여 모든 발신자의 메시지가 수락됩니다.
    • Java를 임의 JavaScript에 실수로 노출할 수 있습니다.

영향

악의적인 행위자는 addJavascriptInterface, postWebMessage, postMessage 메서드를 활용하여 WebView에서 제어하는 코드에 액세스하거나 조작하거나 삽입할 수 있습니다. 이로 인해 사용자가 악성 사이트로 리디렉션되거나, 악성 콘텐츠가 로드되거나, 민감한 정보을 추출하거나 권한 에스컬레이션를 달성할 수 있는 악성 코드가 기기에서 실행될 수 있습니다.

위험: addJavascriptInterface 위험

WebView는 페이지 렌더링, 탐색, JavaScript 실행과 같은 브라우저의 기본 기능을 구현합니다. WebView는 애플리케이션 내에서 활동 레이아웃의 일부로 웹 콘텐츠를 표시하는 데 사용할 수 있습니다. addJavascriptInterface 메서드를 사용하여 WebView 내에서 네이티브 브리지를 구현하면 교차 사이트 스크립팅 (XSS)과 같은 보안 문제가 발생하거나 공격자가 인터페이스 삽입을 통해 신뢰할 수 없는 콘텐츠를 로드하고 호스트 애플리케이션을 의도하지 않은 방식으로 조작하여 호스트 애플리케이션의 권한으로 자바 코드를 실행할 수 있습니다.

완화 조치

자바스크립트 사용 중지

WebView에 JavaScript가 필요하지 않은 시나리오에서는 setJavaScriptEnabled 내에서 WebSettings를 호출하지 마세요 (예: 정적 HTML 콘텐츠를 표시하는 동안). 기본적으로 WebView에서는 JavaScript 실행이 사용 중지됩니다.

신뢰할 수 없는 콘텐츠를 로드할 때 JavaScript 인터페이스 삭제

WebView에서 신뢰할 수 없는 콘텐츠를 로드하기 전에 removeJavascriptInterface를 호출하여 JavaScript 인터페이스의 객체를 삭제해야 합니다. 예를 들어 shouldInterceptRequest 호출에서 이 작업을 실행할 수 있습니다.

Kotlin

webView.removeJavascriptInterface("myObject")

자바

webView.removeJavascriptInterface("myObject");

HTTPS를 통해서만 웹 콘텐츠 로드

신뢰할 수 없는 콘텐츠를 로드해야 하는 경우 WebView가 암호화된 연결을 통해 웹 콘텐츠를 로드하도록 해야 합니다 (일반 텍스트 통신에 관한 가이드라인도 참고하세요). AndroidManifest 파일에서 android:usesCleartextTrafficfalse로 설정하거나 네트워크 보안 구성에서 HTTP 트래픽을 허용하지 않음으로써 암호화되지 않은 연결에서 초기 페이지 로드가 실행되지 않도록 합니다. 자세한 내용은 usesCleartextTraffic 문서를 참고하세요.

XML

<application
    android:usesCleartextTraffic="false">
    <!-- Other application elements -->
</application>

리디렉션 및 추가 앱 탐색이 암호화되지 않은 트래픽에서 발생하지 않도록 하려면 loadUrl 또는 shouldInterceptRequest에서 HTTP 스키마를 확인하세요.

Kotlin

fun loadSecureUrl(webView: WebView?, url: String?) {
    webView?.let { wv ->  // Ensure valid WebView and URL
        url?.let {
            try {
                val uri = URI(url)
                if (uri.scheme.equals("https", ignoreCase = true)) { // Enforce HTTPS scheme for security
                    wv.loadUrl(url)
                } else {
                    // Log an error or handle the case where the URL is not secure
                    System.err.println("Attempted to load a non-HTTPS URL: $url")
                }
            } catch (e: Exception) {
                // Handle exception for improper URL format
                System.err.println("Invalid URL syntax: $url")
            }
        }
    }
}

자바

public void loadSecureUrl(WebView webView, String url) {
    if (webView != null && url != null) { // Ensure valid WebView and URL
        try {
            URI uri = new URI(url);
            String scheme = uri.getScheme();
            if ("https".equalsIgnoreCase(scheme)) { // Enforce HTTPS scheme for security
                webView.loadUrl(url);
            } else {
                // Log an error or handle the case where the URL is not secure
                System.err.println("Attempted to load a non-HTTPS URL: " + url);
            }
        } catch (URISyntaxException e) {
            // Handle exception for improper URL format
            System.err.println("Invalid URL syntax: " + url);
        }
    }
}

신뢰할 수 없는 콘텐츠 검사

WebView에서 외부 링크가 로드되는 경우 스키마와 호스트(허용 목록 도메인)를 모두 검사합니다. 허용 목록에 없는 도메인은 기본 브라우저에서 열어야 합니다.

신뢰할 수 없는 콘텐츠를 로드하지 않음

가능한 경우 엄격하게 범위가 지정된 URL과 앱 개발자가 소유한 콘텐츠만 WebView에 로드합니다.

민감한 정보 노출 안 함

애플리케이션에서 WebView를 사용하여 민감한 정보에 액세스하는 경우 clearCache 메서드를 사용하여 로컬에 저장된 파일을 삭제하기 전에 JavaScript 인터페이스를 사용하는 것이 좋습니다. no-store와 같은 서버 측 헤더를 사용하여 애플리케이션이 특정 콘텐츠를 캐시하면 안 된다는 것을 나타낼 수도 있습니다.

민감한 기능 노출 안 함

애플리케이션에 민감한 권한이 필요하거나 민감한 정보를 수집하는 경우 애플리케이션 내의 코드에서 호출되고 사용자에게 명시적 공개가 제공되는지 확인합니다. 민감한 작업 또는 사용자 데이터에는 JavaScript 인터페이스를 사용하지 마세요.

대상 API 수준 21 이상

addJavascriptInterface 메서드를 안전하게 사용하는 한 가지 방법은 API 수준 21 이상에서 실행될 때만 메서드가 호출되도록 하여 대상 API 수준 21 이상을 타겟팅하는 것입니다. API 21 이전에는 JavaScript가 리플렉션을 사용하여 삽입된 객체의 공개 필드에 액세스할 수 있었습니다.


위험: MessageChannel 위험

postWebMessage()postMessage()에 출처 제어가 없으면 공격자가 메시지를 가로채거나 네이티브 핸들러에 메시지를 보낼 수 있습니다.

완화 조치

postWebMessage() 또는 postMessage()를 설정할 때는 타겟 출처로 * 를 사용하지 않고 예상되는 발신 도메인을 명시적으로 지정하여 신뢰할 수 있는 도메인의 메시지만 허용합니다.


리소스