Introduction
The Android operating system has lots of built-in security features, such as application sandboxing, protection against buffer and integer overflow attacks, and segregated memory areas for program instructions and data. As a result, simple Android apps that don't perform any file system or networking operations can often be considered secure by default.
If you are developing a more complex app, however, it is your responsibility to make it secure and protect the privacy of your users. In this article, I'm going to list some of the best practices you can follow to build a secure Android app that doesn't leak data or permissions, and is, in general, less vulnerable to malicious apps that might be installed on the user's device.
介绍
Android操作系统具有许多内置的安全功能,例如应用程序沙箱,防止缓冲区和整数溢出攻击,以及用于程序指令和数据的隔离存储区。 因此,不执行任何文件系统或网络操作的简单Android应用程序在默认情况下通常被认为是安全的。
如果你正在开发一个更复杂的应用程序,但是,它是你的责任,使其安全,保护用户的隐私。 在本文中,我将列出一些您可以遵循的最佳做法,以构建一个安全的Android应用程序,不泄漏数据或权限,并且一般来说,更不容易受到恶意应用程序可能安装在 用户的设备。
1.
Every Android app has an internal storage directory associated with it whose path is based on the package name of the app. Files inside this directory are very secure because they use the MODE_PRIVATE
file creation mode by default. This means the files cannot be accessed by any other app on the device. Therefore, it is a best place to store all the sensitive data of your app in the internal storage directory.
To determine the absolute path of your app's internal storage directory, it is recommended that you use thegetFilesDir()
method. Once you know its path, referencing files inside it is as simple as referencing files inside any other directory. For example, here's how you could reference a file called myfile.dat
1.对敏感数据使用内部存储
每个Android应用程序都有一个与其关联的内部存储目录,其路径基于应用程序的包名称。 默认情况下,此目录中的文件非常安全,因为它们使用MODE_PRIVATE文件创建模式。 这意味着文件不能由设备上的任何其他应用程序访问。 因此,它是将您的应用程序的所有敏感数据存储在内部存储目录中的最佳位置。
要确定应用程序内部存储目录的绝对路径,建议您使用getFilesDir()方法。 一旦你知道它的路径,引用它里面的文件就像引用任何其他目录中的文件一样简单。 例如,以下是如何在应用程序的内部存储目录中引用名为myfile.dat的文件:
File myFile =
new
File(getFilesDir(),
"myfile.dat"
);
2. Encrypt Data on External Storage
The internal storage capacity of an Android device is often limited. Therefore, at times, you might have no choice but to store sensitive data on external storage media, such as a removable SD card.
Because data on external storage media can be directly accessed by both users and other apps on the device, it is important that you store it in an encrypted format. One of the most popular encryption algorithms used by developers today is AES, short for Advanced Encryption Standard, with a key size of 256 bits.
2.加密外部存储上的数据
Android设备的内部存储容量通常有限。 因此,有时,您可能别无选择,只能将敏感数据存储在外部存储介质(如可移动SD卡)上。
由于外部存储介质上的数据可以由设备上的用户和其他应用程序直接访问,因此以加密格式存储它是非常重要的。 今天开发者使用的最流行的加密算法之一是AES,高级加密标准的简称,密钥大小为256位。
使用Android SDK中包含的javax.crypto软件包编写代码来加密和解密应用程序的数据可能会引起混淆。 因此,大多数开发人员更喜欢使用第三方库,例如Facebook的Conceal库,这些库通常更容易使用。
3. Use Intents for IPC
Experienced programmers who are new to Android application development often try to use sockets, named pipes, or shared files to asynchronously communicate with other apps installed on an Android device. These approaches are not only hard and inelegant, but also prone to threats. An easier and more secure approach to interprocess communication on the Android operating system is to use intents.
To send data to a specific component of an app, you must create a new instance of the Intent class and use its setComponent() method to specify both the package name of the app and the name of the component. You can then add data to it using the putExtra() method.
For example, here's how you could send the string Hello World to an Activity called MyActivity, which belongs to an app whose package name is my.other.app:
Writing code to encrypt and decrypt your app's data using the javax.crypto package, which is included in the Android SDK, can be confusing. Therefore, most developers prefer using third party libraries, such as Facebook's Conceal library, which are usually much easier to work with.
3.使用IPC的意图
经验丰富的程序员,刚刚接触Android应用程序开发经常尝试使用套接字,命名管道或共享文件来与安装在Android设备上的其他应用程序异步通信。 这些方法不仅难以与不雅,但也容易产生威胁。 在Android操作系统上进行进程间通信的一个更简单和更安全的方法是使用意图Intent。
要将数据发送到应用程序的特定组件,必须创建Intent类的新实例,并使用其setComponent()方法指定应用程序的包名称和组件名称。 然后,您可以使用putExtra()方法向其中添加数据。
例如,以下是如何将字符串Hello World发送到名为MyActivity的活动,该活动属于程序包名称为my.other.app的应用程序:
01
02
03
04
05
06
07
08
09
10
11
12
13
// Create an intent
Intent intent = new Intent();
// Specify the component name
intent.setComponent(
new ComponentName( "com.caojiqian.app" , "com.caojiqian.app.MyActivity" )
);
// Add data
intent.putExtra( "DATA" , "Hello Shanghai!" );
// Send the intent to the activity
startActivity(intent);
To send data to multiple apps at once, you can send the intent as a broadcast using the sendBroadcast() method. However, by default, a broadcast can be read by any app that has an appropriately configured BroadcastReceiver.
Therefore, if you want to send sensitive information as a broadcast, you must use a custom permission whose protectionLevel is set to signature. By doing so, the Android operating system makes sure that only apps that were signed using your signing key can receive the broadcast.
Here's a code snippet that shows you how to send the string Hello World as a secure broadcast:
要一次将数据发送到多个应用程序,您可以使用sendBroadcast()方法作为广播发送意图。 但是,默认情况下,广播可以由具有适当配置的BroadcastReceiver的任何应用程序读取。因此,如果要将敏感信息作为广播发送,则必须使用其protectionLevel设置为签名的自定义权限。 通过这样做,Android操作系统可确保只有使用签名密钥签名的应用才能接收广播。这里是一个代码片段,显示如何发送字符串Hello World作为安全广播:
01
02
03
04
05
06
07
08
09
10
11
12
// Create an intent
Intent intent = new Intent();
// Add data
intent.putExtra( "DATA" , "Hello Shanghai" );
// Specify an action name for
// the receiver's intent-filter
intent.setAction( "caojiqian.app.receive" );
// Send as a broadcast using a custom permission
sendBroadcast(intent, "caojiqian.custom.permission" );
Note that the above code works as expected only if the custom permission is declared and used in the manifest files of both the sender and receiver apps.
请注意,只有在发件人和接收方应用程序的清单文件中声明和使用自定义权限时,上述代码才能正常工作。
1
2
3
< permission android:name = "caojiqian.custom.permission"
android:protectionLevel = "signature" />
< uses-permission android:name = "caojiqian.custom.permission" />
4. Use HTTPS
All communications between your app and your servers must be over an HTTPS connection, preferably using the HttpsURLConnection class. If you think using HTTP for data that is not confidential is fine, think again.
Many Android users connect to several open Wi-Fi hotspots in public areas every day. Some of those hotspots could be malicious. A malicious hotspot can easily alter the contents of HTTP traffic to make your app behave in an unexpected manner, or worse still, inject ads or exploits into it.
By using HTTPS, as long as the server is configured with a certificate issued by a trusted certificate authority, such as DigiCert or GlobalSign, you can be sure that your network traffic is secure against both eavesdropping and man-in-the-middle attacks.
If your app has a lot of networking code and you are afraid that you might unwittingly be sending some data as cleartext, you should consider using nogotofail, an open source tool built by Google to find such mistakes.
4.使用HTTPS
您的应用程序和服务器之间的所有通信都必须通过HTTPS连接,最好使用HttpsURLConnection类。如果你认为使用HTTP不是机密的数据是好的,再想想。
许多Android用户每天在公共区域连接到几个开放的Wi-Fi热点。其中一些热点可能是恶意的。恶意热点可以轻松更改HTTP流量的内容,使您的应用以意想不到的方式运行,或者更糟糕的是,向其中插入广告或漏洞。
通过使用HTTPS,只要服务器配置了受信任的证书颁发机构(例如DigiCert或GlobalSign)颁发的证书,就可以确保您的网络流量对于窃听和中间人攻击都是安全的。
如果您的应用程式含有大量网路程式码,而且您害怕您可能会不小心将某些资料传送成清单,建议您考虑使用nogotofail这个由Google建立的开放原始码工具来找出这类错误。
5. Use GCM Instead of SMS
Back when GCM, short for Google Cloud Messaging, didn't exist, many developers were using SMS to push data from their servers to their apps. Today, this practice is largely gone.
If you are one of those developers who still hasn't made the switch from SMS to GCM, you must know that the SMS protocol is neither encrypted nor safe against spoofing attacks. What's more, an SMS can be read by any app on the user's device that has the READ_SMS permission.
GCM is a lot more secure and is the preferred way to push messages to an app because all GCM communications are encrypted. They are authenticated using regularly refreshed registration tokens on the client side and a unique API key on the server side. To learn more about GCM, you can refer to this tutorial about push notifications.
5.使用GCM而不是SMS
回来,当Google Cloud Messaging的简称GCM不存在时,许多开发人员使用SMS将数据从其服务器推送到应用程序。今天,这种做法基本上消失了。
如果您是那些仍然没有从短信切换到GCM的开发者之一,您必须知道SMS协议既不加密也不安全,无法防止欺骗攻击。此外,用户设备上具有READ_SMS权限的任何应用程序都可以读取短信。
GCM更安全,是将邮件推送到应用程序的首选方式,因为所有GCM通信都已加密。它们使用客户端上定期刷新的注册令牌和服务器端的唯一API密钥进行身份验证。要了解有关GCM的更多信息,可以参考本教程了解推送通知。
6. Avoid Asking for Personal Data
User privacy is given a lot of importance these days. In fact, there are laws, such as the European Union's Data Protection Directive and Canada's Personal Information Protection and Electronic Documents Act, which mandate the protection of the privacy of a user. Therefore, unless you have a good reason and a very secure infrastructure to collect, store, and transmit personal user information, you must avoid directly asking for it in your apps.
A better approach to user authentication and user profile information look up on Android is through the Google Identity Platform. Google Identity Platform allows users to quickly sign in to your app using their Google account. After a successful sign in through the platform, whenever necessary, your app can easily look up various details about the user, such as the user's name, email address, profile photo, contacts, and more. Alternatively, you could use free services like Firebase that can manage user authentication for you.
If you must handle user credentials yourself, it is recommended that you store and transmit them in the form of secure hashes. The most straightforward way to generate different types of hashes using the Android SDK is by using the MessageDigest class.
Here's a little code snippet that shows you how to create a hash of the string Hello World using the SHA-256 hashing function:
6.避免询问个人数据
用户隐私被赋予了很多重要性。事实上,有一些法律,例如欧盟的数据保护指令和加拿大的个人信息保护和电子文件法,其中规定保护用户的隐私。因此,除非您有充分的理由和非常安全的基础设施来收集,存储和传输个人用户信息,否则您必须避免在应用程序中直接要求它。
在Android上查找用户身份验证和用户个人资料信息的更好方法是通过Google身份平台。 Google身份平台可让用户使用他们的Google帐户快速登录您的应用。通过平台成功登录后,您的应用可以轻松查找用户的各种详细信息,例如用户名,电子邮件地址,个人资料照片,联系人等。或者,您可以使用免费服务,如Firebase,可以为您管理用户身份验证。
如果您必须自己处理用户凭据,建议您以安全哈希的形式存储和传输它们。使用Android SDK生成不同类型的散列最直接的方法是使用MessageDigest类。
这里有一个小代码片段,显示如何使用SHA-256哈希函数创建字符串Hello World的哈希:
1
2
3
4
5
// Initialize MessageDigest to use SHA-256
MessageDigest md = MessageDigest.getInstance( "SHA-256" );
// Convert the string to a hash
byte [] sha256Hash = md.digest( "Hello World" .getBytes());
7. Validate User Input
On Android, invalid user input doesn't usually lead to security issues like buffer overruns. However, if you allow users to interact with a SQLite database or a content provider that internally uses a SQLite database, you must either rigorously sanitize user input or make use of parameterized queries. Failing to do so makes your data vulnerable to SQL injection attacks.
On a similar note, user input validation and sanitization is also very important if you are using user input to dynamically generate code to run on an embedded scripting engine, such as Mozilla Rhino.
7.验证用户输入
在Android上,无效的用户输入通常不会导致像缓冲区溢出这样的安全问题。 但是,如果允许用户与内部使用SQLite数据库的SQLite数据库或内容提供者进行交互,则必须严格地清理用户输入或使用参数化查询。 如果不这样做,您的数据容易受到SQL注入攻击。
类似地,如果使用用户输入动态生成代码以在嵌入式脚本引擎(例如Mozilla Rhino)上运行,则用户输入验证和清理也非常重要。
8. Use ProGuard Before Publishing
Security measures built into an Android app can be severely compromised if attackers are able to get their hands on the source code. Before you publish your app, it is recommended to make use of a tool called ProGuard, which is included in the Android SDK, to obfuscate and minify source code.
Android Studio automatically includes ProGuard in the build process if the buildType is set to release. The default ProGuard configuration available in the Android SDK's proguard-android.txt file is sufficient for most apps. If you want to add custom rules to the configuration, you can do so inside a file named proguard-rules.pro, which is a part of every Android Studio project.
8.发布前使用ProGuard
如果攻击者能够掌握源代码,Android应用中内置的安全措施可能会受到严重影响。在发布应用程序之前,建议使用Android SDK中包含的名为ProGuard的工具来模糊和缩小源代码。
如果buildType设置为release,Android Studio会在构建过程中自动包含ProGuard。 Android SDK的proguard-android.txt文件中提供的默认ProGuard配置足以满足大多数应用程序的需要。如果要向配置添加自定义规则,可以在名为proguard-rules.pro的文件中执行此操作,该文件是每个Android Studio项目的一部分。
Conclusion
I hope you now have a better understanding of how to make your Android apps secure. Most of the best practices I mentioned in this article are applicable only if you are using the Android SDK to develop your apps. If you are using the Android NDK instead, you have to be a lot more careful because, while programming in the C language, you are expected to manage low-level details, such as pointers and memory allocation yourself.
结论
我希望您现在有了更好的了解如何使您的Android应用程序安全。我在本文中提到的大多数最佳做法仅适用于您使用Android SDK开发您的应用程序。如果你使用Android NDK,你必须更加小心,因为在用C语言编程时,你需要自己管理底层的细节,如指针和内存分配。