Enforce secure communication
Use implicit intents and non-exported content providers
Show an app chooser
ถ้า implicit intent สามารถเรียกเปิด แอพที่2
บนอุปกรณ์ของผู้ใช้
ให้ทำ app chooser วิธีนี้ช่วยให้ผู้ใช้โอนถ่าย sensitive information ไปยัง appที่เขาไว้วางใจ
val intent = Intent(ACTION_SEND)
val possibleActivitiesList: List<ResolveInfo> = 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".
val chooser = resources.getString(R.string.chooser_title).let { title ->
Intent.createChooser(intent, title)
}
startActivity(chooser)
} else if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
Apply signature-based permissions
<permission
android:name="com.example.ak1.PREM"
android:protectionLevel="signature" /> // กำหนดให้สามารถเข้าถึงได้เฉพาะ App ที่สร้างโดย Keystore เดียวกัน
เพิ่มเติม : android:protectionLevel
ตัวอย่างการนำมาใช้กับ broadcast receiver
<receiver
android:name=".OnWifiEnabled"
android:permission="com.example.ak1.PERM">
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
อันตรายจากการไม่ใช้ Custom Permission
https://iak1.blogspot.com/2015/09/android-intent-spoofing.html
Disallow access to your app's content providers
ถ้าคุณตั้งใจจะส่งข้อมูลจากแอพคุณไปยังแอพอื่น (ที่ไม่ใช่แอพของคุณ)
ไม่ควรอนุญาติให้ app ตัวอื่นเข้าถึง contentProvider ที่แอพคุณมี
หากแอพของคุณสามารถติดตั้งบน android 4.1.1 (api level 16)
หรือต่ำกว่านั้น android:exported attribute จะเป็น "true" by default
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
...
android:exported="false">
<!-- Place child elements of <provider> here. -->
</provider>
แล้วถ้าไม่ประกาศ exported=false ก็จะเจอกับ Exploiting Content Provider
(SQL Injection)
Ask for credentials before showing sensitive information
เมื่อขอข้อมูลประจำตัวจากผู้ใช้เพื่อให้สามารถเข้าถึง sensitive information หรือ premium content ในแอพ ให้ขออย่างใดอย่างนึงเช่น Pin / Password / Pattern หรือ biometric credential เช่น fingerprint
ตัวอย่างการทำ Biometric Authen ทั้งแบบ kotlin และ javaได้ที่ https://developer.android.com/topic/security/best-practices#ask-for-credentials
System dialog requesting biometric authentication |
Apply network security measures
Use SSL traffic
ถ้า app ของคุณมีการติดต่อกับ web server ที่มี certificateที่ออกโดย บริษัทที่มีชื่อเสียงและเชื่อถือได้
ใช้ https request แบบ simple simple แบบนี้
val url = URL("https://www.google.com")
val urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.connect()
urlConnection.inputStream.use {
...
}
Add a network security configuration
ถ้า app ของคุณใช้ custom CAs
ก็ยังสามารถทำการตั้งค่า network's security settings ได้ใน configuration file
วิธีการสร้าง configuration โดยไม่ต้องแก้ไขโค้ดในแอพ
1. Declare the configuration in your app's manifest:
<manifest ... >
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
<!-- Place child elements of <application> element here. -->
</application>
</manifest>
2.เพิ่ม XML resource file ไว้ใน res/xml/network_security_config.xml
ระบุว่าให้ traffic ทั้งหมดอยู่บนโดเมนเฉพาะ ควรใช้ HTTPS มาปิดการใช้งาน clear-text
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">secure.example.com</domain>
...
</domain-config>
</network-security-config>
3.ระบุใน network security configuration XML file:
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
อ่านแบบละเอียดๆ - Network security configuration
https://developer.android.com/training/articles/security-config.html
Create your own trust manager
SSL ของคุณไม่ควรยอมรับทุกใบรับรอง
คุณอาจต้องการ setup trust manager และ handle ssl warning ที่เกิดขึ้น
หากมีเงื่อนไขใดๆต่อไปนี้ใน use case
- คุณกำลังติดต่อกับ web server ที่มี certificate ที่ signed โดย custom CA
- CA ที่ไม่น่าเชื่อถือจาก device ที่คุณกำลังใช้
- คุณไม่สามารถใช้ network security configuration.
To learn more about how to complete these steps, see discussion about handling an : unknown cerificate authority.
ข้อมูลที่เกี่ยวข้อง :
Provide the right permissions
Use intents to defer permissions
เป็นไปได้คุณไม่ควรเพิ่ม permission
เพื่อดำเนินการอะไรบางอย่างให้เสร็จสมบูรณ์ในแอพอื่น
ใช้ intent เพื่อ defer request ไปยังแอพที่ต่างกันที่มี permission อยู่แล้ว
ตัวอย่างการใช้ intent เพื่อพาผู้ใช้ไปยัง Contacts app
แทนการร้องขอ READ_CONTACTS and WRITE_CONTACTS permissions:
// Delegates the responsibility of creating the contact to a contacts app,
// which has already been granted the appropriate WRITE_CONTACTS permission.
Intent(Intent.ACTION_INSERT).apply {
type = ContactsContract.Contacts.CONTENT_TYPE
}.also { intent ->
// Make sure that the user has a contacts app installed on their device.
intent.resolveActivity(packageManager)?.run {
startActivity(intent)
}
}
นอกจากนี้, ถ้า app ของคุณต้องการดำเนินการ file-based I/O เช่นการเข้าถึงที่เก็บข้อมูล
หรือการเลือกไฟล์ต่างๆ, มันไม่ต้องการ Special permissions เพราะว่าระบบสามารถดำเนินการให้เสร็จสมบูรณ์อยู่แล้ว
Share data securely across apps
- การ share app content กับแอพอื่นๆในแบบที่ปลอดภัย
- ใช้ read-only หรือ write-only permissions ตามความจำเป็น
- จัดการ one-time access to data โดยใช้ FLAG_GRANT_READ_URI_PERMISSION และ FLAG_GRANT_WRITE_URI_PERMISSION flags.
Store data safely
Store private data within internal storage
เก็บข้อมูลส่วนตัวของผู้ใช้ทั้งหมดไว้ใน
devicei internalstorage ที่มี sanboxed แต่ละ app.
แอพของคุณไม่ต้องการ request permission เพื่อดูไฟล์เหล่านี้, และแอพอื่นๆไม่สามารถเข้าถึงไฟล์ เป็นการรักษาความปลอดภัยที่เพิ่มเข้ามา
เมื่อ user uninstall app ... device จะลบทุกไฟล์ที่แอพเซฟไว้ใน internal storage
// 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.
val FILE_NAME = "sensitive_info.txt"
val fileContents = "This is some top-secret information!"
openFileOutput(FILE_NAME, Context.MODE_PRIVATE).use { fos ->
fos.write(fileContents.toByteArray())
}
Use external storage cautiously
โดย default, android system ไม่บังคับใช้ข้อจำกัดด้านความปลอดภัยบนข้อมูลที่อยู่ใน external storage, และการจัดเก็บข้อมูลไม่รับประกัณว่าจะสามารถเชื่อมต่อกับอุปกรณ์ได้ (External storage directory แต่ละเครื่องไม่เหมือนกัน), คุณควรใช้มาตรการความปลอดภัยต่อไปนี้ เพื่อให้การเข้าถึงข้อมูลอย่างปลอดภัยใน external storage
> Use scoped directory access
ถ้า app ของคุณต้องการ เข้าถึงเฉพาะ directory ที่กำหนดใน external storage ของอุปกรณ์,
คุณสามารถใช้ scoped directory access เพื่อจำกัด app ที่จะเข้าถึงที่เก็บข้อมูล external storage ของ device ....เพื่อความสะดวกแก่ผู้ใช้ app คุณควร save directory access URI เพื่อให้ผู้ใช้ไม่จำเป็นต้องอนุมัติการเข้าถึง directory ทุกครั้งที่แอพของคุณพยาม access มัน
Note* ถ้าคุณใช้ scoped directory access กับ directory ที่เฉพาะเจาะจงใน external storage, ผู้ใช้อาจจะลบกันได้ดังนั้นควรจะมี logic ที่มาควบคุมโดยใช้ Environment.getExternalStorageState()
จะ return value ที่เป็น พฤติกรรมของผู้ใช้
The following code snippet uses scoped directory access with the pictures directory
within a device's primary shared storage:
private const val PICTURES_DIR_ACCESS_REQUEST_CODE = 42
...
private fun accessExternalPicturesDirectory() {
val intent: Intent = (getSystemService(Context.STORAGE_SERVICE) as StorageManager)
.primaryStorageVolume.createAccessIntent(Environment.DIRECTORY_PICTURES)
startActivityForResult(intent, PICTURES_DIR_ACCESS_REQUEST_CODE)
}
...
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == PICTURES_DIR_ACCESS_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// User approved access to scoped directory.
if (resultData != null) {
val picturesDirUri: Uri = resultData.data
// Save user's approval for accessing this directory
// in your app.
contentResolver.takePersistableUriPermission(
picturesDirUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
}
}
คำเตือน !! อย่าส่งค่า null เข้าไปใน createAccessIntent() เกินความจำเป็นเพราะนี้จะทำให้แอปของคุณเข้าถึงปริมาณทั้งหมดที่ StorageManager ค้นหาแอปของคุณ
> Check validity of data
ถ้าแอปของคุณใช้ข้อมูลจาก external storage, ต้อง make sure ว่า contents ของข้อมูล
ไม่ได้รับความเสียหาย หรือ modified แอปของคุณควรจะมี logic เพื่อจัดการความถูกต้องด้วย
The following example shows the permission and logic that check a file's validity:
<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
private val UNAVAILABLE_STORAGE_STATES: Set<String> =
setOf(MEDIA_REMOVED, MEDIA_UNMOUNTED, MEDIA_BAD_REMOVAL, MEDIA_UNMOUNTABLE)
...
val ringtone = File(getExternalFilesDir(DIRECTORY_RINGTONES), "my_awesome_new_ringtone.m4a")
when {
isExternalStorageEmulated(ringtone) -> {
Log.e(TAG, "External storage is not present")
}
UNAVAILABLE_STORAGE_STATES.contains(getExternalStorageState(ringtone)) -> {
Log.e(TAG, "External storage is not available")
}
else -> {
val fis = FileInputStream(ringtone)
// available() determines the approximate number of bytes that
// can be read without blocking.
val bytesAvailable: Int = fis.available()
val fileBuffer = ByteArray(bytesAvailable)
StringBuilder(bytesAvailable).apply {
while (fis.read(fileBuffer) != -1) {
append(fileBuffer)
}
// Implement appropriate logic for checking a file's validity.
checkFileValidity(this)
}
}
}
Store only non-sensitive data in cache files
เพื่อให้เข้าถึงได้เร็วขึ้น non-sensitive app data ควรเก็บมันไว้ใน device's cache.
สำหรับ cache ที่ใหญ่กว่า 1MB ใช้ getExternalCacheDir() หรือใช้ getCacheDir()
The following code snippet shows how to cache a file that your app recently downloaded:
val cacheFile = File(myDownloadedFileUri).let { fileToCache ->
File(cacheDir.path, fileToCache.name)
}
Use SharedPreferences in private mode
อันนี้คือ basic อยู่แล้วขอข้ามคำอธิบายไป แต่แนะนำว่า
ถ้าต้องการแชร์ data ข้าม app ห้ามใช้ SharedPreferences
ให้ย้อนไปอ่าน share data securely across apps.
เพิ่มเติม : http://trymydroid.blogspot.com/2017/06/hack-android-hack-sharedpreference.html
Update*
https://trymydroid.blogspot.com/2020/01/encryptedsharedpreferences-security.html
Keep services and dependencies up-to-date
Check the Google Play services security provider
section นี้ใช้กับ app ที่มีการใช้งาน google play service เท่านั้น
Update all app dependencies
ก่อนการ deploy app ต้อง make sure ก่อนว่า libraries, SDKs, และ dependencies อื่นๆ อัพเดทเป็นเวอร์ชั่นล่าสุด
ตัวอย่างเช่น Android SDK ใช้ tool บน Android Studio อย่าง SDK Manager ที่มา update ให้
หรืออีกตัวอย่าง third-party dependencies ให้เช็คที่ website ของ libraries ที่ app เพื่ออัพเดท security patches
...........................SUMMARY............................
- Signature-basedpermissions
- Disallow access to your app's content providers
- ทำ app chooser ช่วยให้ผู้ใช้สามารถเลือกโอนถ่าย sensitive information ไปยัง appที่เขาไว้วางใจ
- เพื่อให้สามารถเข้าถึง sensitive information หรือ premium content ในแอพ ให้สอบถาม
Password / Pattern หรือ biometric credential เช่น fingerprint
- Use SSL traffic
- ถ้า app ของคุณมีการติดต่อกับ web server ที่มี certificateที่ออกโดย บริษัทที่มีชื่อเสียงและเชื่อถือได้
- ถ้า app ของคุณใช้ custom CAsก็ยังสามารถทำการตั้งค่า network's security settings ได้ใน configuration file
- การ share app content กับแอพอื่นๆในแบบที่ปลอดภัย
- ใช้ read-only หรือ write-only permissions ตามความจำเป็น
- จัดการ one-time access to data โดยใช้ FLAG_GRANT_READ_URI_PERMISSION และ FLAG_GRANT_WRITE_URI_PERMISSION flags.
- เก็บข้อมูลส่วนตัวของผู้ใช้ทั้งหมดไว้ใน device internal storage ที่มี sanboxed แต่ละ app.
- การเข้าถึงข้อมูลอย่างปลอดภัยใน external storage
- Use scoped directory access
- Environment.getExternalStorageState() จะ return value ที่เป็น พฤติกรรมของผู้ใช้
ต้อง make sure ว่า contents ของข้อมูลไม่ได้รับความเสียหาย หรือ modified แอปของคุณควรจะมี logic เพื่อจัดการความถูกต้องด้วย
- non-sensitive app data ควรเก็บมันไว้ใน device's cache. สำหรับ cache ที่ใหญ่กว่า 1MB ใช้ getExternalCacheDir() หรือใช้ getCacheDir()
- Check the Google Play services security provider
- Update all app dependencies
Ref: https://developer.android.com/topic/security/best-practices