首页 安卓& Kotlin Books 通过教程的现实世界Android

15
用户隐私 由KolinStürt撰写

近期使用这么多数据漏洞和新的隐私法,您的应用程序的可信度取决于您如何管理用户的数据。虽然安全对用户和立法者非常重要,但它仍然是移动应用程序开发的忽视方面。构建应用程序时,您需要考虑从头开始的安全性。

为了帮助开发人员保持用户数据安全,Android 11提供新的隐私功能和设备增强功能,包括范围存储,硬化权限,生物认证和硬件备份密钥存储。此外,有强大的数据隐私API,您可以很好地使用。

在本章中,您’ll learn about:

  • 隐私和安全基础知识
  • 权限
  • 锁定用户数据

如果您错过了以前的章节,则示例应用程序包含一份宠物列表及其医疗数据以及允许您匿名报告问题的部分:

图15.1  - 报告部分
图15.1 - 报告部分

在本章中,您’ll focus on keeping that sensitive information secure.

保护基础

当您第一次开始构建您的应用程序时,重要的是要考虑您需要保留多少用户数据。这些天,最好的做法是避免如果您不必存放私有数据。当然,宠物总是担心他们的隐私权。我们知道宠物最终得到了他们的方式,所以你可能也可以从一开始就得到安全。

开始保护您的应用并确保Deva,首先拥抱才能泄漏到 世界其他地区。在Android中,这通常意味着防止任何其他应用程序读取您的用户数据并限制存储数据并安装应用程序的位置。这将是您迈向保护私人信息的第一步。

使用权限

Ever since Android 6.0, you set the files and SharedPreferences you save with the MODE_PRIVATE constant. That means only your app can access the data. Android 7 doesn’t allow any other option, so you’ll implement this next.

图15.2  - 已弃用的常量
Cilaqo 54.3 - Maxravataw Hawjxonln

@Singleton
class PetSavePreferences @Inject constructor(
    @ApplicationContext context: Context
) : Preferences {
  // ...
  private val preferences = context.getSharedPreferences(PREFERENCES_NAME,
      Context.MODE_PRIVATE)
  private val preferencesWrite = context.getSharedPreferences(PREFERENCES_NAME,
      Context.MODE_PRIVATE)
  // ...
}
图15.3  -  mode_world_wrable此次支持的错误
Gasixi 81.4 - Feni_xivby_qmusotouybo xu Trimum Legosneh Ihnon

限制安装目录

过去几年的Android问题之一是由于许多设备的存储容量较低而耗尽内存,以安装过多的可用应用程序。虽然技术已先进,但现在大多数设备包装大量存储,Android仍然可以通过安装应用程序来缓解存储不足 外部存储.

android:installLocation="internalOnly"

请求用户权限

如前所述,Android 11使用新的编辑功能首次亮相,谁可以在此阅读Nout: //developer.android.com/about/versions/11/privacy.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
  // ...
  private fun uploadPhotoPressed() {
    context?.let {
      if (ContextCompat.checkSelfPermission(it, Manifest.permission.READ_EXTERNAL_STORAGE) // 1
          != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, // 2
            Manifest.permission.READ_EXTERNAL_STORAGE), PIC_FROM_GALLERY)
      } else {
        val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        startActivityForResult(galleryIntent, PIC_FROM_GALLERY)
      }
    }
  }
  // ...
}
@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
  // ...
  override fun onRequestPermissionsResult(requestCode: Int,
                                          permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
      PIC_FROM_GALLERY -> {
        // If request is cancelled, the result arrays are empty.
        if ((grantResults.isNotEmpty()
                && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
          // Permission was granted
          val galleryIntent = Intent(Intent.ACTION_PICK,
              MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
          startActivityForResult(galleryIntent, PIC_FROM_GALLERY)
        }
        return
      }
      else -> {
        // Ignore all other requests.
      }
    }
  }
}
图15.4  - 照片权限请求
Nudosi 72.5 - Ybuwi Gesxojloet Pibiadg

使用IPC.

权限涵盖您需要访问和传递应用程序之外的数据。但有时您将通过IPC传递给您构建的其他应用程序。 IPC代表进程间通信,并且是应用程序中的一个组件的方式,以与另一个组件共享数据。

val intent = Intent()
val packageName = "com.example.app" //1
val activityClass = "com.example.app.TheActivity" // 2
intent.component = ComponentName(packageName, activityClass)
intent.putExtra("UserInfo", "Example string") //3
startActivityForResult(intent) //4

使用签名密钥保护数据广播

In the manifest file, find protectionLevel — it’s part of the first permission. You’ll notice it’s set to normal. Change it to signature by replacing that line with the following:

android:protectionLevel="signature" />
android:protectionLevel="signature"
<uses-permission android:name="com.raywenderlich.android.snitcher.permission.REPORT_DETAIL_FRAGMENT"/>
val intent = Intent()
intent.putExtra("UserInfo", "Example string")
intent.action = "com.example.SOME_NOTIFICATION"
sendBroadcast(intent, "com.example.mypermission")

选择退出

使用权限正确提供另一个好处:它授予用户在系统设置中撤消权限的能力,如果他们以后改变主意,请退出数据共享。为了让用户通知,您的应用程序需要隐私政策,如下所述: //developers.google.com/assistant/console/policies/privacy-policy-guide.

清除缓存

如果用户选择退出,则必须删除您收集的任何数据。这包括临时文件和缓存!因为这个应用程序允许您发送匿名报告,所以您不希望任何数据持续并将其绑在用户身上。您的应用程序或第三方库可以使用缓存文件夹,因此您应该在不再需要时清除它。

@AndroidEntryPoint
class ReportDetailFragment : Fragment() {
  // ...
  override fun onPause() {
    context?.cacheDir?.deleteRecursively()
    context?.externalCacheDir?.deleteRecursively()
    super.onPause()
  }
}

禁用键盘缓存

您的应用程序还有一个具有自动更正的文本字段的键盘缓存。 Android在此处存储用户文本和学习的单词,因此它可以检索用户已输入私有报告的各种单词。为防止泄漏此信息,您需要禁用此缓存。

android:inputType="textNoSuggestions|textVisiblePassword|textFilter|textMultiLine"
android:inputType="textNoSuggestions|textVisiblePassword|textFilter"

禁用其他缓存

There are a few other caches to consider. For example, Android caches data sent over the network to memory and on-device storage. You don’t want to leave that data behind, either. In provideOkHttpClient() inside apimodule.kt., replace //TODO: Disable cache here with:

.cache(null)
connection.setRequestProperty("Cache-Control", "没有缓存")
connection.defaultUseCaches = false
connection.useCaches = false
webview.clearCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)

禁用日志记录

Android将Debug日志保存到您可以检索应用程序的生产构建的文件。即使您正在编写代码和调试应用程序,请务必不要将敏感信息(如密码和键)进行记录到控制台。在发布您的应用程序之前,您将不想忘记删除日志!

if (BuildConfig.DEBUG) {
  Log.v(TAG, "Some log stuff...")
}

禁用屏幕截图

您确保没有留下报告的痕迹,但该应用程序仍然可以拍摄整个报告屏幕的屏幕截图。操作系统也需要您的应用程序的屏幕截图。它将它们用于动画,它在将应用程序放入后台或任务切换器中的打开应用程序列表时播放。这些屏幕截图存储在设备上。

window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
图15.5  - 填充报告
Matixu 13.8 - Bulrrax nihivt

图15.6  - 屏幕截图安全警报
Rijera 87.1 - JZXIUQMCEG Dejiwajp eCikz

擦拭记忆安全

当OS删除文件时,它只删除引用,而不是数据。要完全删除该数据,必须使用随机数据覆盖文件:

fun wipeFile(file: File) {
  if (file.exists()) {
    val length = file.length()
    val random = SecureRandom()
    val randomAccessFile = RandomAccessFile(file, "rws")
    randomAccessFile.seek(0)
    randomAccessFile.filePointer
    val data = ByteArray(64)
    var position = 0
    while (position < length) {
      random.nextBytes(data)
      randomAccessFile.write(data)
      position += data.size
    }
    randomAccessFile.close()
    file.delete()
  }
}
Arrays.fill(byteArray, 0.toByte())
Arrays.fill(charArray, '\u0000')

关键点

在本章中,您已经发现了很多关于数据隐私,并且您的用户现在可以信任您遵循最佳实践以保护其数据。随时下载已完成的最终项目。

然后去哪儿?

因此,您将在高电平缩小对数据的访问。但是,这些只是权限,并且您可以在扎根设备上绕过权限措施。解决方案?与前面提到的相同 - 通过潜在攻击者无法找到的信息加密数据。因此,要学习加密的更精细的详细信息,请访问下一章。

有一个技术问题?想报告一个错误吗? 您可以向书记论坛中的书籍的作者提出问题并报告错误 这里.

Hage反馈分享在线阅读体验? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在这里拥有订阅或本身订阅的书。