WebView中常见的安全问题有哪些?如何防范?
WebView是移动端混合开发的核心组件,但因其承载不可信Web内容的特性,长期被视为安全攻击面的重灾区。以下从实际面试和工程实践出发,梳理WebView中最常见的安全问题及其防范方案。
远程代码执行(RCE)漏洞
这是WebView最严重的安全威胁之一。在Android API 16及以下版本中,addJavascriptInterface()暴露的Java对象可通过反射机制被JavaScript调用任意系统方法,攻击者可借此执行任意命令。
防范措施:
- Android 4.2(API 17)及以上必须使用
@JavascriptInterface注解标记允许被JS调用的方法,未标注的方法不会被暴露 - 绝不通过
addJavascriptInterface传递Context、Activity等敏感对象 - 如果仅需JS调用原生功能,优先使用
evaluateJavascript()回调方式替代双向接口 - 低版本兼容方案:移除JS接口或使用URL Scheme+
shouldOverrideUrlLoading做消息中转
java// 正确用法:仅暴露必要方法 webView.addJavascriptInterface(new SafeJsBridge(), "NativeBridge"); class SafeJsBridge { @JavascriptInterface public String getToken() { return "masked_token"; // 不返回真实敏感数据 } }
本地文件访问漏洞
WebView默认允许通过file://协议访问本地文件系统。恶意页面可利用此特性读取应用私有目录下的数据库、SharedPreferences等敏感文件,甚至通过file://跨域读取其他应用的沙箱数据。
防范措施:
javaWebSettings settings = webView.getSettings(); settings.setAllowFileAccess(false); // 禁止文件访问 settings.setAllowFileAccessFromFileURLs(false); // 禁止file协议跨域 settings.setAllowUniversalAccessFromFileURLs(false); // 禁止通用文件访问 settings.setAllowContentAccess(false); // 禁止ContentProvider访问
注意:Android 11(API 30)及以上setAllowFileAccess默认为false,但低版本需要手动关闭。setAllowFileAccessFromFileURLs和setAllowUniversalAccessFromFileURLs在API 16+已默认false,但仍建议显式设置以避免覆盖。
URL校验绕过
攻击者通过构造特殊格式的URL绕过白名单校验,常见手法包括:使用javascript:协议注入代码、利用URL编码和反斜杠差异(Android 7.1及以下Uri.parse与浏览器解析不一致)、Intent Scheme劫持等。
防范措施:
- 使用
java.net.URI替代android.net.Uri进行校验(低版本兼容性更好) - 白名单同时校验scheme和host,不允许
javascript:和data:协议 - 对所有外部输入的URL做规范化处理后再校验
java@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { String url = request.getUrl().toString(); try { URI uri = new URI(url); String scheme = uri.getScheme(); String host = uri.getHost(); if (!"https".equals(scheme) && !"http".equals(scheme)) { return true; // 拦截非HTTP(S)协议 } if (host == null || !ALLOWED_HOSTS.contains(host)) { return true; // 拦截非白名单域名 } } catch (URISyntaxException e) { return true; // 拦截异常URL } return false; }
SSL中间人攻击
部分开发者为解决自签名证书问题,会自定义WebViewClient.onReceivedSslError()并直接调用handler.proceed(),这相当于信任所有证书,使HTTPS形同虚设。
防范措施:
- 绝不在
onReceivedSslError中无条件调用handler.proceed() - 自签名证书场景应将证书打包到APK内,通过
KeyStore加载并校验 - 使用Android Network Security Configuration(7.0+)配置证书固定
xml<!-- res/xml/network_security_config.xml --> <network-security-config> <domain-config> <domain includeSubdomains="true">yourdomain.com</domain> <pin-set> <pin digest="SHA-256">base64编码的证书公钥哈希</pin> </pin-set> </domain-config> </network-security-config>
密码明文存储与Cookie窃取
WebView默认开启密码自动保存功能,用户输入的密码会以明文存储在/data/data/包名/webview.db中。Cookie若未正确配置域和HttpOnly标志,也可能被恶意页面窃取。
防范措施:
javasettings.setSavePassword(false); // 禁用密码保存(API 18已废弃但仍需设置) settings.setSaveFormData(false); // Cookie安全配置 CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.setAcceptThirdPartyCookies(webView, false); // 禁止第三方Cookie
- 服务端设置Cookie的
HttpOnly和Secure标志 - 敏感操作不从Cookie中读取凭证,改用Header传递Token
缓存与数据泄露
WebView的缓存、历史记录、表单数据可能包含敏感信息。如果应用被root设备上的其他应用访问,或用户在公共设备上使用,这些数据可能被提取。
防范措施:
- 敏感页面加载前设置
settings.setCacheMode(WebSettings.LOAD_NO_CACHE) - 用户退出或切换账号时彻底清理WebView数据:
javawebView.clearCache(true); webView.clearHistory(); CookieManager.getInstance().removeAllCookies(null); WebStorage.getInstance().deleteAllData();
- 避免在URL参数中传递敏感数据(如Token),改用POST请求或Header注入
JavaScript注入(XSS)
WebView加载的页面若未对用户输入做充分过滤,攻击者可注入恶意脚本,窃取页面数据或操控原生接口。
防范措施:
- 对所有用户输入和URL参数做HTML实体编码
- 服务端设置Content-Security-Policy响应头
- 使用
safeBrowsingEnabled启用Google安全浏览(API 26+):
javasettings.setSafeBrowsingEnabled(true);
导出组件劫持
如果WebView所在的Activity被设置为exported=true且未做权限校验,任意应用都可以通过Intent启动该Activity并指定加载的URL,从而加载钓鱼页面。
防范措施:
- 非必要不导出包含WebView的Activity
- 导出的Activity必须校验调用方身份和URL白名单
- 在
onCreate中对Intent携带的URL做二次校验
xml<activity android:name=".WebActivity" android:exported="false"> <!-- 非必要不导出 --> </activity>
以上8个安全问题覆盖了WebView从接口暴露、协议漏洞、数据泄露到组件安全的完整攻击面。面试中回答时可遵循"问题描述 -> 攻击原理 -> 防范代码"的三段式结构,重点讲清楚RCE和文件访问这两类高危漏洞,其余问题简要带过即可体现完整度。