極力推薦文章:歡迎收藏
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
。
對於第三方依賴項,請檢查應用程序使用的庫的網站,並安裝任何可用的更新和安全修補程序。
至此,本篇已結束,若有不對的地方,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!
若有侵權,請聯繫小編,小編對此深感抱歉,屆時小編會刪除文章,當即中止侵權行爲,請您多多包涵。