1. 문제상황

끼리끼리를 업데이트하려고 구글플레이 콘솔에 들어갔더니 이런 경고가 표시됨

 

그래서 안전하지 않은 WebView SSL 오류 핸들러 구현 이 무엇인지 찾아봄

 

@Composable
fun LegalScreen(navHostController: NavHostController) {
    LegalContent(navHostController)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LegalContent(navHostController: NavHostController) {
    val webView = rememberWebView()
    Scaffold(
        modifier = Modifier
            .fillMaxSize(),
        topBar = {
            TopAppBar(
                title = "개인정보처리방침/이용약관",
                navigationIconImage = ImageVector.vectorResource(id = R.drawable.arrow_back_ios_24px),
                navigationIconOnClick = {
                    navHostController.popBackStack(MainScreenName.SCREEN_LEGAL.name, true)
                },
                isDivider = false
            )
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
        ) {
            AndroidView(
                factory = { webView },
                modifier = Modifier.fillMaxSize()
            )
        }
    }
}


@SuppressLint("SetJavaScriptEnabled")
@Composable
fun rememberWebView(): WebView {
    val context = LocalContext.current
    val webView = remember {
        WebView(context).apply {
            settings.apply {
                javaScriptEnabled = true
                domStorageEnabled = true  // localStorage 등을 위해 필요
                mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW  // HTTPS/HTTP 혼합 컨텐츠 허용
            }
            webViewClient = object : WebViewClient() {
                override fun onReceivedSslError(
                    view: WebView?,
                    handler: SslErrorHandler?,
                    error: SslError?
                ) {
                    handler?.proceed()  // SSL 에러 무시하고 진행
                }
            }
            loadUrl("앱의 개인정보처리방침,이용약관을 담은 구글사이트")
        }
    }
    return webView
}

 

여기서

webViewClient = object : WebViewClient() {
                override fun onReceivedSslError(
                    view: WebView?,
                    handler: SslErrorHandler?,
                    error: SslError?
                ) {
                    handler?.proceed()  // SSL 에러 무시하고 진행
                }
            }

이 부분이 문제였던것이다
SSL에러를 무시하고 웹을 표시할경우 구글의 " 기기 및 네트워크 악용 정책" 위반한다고 한다
SSL오류가 무엇인지 바로 LLM에 물어봤다

 

SSL이란?

SSL(Secure Sockets Layer)은 웹사이트와 사용자의 브라우저 사이의 데이터를 암호화하는 보안 프로토콜입니다.
지금은 TLS라는 이름으로 발전했지만, 흔히 SSL이라고 부릅니다.

📶 예시:

  • 주소창에 🔒 자물쇠가 있는 웹사이트 → SSL 인증서를 가지고 있는 사이트
  • 예: https://www.google.com

 

❗ SSL 오류란?

웹사이트의 SSL 인증서에 문제가 있을 때 발생하는 오류입니다.

🔸 주요 원인

  1. 만료된 인증서
  2. 신뢰할 수 없는 인증서 발급기관(CA)
  3. 도메인 이름과 인증서가 불일치
  4. 자체 서명된 인증서(공식 CA 아닌 경우)

🧨 결과

브라우저나 앱이 경고창을 띄우며 이 웹사이트는 안전하지 않음을 표시합니다.
SSL 오류가 있으면 **중간자 공격(Man-in-the-Middle)**에 노출될 위험이 있습니다.

 

핵심은 해킹위협에 사용자가 노출될수있다는것이다

그래서 위 코드를 아래와같이 수정했다

@Composable
fun LegalScreen(navHostController: NavHostController) {
    LegalContent(navHostController)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LegalContent(navHostController: NavHostController) {
    val context = LocalContext.current

    val showDialog = remember { mutableStateOf(false) }
    val pendingSslHandler = remember { mutableStateOf<SslErrorHandler?>(null) }

    val webView = remember {
        WebView(context).apply {
            settings.apply {
                javaScriptEnabled = true
                domStorageEnabled = true
                mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
            }
            webViewClient = object : WebViewClient() {
                override fun onReceivedSslError(
                    view: WebView?,
                    handler: SslErrorHandler?,
                    error: SslError?
                ) {
                    // 다이얼로그 표시 요청
                    pendingSslHandler.value = handler
                    showDialog.value = true
                }
            }
            loadUrl("https://sites.google.com/view/ggiriggiri-information")
        }
    }

    // 다이얼로그 컴포저블
    if (showDialog.value) {
        CustomAlertDialog(
            onDismiss = {
                showDialog.value = false
                pendingSslHandler.value?.cancel()  // 취소 시 로딩 중지
            },
            onConfirmation = {
                showDialog.value = false
                pendingSslHandler.value?.proceed() // 확인 시 계속 진행
            },
            icon = Icons.Default.Info,
            dialogTitle = "SSL 인증서 경고",
            dialogText = "이 페이지는 보안 인증서에 문제가 있습니다.\n그래도 계속 진행하시겠습니까?"
        )
    }

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        topBar = {
            TopAppBar(
                title = "개인정보처리방침/이용약관",
                navigationIconImage = ImageVector.vectorResource(id = R.drawable.arrow_back_ios_24px),
                navigationIconOnClick = {
                    navHostController.popBackStack(MainScreenName.SCREEN_LEGAL.name, true)
                },
                isDivider = false
            )
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier.padding(innerPadding)
        ) {
            AndroidView(
                factory = { webView },
                modifier = Modifier.fillMaxSize()
            )
        }
    }
}