WebView安全
明文密码存储
概念
- 如果应用程序的webView对象没有使用setSavePassword()方法或该方法的参数为true,那么用户通过应用浏览网页时记住的密码会以明文的形式保存在系统的webview中。如果手机root,能够从手机上/data/data/应用程序包名/databases路径下导出webview.db文件,而相关的账号和密码就是以明文的形式保存在该db文件中。
依据
如果WebView对象使用了loadUrl方法,且满足以下任意一个条件,则该程序存在明文密码存储
- 未使用setSavePassword()方法
- 使用了setSavePassword()方法,但是参数为true
修复方案
WebView对象调用setSavePassword方法,且参数为false
webView.setSavePassword(false)
远程代码执行漏洞
概念
- Android系统版本低于4.2.2(API 17),会存在一个安全漏洞
- WebView调用了setJavaScriptEnabled()方法且参数为true,同时调用addJavaScriptInterface()方法给该WebView对象添加了Interface
- 攻击者就可以在此基础上为WebView所访问的网页上构建JavaScript语句,并通过WebView所添加的interface在安装该应用的手机上执行这段代码
- 3.0(API11)到4.1(API 16)之间,即使开发者不用addJavaScriptInterface()方法添加Interface,系统中也有使用该方法或其他方法来给WebView对象添加Interface
- 这些方法分别是searchBoxJavaBridge、accessiblity、accessibilityTraversal。
- 所以系统在3.0~4.1之间,如果WebView对象调用了setJavaScriptEnable方法且参数为true,即使没有调用addJavaScriptInterface()方法,它也一样存在远程代码执行漏洞
依据
- API小于17(4.2)也就是minSdk小于17会存在漏洞
- 代码中使用了WebView.addJavascriptInterface方法注册可供javascript的java对象,如
webview.addJavascriptInterface(new PInterface(pHandler), "peakmain");
- 系统在3.0<=version<=4.1之间时会存在三个系统添加的interface,分别是searchBoxJavaBridge、accessibility、accessibilityTraversal,所以还要判断是否移除以这些Interface。未移除则存在漏洞
- webView调用setJavascriptInterface为ture
WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
- 调用了loadUrl方法
webView.loadUrl(url)
总结
一、同时满足以下条件
- minSdk版本低于4.2,即API 17(不包含)
- 使用了addJavascriptInterface方法
- 使用了setJavascript,且参数为true
- 使用了loadUrl方法
二、同时满足以下条件
- mindSdk版本在3.0和4.1之间(包含)
- 未移除searchBoxJavaBridge_、accessibility、accessiTraversal这三个接口
- 使用setJavaScriptEnabled方法,且参数为true
- 使用了loadUrl
修复方案
存在漏洞的代码
WebView webView=findViewById(R.id.webView);
webView.getSetting().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new PInteface(),"peakmain");
webView.loadUrl("https://www.baidu.com");
方案一:
WebView对象不使用addJavaScript方法且如果系统版本为11到16之间(包含11和16),则调用removeJavascript方法移除searchBoxJavaBridge_、accessibility、accesibilityTraversal
WebView webView = findViewById(R.id.webView);
webView.getSetting().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT < 17) {
webView.removeJavascriptInterface("searchBoxJavaBridge_");
webView.removeJavascriptInterface("accessibility");
webView.removeJavascriptInterface("accessibilityTraversal");
}
webView.addJavascriptInterface(new PInteface(), "peakmain");
webView.loadUrl("https://www.baidu.com");
方案二:
WebView对象调用setJavaScriptEnabled方法且参数为false,或不调用该方法(该方法默认情况为false)
WebView webView=findViewById(R.id.webView);
webView.loadUrl("https://www.baidu.com");
方案三:
设置minSdk大于等于17,并且在调用addJavascriptInterface()的方法体头部加上@SuppressLint("JavascriptInterface")标志
@SuppressLint("JavascriptInterface")
public void test() {
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new PInterface(), "PInterf2ace");
webView.loadUrl("https://www.baidu.com");
}
Application安全检测
概念
主要检测AndroidManifest.xml中Application标签下某些属性设置是否安全
调试模式
- 调试模式就是在手机上调试应用程序。在AndroidManifest.xml中有个debugger属性,决定着应用程序是否在手机上可以进行调试。若为true咋表示我们可以在手机上进行调试
- 如果使用ant打包,该值取决于ant的打包参数是release还是debug
- 所以最好不要明确设定,而由打包方式决定改属性的值
- 某些第三方加固工具,会默认将这个参数改成true
检测方法
获取AndroidMainifest.xml中Debuggable属性,判断true还是false
修复方案
不明确设置Debuggable属性,由打包发方式来决定
允许备份检测
概念
- AndroidManifest.xml中Application标签下的allowBackup属性决定这个应用程序是否可以被备份。如果可以被备份,那么这个应用程序的数据都会被流出
- 有些程序确实需要备份,那么他就必须明确的指定backupAgent属性
检测方法
- 如果一个应用程序allowBackup没有明确的指定为false或者没有设置backupAgent,那么这个应用的数据就有可能被外部程序通过备份的方式来窃取数据
修复方案
allowBackup设置为false,或者明确指定backupAgent属性
Android 12适配
- 官方文档:developer.android.google.cn/about/versi…
- 对于在 Android 12(API 级别 31)或更高版本上运行且以其为目标平台的应用,指定
android:allowBackup="false"会停用向 Google 云端硬盘的备份,但不会停用应用的设备间传输 - android:dataExtractionRules.xml文件专门设置备份配置
- 如果要禁用备份,xml文件备份中不必包含任何内容
<data-extraction-rules>
<cloud-backup [disableIfNoEncryptionCapabilities="true|false"]>
<include domain=""/>
</cloud-backup>
<device-transfer>
<include domain=""/>
</device-transfer>
</data-extraction-rules>
四大组件暴露
四大组件,activity、receiver、service三者几乎相似,provider只是在检测方法上有所不同
概念
- activity在AndroidManifest.xml中有几个属性决定这个组件是否被导出,即外部调用,分别是enabled、exproted、permission和action
- enabled: 默认是true。它决定Activity是否能够被系统实例化,true表示可被实例化,否则为不能被实例化
- exported: 属性用于设置该Activity能否被另一个应用程序的组件启动,true则表示可以被启动,false则表示该Activity只能被同一个应用程序的组件或相同用户id的应用程序启动。如果不设置exported并且该Activity下有Intentfilter,那么export为true
- permission: 启动该Activity所需要的权限,如果具有权限,则可以成功启动,否则无法启动
- action: 这个属性在intentFilter下的,只有启动者的action属性与改值一样是才可以成功启动
检测方法
需要同时满足以下的条件
- exported为true
- enabled为true
- permission为空或者该permission的protection属性为normal、dangerous
- 判断代码中是否有对调用者的uid或签名信息进行判断
- 当代码既没有判断调用者的uid,也没有判断签名信息时,该Activity才存在暴露。
修复方案
- 更改AndroidManifest.xml上的设置,将exported或enabled属性明确设定为false
- 设置一个protectionLevel为signature或signatureOrSystem的permission
- 在Java代码中判断调用者的uid
int callUid=Binder.getCallingUid();
if(callUid!=Process.myUid()){
//uid不对
}
- Binder.getCallingUid()方法获取调用者的Uid
- Process.myUid()可以获取当前组件的Uid。
- 两个值不相同则表示调用者为外部程序
- 判断调用者的签名信息
int callUid=Binder.getCallingUid();
String[] packageName=packageManager.getPackagesForUid(callUid);
PackageInfo packageInfo=packageManager.getPackageInfo(packageName[0],packageManager.GET_SIGNATURES);;
Signature[] signatures=packageInfo.signature;
Provider检测方法
在AndroidMainfest.xml中获得Provider下的exported、enabled、permission、readPermission、writePermission属性,并进行判断,看这些属性是否满足以下的条件
- exported为true
- enabled为true
- permission为空或者该permission的protection属性为normal、dangerous
如果同时满足以上所有条件,则判断是否还满足以下任何一种情况
- 不具备任何全新啊
- 仅存在permission,并且permission的protection的属性为normal、dangerous
- 仅存在readPermission
- 仅存在writerPermission
- 仅存在permission和readPermission,并且其中至少一个的protection属性为normal、dangerous
- 仅存在permission和writePermisson,并且其中至少一个的protection属性为normal、dangerous
- 同时存在readPermission和writePermission,并且其中至少一个的protection属性为normal、dangerous. 如果满足上面任何一种情况,则说明provider已暴露了。另外,provider下还有一个path-permission标签,它能为特定的path指定特定的permission。如果这个permission的protectionLevel为normal或dangerous,那么即使Provider没有被暴露,这个特指的path也会暴露
不安全存储
sdcard文件
- SDCard的路径有: Environment.getExternalStorageDirectory(),"/sdcard/","sdcard0","storage/sdcard" 等
- 请勿将敏感信息存储在sdcard上,防止数据泄露或被篡改。推荐存储在"data/data/包名"目录下
sharedPreferences
概念
Shared Preferences存储安全风险源于:
- 开发者在创建文件时没有正确的选取合适的创建模式(MODE_PRIVATE、MODE_WORLD_READABLE以及MODE_WORLD_WRITEABLE)进行权限控制;
- 将用户信息、密码等敏感重要的信息明文存储在SharedPreferences文件中,导致攻击者可通过root手机来查看敏感信息
修复方案
- 将访问权限设置为私有权限或者默认。访问权限默认设置为MODE_PRIVATE/MODE_APPEND
SharedPreferences sp = this.getSharedPreferences("SharedPreference", MODE_PRIVATE);//或者MODE_APPEND
说明
- 当mode参数为 MODE_PRIVATE 和 MODE_APPEND 时,其他应用程序无法对该文件进行读写操作。
- 当mode参数为 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 时,其他应用程序可以对该文件进行读写操作。
- mode默认参数为MODE_PRIVATE
文件
- 修复方案:将访问权限设置为私有权限或者默认。访问权限默认设置为 MODE_PRIVAT ;当访问权限设置为 MODE_PRIVATE 或 MODE_APPEND 即为私有权限
FileOutputStream fos = getBaseContext().openFileOutput("fileName", MODE_PRIVATE);//或者MODE_APPEND
Sqlite
- 修复方案:请将访问权限设置为私有权限或者默认。访问权限默认设置为 MODE_PRIVAT ;当访问权限设置为 MODE_PRIVATE 或 MODE_APPEND 即为私有权限。
SQLiteDatabase myDatabase = getBaseContext().openOrCreateDatabase("Context", MODE_PRIVATE , null);//或者MODE_APPEND