极力推荐文章:欢迎收藏
Android 干货分享
html
本篇文章主要介绍 Android
开发中的部分知识点,经过阅读本篇文章,您将收获如下内容:java
1、增强APP 安全沟通android
- 建议显示使用应用选择器
- 应用共享数据时,建议使用 签名权限
- 禁止其余应用访问本身的ContentProvider
- 获取敏感信息,须要提早询问凭据
- 提升应用网络安全措施
- 建立本身的信任管理器
- 谨慎使用 WebView 对象
- 使用HTML消息通道
2、提供正确的权限permissionweb
- 使用Intent 进行权限申请
- 跨应用 安全访问数据
3、安全的进行数据存储缓存
- 将私有数据存储在内部存储中
- 谨慎使用外部存储
- 检查数据的有效性
- 仅将非敏感数据存储在缓存文件中
- 请在私有模式SharedPreferences
4、使用安全的三方服务安全
- 使用 Google Play服务
- 更新全部应用依赖项
经过提升应用程序的安全性,您能够帮助保持用户信任和设备完整性。服务器
此文主要介绍了几种对您的应用安全性产生重大积极影响的最佳作法。网络
当您保护在应用程序与其余应用程序之间或应用程序与网站之间交换的数据时,能够提升应用程序的稳定性并保护您发送和接收的数据。app
若是隐式意图能够在用户的设备上启动至少两个可能的应用程序,请明确显示应用程序选择器。此交互策略容许用户将敏感信息传输到他们信任的应用程序。
举例以下:dom
Intent intent = new Intent(Intent.ACTION_SEND); List<ResolveInfo> possibleActivitiesList = queryIntentActivities(intent, PackageManager.MATCH_ALL); // Verify that an activity in at least two apps on the user's device // can handle the intent. Otherwise, start the intent only if an app // on the user's device can handle the intent. if (possibleActivitiesList.size() > 1) { // Create intent to show chooser. // Title is something similar to "Share this photo with". String title = getResources().getString(R.string.chooser_title); Intent chooser = Intent.createChooser(intent, title); startActivity(chooser); } else if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
在您控制或拥有的两个应用程序之间共享数据时,请使用 基于签名的权限。这些权限不须要用户确认,而是检查访问数据的应用程序是否使用相同的签名密钥进行签名。所以,这些权限提供了更简化,安全的用户体验。
举例以下:
<manifest xmlns:android = “http://schemas.android.com/apk/res/android” package = “com.example.myapp” > <permission android:name = “my_custom_permission_name” android:protectionLevel = “signature” />
除非您打算将数据从应用程序发送到您不拥有的其余应用程序,不然您应明确禁止其余开发人员的应用程序访问ContentProvider
您的应用程序包含的对象。若是您的应用能够安装在运行Android 4.1.1(API级别16)
或更低级别的设备上,则此设置尤为重要,由于 默认状况下
,这些Android
版本android:exported
的<provider>
元素属性 true
。
举例以下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application ... > <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.myapp.fileprovider" ... android:exported="false"> <!-- Place child elements of <provider> here. --> </provider> ... </application> </manifest>
在向用户请求凭据以便他们能够访问应用中的敏感信息或高级内容时,请询问PIN/Password/Pattern
或 生物识别凭证,例如指纹,人脸识别、虹膜等。
本节重点介绍推荐的生物识别登陆方法。
所述生物识别库 指纹等
显示系统提示,要求用户登陆使用的生物统计凭证。对话框外观的最终一致性建立了更可靠的用户体验。图1中显示了一个示例对话框。
注意:生物识别库扩展了其功能 FingerprintManager
。
要为您的应用提供生物识别身份验证,请完成如下步骤:
app/build.gradle
中添加如下代码dependencies { implementation 'androidx.biometric:biometric:1.0.0-alpha04' }
activity
或fragment
时,使用如下代码段中显示的逻辑显示dialog
:private Handler handler = new Handler(); private Executor executor = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; @Override protected void onCreate(Bundle savedInstanceState) { // ... // Prompt appears when user clicks "Log in" Button biometricLoginButton = findViewById(R.id.biometric_login); biometricLoginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showBiometricPrompt(); } }); } private void showBiometricPrompt() { BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Cancel") .build(); BiometricPrompt biometricPrompt = new BiometricPrompt(MainActivity.this, executor, new BiometricPrompt.AuthenticationCallback() { @Override public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { super.onAuthenticationError(errorCode, errString); Toast.makeText(getApplicationContext(), "Authentication error: " + errString, Toast.LENGTH_SHORT) .show(); } @Override public void onAuthenticationSucceeded( @NonNull BiometricPrompt.AuthenticationResult result) { super.onAuthenticationSucceeded(result); BiometricPrompt.CryptoObject authenticatedCryptoObject = result.getCryptoObject(); // User has verified the signature, cipher, or message // authentication code (MAC) associated with the crypto object, so // you can use it in your app's crypto-driven workflows. } @Override public void onAuthenticationFailed() { super.onAuthenticationFailed(); Toast.makeText(getApplicationContext(), "Authentication failed", Toast.LENGTH_SHORT) .show(); } }); // Displays the "log in" prompt. biometricPrompt.authenticate(promptInfo); }
如下部分介绍了如何改善应用程序的网络安全性。
若是您的应用与具备由知名可信CA
颁发的证书的Web
服务器通讯,则HTTPS
请求很是简单:
URL url = new URL("https://www.google.com"); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.connect(); InputStream in = urlConnection.getInputStream();
若是您的应用使用新的或自定义CA
,则能够在配置文件中声明网络的安全设置。此过程容许您在不修改任何应用程序代码的状况下建立配置。
要将网络安全配置文件添加到您的应用,请按照下列步骤操做:
<manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > <!-- Place child elements of <application> element here. --> </application> </manifest>
res/xml/network_security_config.xml
) 文件经过禁用明文指定特定域的全部流量都应使用HTTPS:
<network-security-config> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="true">secure.example.com</domain> ... </domain-config> </network-security-config>
在开发过程当中,您可使用该 <debug-overrides>
元素以expliticly
方式容许用户安装的证书。在调试和测试期间,此元素会覆盖应用程序的安全关键选项,而不会影响应用程序的发布配置。如下代码段显示了如何在应用程序的网络安全配置XML
文件中定义此元素:
<network-security-config> <debug-overrides> <trust-anchors> <certificates src="user" /> </trust-anchors> </debug-overrides> </network-security-config>
您的SSL
检查程序不该接受每一个证书。您可能须要设置信任管理器并处理在如下条件之一适用于您的用例时发生的全部SSL
警告:
CA
或自定义CA
签名的证书的Web
服务器进行通讯。CA
.只要有可能,只加载WebView
对象中的白名单内容 。换句话说,WebView
应用中的 对象不该容许用户导航到您没法控制的网站。
此外,除非您彻底控制并信任应用程序对象中的内容,不然 不该启用 JavaScript
界面支持WebView
。
若是您的应用必须在运行Android 6.0(API级别23)
及更高版本的设备上使用JavaScript
界面支持,请使用HTML
消息渠道而不是 evaluateJavascript()在网站和应用之间进行通讯,如如下代码段所示:
WebView myWebView = (WebView) findViewById(R.id.webview); // messagePorts[0] and messagePorts[1] represent the two ports. // They are already tangled to each other and have been started. WebMessagePort[] channel = myWebView.createWebMessageChannel(); // Create handler for channel[0] to receive messages. channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { Log.d(TAG, "On port " + port + ", received this message: " + message); } }); // Send a message from channel[1] to channel[0]. channel[1].postMessage(new WebMessage("My secure message"));
您的应用只应请求正常运行所需的最少权限。若是可能,您的应用应在再也不须要时放弃其中一些权限
只要有可能,请不要向您的应用添加权限,以完成可在其余应用中完成的操做。相反,使用意图将请求推迟到已具备必要权限的其余应用程序。
如下示例显示如何使用intent
将用户定向到联系人应用程序,而不是请求 READ_CONTACTS
和 WRITE_CONTACTS
权限:
// Delegates the responsibility of creating the contact to a contacts app, // which has already been granted the appropriate WRITE_CONTACTS permission. Intent insertContactIntent = new Intent(Intent.ACTION_INSERT); insertContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE); // Make sure that the user has a contacts app installed on their device. if (insertContactIntent.resolveActivity(getPackageManager()) != null) { startActivity(insertContactIntent); }
此外,若是您的应用须要执行基于文件的I / O
(例如访问存储或选择文件),则不须要特殊权限,由于系统能够表明您的应用完成操做。更好的是,在用户选择特定URI的内容后,调用应用程序将被授予对所选资源的权限。
请遵循如下最佳作法,以便以更安全的方式与其余应用分享您的应用内容:
“content://”URI
,而不是“file://”URI
。FileProvider 为您执行此操做的实例 。如下代码段显示了如何使用URI
权限授予标志和内容提供程序权限在单独的PDF Viewer
应用程序中显示应用程序的PDF
文件:
// Create an Intent to launch a PDF viewer for a file owned by this app. Intent viewPdfIntent = new Intent(Intent.ACTION_VIEW); viewPdfIntent.setData(Uri.parse("content://com.example/personal-info.pdf")); // This flag gives the started app read access to the file. viewPdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Make sure that the user has a PDF viewer app installed on their device. if (viewPdfIntent.resolveActivity(getPackageManager()) != null) { startActivity(viewPdfIntent); }
虽然您的应用可能须要访问敏感用户信息,但只有当用户信任您能够正确保护其数据时,您的用户才会授予您应用访问其数据的权限。
将全部私有用户数据存储在设备的内部存储中,该存储是按应用程序沙箱化的。您的应用无需请求查看这些文件的权限,其余应用没法访问这些文件。做为一项额外的安全措施,当用户卸载应用程序时,设备会删除应用程序在内部存储中保存的全部文件。
// Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. final String FILE_NAME = "sensitive_info.txt"; String fileContents = "This is some top-secret information!"; FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); fos.write(fileContents.getBytes()); fos.close();
// The file name cannot contain path separators. final String FILE_NAME = "sensitive_info.txt"; FileInputStream fis = openFileInput(FILE_NAME); // available() determines the approximate number of bytes that can be // read without blocking. int bytesAvailable = fis.available(); StringBuilder topSecretFileContents = new StringBuilder(bytesAvailable); // Make sure that read() returns a number of bytes that is equal to the // file's size. byte[] fileBuffer = new byte[bytesAvailable]; while (fis.read(fileBuffer) != -1) { topSecretFileContents.append(fileBuffer); }
默认状况下,Android系统不会对驻留在外部存储中的数据实施安全限制,而且不保证存储介质自己保持与设备的链接。所以,您应该应用如下安全措施来提供对外部存储中信息的安全访问.
若是您的应用只须要访问设备外部存储中的特定目录,则可使用 范围目录访问来限制应用对相应设备外部存储的访问。为方便用户,您的应用应保存目录访问URI,以便用户每次尝试访问目录时都不须要批准对目录的访问权限。
注意:若是对外部存储中的特定目录使用做用域目录访问,请知道用户可能会在应用程序运行时弹出包含此存储的媒体。您应该包含逻辑以正常处理Environment.getExternalStorageState(), 此用户行为致使的返回值的更改 。
如下代码段使用做用域目录访问与设备主要共享存储中的pictures
目录:
private static final int PICTURES_DIR_ACCESS_REQUEST_CODE = 42; private void accessExternalPicturesDirectory() { StorageManager sm = (StorageManager) getSystemService(Context.STORAGE_SERVICE); StorageVolume volume = sm.getPrimaryStorageVolume(); Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES); startActivityForResult(intent, PICTURES_DIR_ACCESS_REQUEST_CODE); } ... @Override public void onActivityResult(int requestCode, int resultCode, Intent resultData) { if (requestCode == PICTURES_DIR_ACCESS_REQUEST_CODE && resultCode == Activity.RESULT_OK) { // User approved access to scoped directory. if (resultData != null) { Uri picturesDirUri = resultData.getData(); // Save user's approval for accessing this directory // in your app. ContentResolver myContentResolver = getContentResolver(); myContentResolver.takePersistableUriPermission(picturesDirUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } } }
警告:请勿null
进行 createAccessIntent()没必要要的操做,由于这会授予您对应用StorageManager 找到的整个卷的应用访问权限 。
若是您的应用使用外部存储中的数据,请确保数据内容未被破坏或修改。您的应用还应包含处理再也不采用稳定格式的文件的逻辑。
如下示例显示了检查文件有效性的权限和逻辑:
<manifest ... > <!-- Apps on devices running Android 4.4 (API level 19) or higher cannot access external storage outside their own "sandboxed" directory, so the READ_EXTERNAL_STORAGE (and WRITE_EXTERNAL_STORAGE) permissions aren't necessary. --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="18" /> ... </manifest>
MyFileValidityChecker
File ringtone = new File(getExternalFilesDir(DIRECTORY_RINGTONES, "my_awesome_new_ringtone.m4a")); if (isExternalStorageEmulated(ringtone)) { Logger.e(TAG, "External storage is not present"); } else if (getExternalStorageState(ringtone) == MEDIA_REMOVED | MEDIA_UNMOUNTED | MEDIA_BAD_REMOVAL | MEDIA_UNMOUNTABLE) { Logger.e(TAG, "External storage is not available"); } else { FileInputStream fis = new FileInputStream(ringtone); // available() determines the approximate number of bytes that // can be read without blocking. int bytesAvailable = fis.available(); StringBuilder fileContents = new StringBuilder(bytesAvailable); byte[] fileBuffer = new byte[bytesAvailable]; while (fis.read(fileBuffer) != -1) { fileContents.append(fileBuffer); } // Implement appropriate logic for checking a file's validity. checkFileValidity(fileContents); }
要更快地访问非敏感应用程序数据,请将其存储在设备的缓存中。对于大小超过1 MB
的缓存,请使用 getExternalCacheDir()
; 不然,使用getCacheDir()
。每种方法都为您提供File
包含应用程序缓存数据的对象。
如下代码段显示了如何缓存应用最近下载的文件:
File cacheDir = getCacheDir(); File fileToCache = new File(myDownloadedFileUri); String fileToCacheName = fileToCache.getName(); File cacheFile = new File(cacheDir.getPath(), fileToCacheName);
注意:若是您使用 getExternalCacheDir() 将应用程序的缓存放在共享存储中,则用户可能会在应用程序运行时弹出包含此存储的媒体。您应该包含逻辑以正常处理此用户行为致使的缓存未命中。
警告:没有对这些文件强制执行安全性。所以,任何具备该WRITE_EXTERNAL_STORAGE 权限的应用都 能够访问此缓存的内容。
使用 getSharedPreferences()
建立或访问应用程序SharedPreferences
对象时,请使用MODE_PRIVATE
。这样,只有您的应用能够访问共享首选项文件中的信息。
若是要跨应用程序共享数据
,请不要使用 SharedPreferences
对象。相反,您应该按照必要的步骤在应用程序之间安全地共享数据。
大多数应用程序使用外部库和设备系统信息来完成专门的任务。经过使应用程序的依赖关系保持最新,您可使这些通讯点更加安全。
若是您的应用使用Google Play
服务,请确保在安装了应用的设备上对其进行了更新。该检查应该在UI
线程以外异步完成。若是设备不是最新的,则您的应用应触发受权错误。
要肯定安装了应用的设备上的Google Play
服务是不是最新的,请按照更新安全提供程序以防止SSL攻击的指南中的步骤进行操做 。
在部署应用程序以前,请确保全部库,SDK
和其余依赖项都是最新的:
对于第一方依赖项(例如Android SDK)
,请使用Android Studio
中的更新工具,例如 SDK Manager
。
对于第三方依赖项,请检查应用程序使用的库的网站,并安装任何可用的更新和安全修补程序。
至此,本篇已结束,若有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!
若有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,当即中止侵权行为,请您多多包涵。