OWASP 카테고리: MASVS-CODE: 코드 품질
개요
안전하지 않은 URI 로드는 Android 애플리케이션이 WebView에 로드하기 전에 URI의 유효성을 올바르게 평가하지 못하는 경우 발생합니다.
이러한 유형의 취약점이 발생하는 근본 원인은 URI가 여러 부분으로 구성되며 그중 최소한 스키마와 호스트(권한 부분)는 URI를 WebView에 로드하거나 애플리케이션에서 내부적으로 사용하기 전에 확인해야 한다는 것입니다(예: 허용 목록에 추가).
가장 흔한 실수는 다음과 같습니다.
- 호스트는 확인하고 스키마는 확인하지 않아 공격자가 인증된 호스트로
http://
,content://
또는javascript://
와 같은 스키마를 사용할 수 있습니다. - 특히 URI가 문자열로 수신되는 경우 URI를 올바르게 파싱하지 못합니다.
- 스키마는 확인하고 호스트는 확인하지 않습니다(호스트 검증이 충분하지 않음).
마지막 사례의 경우 이러한 문제는 일반적으로 애플리케이션이 기본 도메인의 임의 하위 도메인을 허용해야 할 때 발생합니다. 따라서 호스트 이름이 올바르게 추출되었더라도 앱은 java.lang.String
클래스의 startsWith
, endsWith,
또는 contains
와 같은 메서드를 사용하여, 추출된 문자열 섹션에서 기본 도메인이 있는지 확인합니다. 이러한 메서드를 잘못 사용하면 잘못된 결과가 나올 수 있으며 애플리케이션이 잠재적으로 악의적인 호스트를 부적절하게 신뢰하게 될 수 있습니다.
영향
호스트가 사용되는 컨텍스트에 따라 영향은 다를 수 있습니다. WebView에서 악성 URI(필터링/허용 목록을 우회한 URI)를 로드하면 계정 탈취(예: 피싱 사용), 코드 실행(예: 악성 JavaScript 로드) 또는 기기 손상(하이퍼링크를 통해 전송된 악용 코드)이 발생할 수 있습니다.
완화 조치
문자열 URI를 처리할 때 문자열을 URI로 파싱하고 스키마와 호스트를 모두 검증하는 것이 중요합니다.
Kotlin
fun isUriTrusted(incomingUri: String, trustedHostName: String): Boolean {
try {
val uri = Uri.parse(incomingUri)
return uri.scheme == "https" && uri.host == trustedHostName
} catch (e: NullPointerException) {
throw NullPointerException("incomingUri is null or not well-formed")
}
}
Java
public static boolean isUriTrusted(String incomingUri, String trustedHostName)
throws NullPointerException {
try {
Uri uri = Uri.parse(incomingUri);
return uri.getScheme().equals("https") &&
uri.getHost().equals(trustedHostName);
} catch (NullPointerException e) {
throw new NullPointerException(
"incomingUri is null or not well-formed");
}
}
호스트 유효성 검사의 경우 해당 URI 부분을 격리한 후에는 호스트를 신뢰할 수 있는지 정확하게 식별하기 위해 부분이 아닌 전체를 확인하는 것이 중요합니다. startsWith
또는 endsWith
와 같은 메서드를 사용해야 하는 경우 올바른 문법을 사용하고 필요한 문자나 기호를 간과하지 않는 것이 중요합니다. 예를 들어 endsWith
는 정확한 일치를 위해 '.
' 점 문자가 도메인 이름 앞에 필요합니다. 이러한 문자를 무시하면 일치 항목이 부정확해지고 보안이 침해될 수 있습니다. 하위 도메인은 무한 중첩될 수 있으므로 정규 표현식 일치는 호스트 이름을 검증하는 데 권장되는 전략이 아닙니다.
도움을 주신 분들: Microsoft Threat Intelligence의 Dimitrios Valsamaras 및 Michael Peck