Android Secure Coding Standard / 2020
Android Secure Coding Standard / 2020
Android Secure Coding Standard / 2020
Contents
1 Introduction 2
1.1 Building a Secure Smartphone Society . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Timely Feedback on a Regular Basis Through the Beta Version . . . . . . . . . . . . . . . 3
1.3 Usage Agreement of the Guidebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Correction articles of September 1, 2018 edition . . . . . . . . . . . . . . . . . . . . . . . 3
i
Secure Coding Guide Documentation Release 2019-12-01
ii
Secure Coding Guide Documentation Release 2019-12-01
• The content of this guide is up to date as of the time of publication, but standards and environments
are constantly evolving. When using sample code, make sure you are adhering to the latest coding
standards and best practices.
• JSSEC and the writers of this guide are not responsible for how you use this document. Full
responsibility lies with you, the user of the information provided.
• Android is a trademark or a registered trademark of Google Inc. The company names, product
names and service names appearing in this document are generally the registered trademarks or
trademarks of their respective companies. Further, the registered trademark ®, trademark (TM)
and copyright © symbols are not used throughout this document.
• Parts of this document are copied from or based on content created and provided by Google, Inc.
They are used here in accordance with the provisions of the Creative Commons Attribution 3.0
License
1
Secure Coding Guide Documentation Release 2019-12-01
Introduction
1
1.1 Building a Secure Smartphone Society
This guidebook is a collection of tips concerning the know-how of secure designs and secure coding for
Android application developers. Our intent is to have as many Android application developers as possible
take advantage of this, and for that reason we are making it public.
In recent years, the smartphone market has witnessed a rapid expansion, and its momentum seems
unstoppable. Its accelerated growth is brought on due to the diverse range of applications. An unspecified
large number of key functions of mobile phones that were once not accessible due to security restrictions
on conventional mobile phones have been made open to smartphone applications. Subsequently, the
availability of varied applications that were once closed to conventional mobile phones is what makes
smartphones more attractive.
With great power that comes from smartphone applications comes great responsibility from their devel-
opers. The default security restrictions on conventional mobile phones had made it possible to maintain
a relative level of security even for applications that were developed without security awareness. As
it has been aforementioned with regard to smartphones, since the key advantage of a smartphone is
that they are open to application developers, if the developers design or code their applications without
the knowledge of security issues then this could lead to risks of users’ personal information leakage or
exploitation by malware causing financial damage such as from illicit calls to premium-rate numbers.
Due to Android being a very open model allowing access to many functions on the smartphone, it is
believed that Android application developers need to take more care about security issues than iOS
application developers. In addition, responsibility for application security is almost solely left to the
application developers. For example, applications can be released to the public without any screening
from a marketplace such as Google Play (former Android Market), though this is not possible for iOS
applications.
In conjunction with the rapid growth of the smartphone market, there has been a sudden influx of
software engineers from different areas in the smartphone application development market. As a result,
there is an urgent call for the sharing knowledge of secure design and consolidation of secure coding
know-how for specific security issues related to mobile applications.
Due to these circumstances, Japan’s Smartphone Security Association (JSSEC) has launched the Secure
Coding Group, and by collecting the know-how of secure design as well as secure coding of Android
applications, it has decided to make all of the information public with this guidebook. It is our intention
to raise the security level of many of the Android applications that are released in the market by having
many Android application developers become acquainted with the know-how of secure design and coding.
As a result, we believe we will be contributing to the creation of a more reliable and safe smartphone
society.
2
Secure Coding Guide Documentation Release 2019-12-01
We, the JSSEC Secure Coding Group, will do our best to keep the content contained in the Guidebook
as accurate as possible, but we cannot make any guarantees. We believe it is our priority to publicize
and share the know-how in a timely fashion. Equally, we will upload and publicize what we consider to
be the latest and most accurate correct information at that particular juncture, and will update it with
more accurate information once we receive any feedback or corrections. In other words, we are taking
the beta version approach on a regular basis. We think this approach would be meaningful for many of
the Android application developers who are planning on using the Guidebook.
The latest version of the Guidebook and sample codes can be obtained from the URL below.
• https://www.jssec.org/dl/android_securecoding_en.pdf Guidebook (English)
• https://www.jssec.org/dl/android_securecoding_en.zip Sample Codes (English)
The latest Japanese version can be obtained from the URL below.
• https://www.jssec.org/dl/android_securecoding.pdf Guidebook (Japanese)
• https://www.jssec.org/dl/android_securecoding.zip Sample Codes (Japanese)
We need your consent for the following two precautionary statements when using the Guidebook.
1. The information contained in the Guidebook may be inaccurate. Please use the information written
here by your own discretion.
2. In case of finding any mistakes contained in the Guidebook, please send us an e-mail to the address
listed below. However, we cannot guarantee a reply or any revisions thereof.
Japan Smartphone Security Association
Secure Coding Group Inquiry
E-mail: jssec-securecoding-qa@googlegroups.com
Subject: [Comment] Android Secure Coding Guidebook 20191201EN
Content: Name (optional), Affiliation (optional), E-mail (optional), Comment (required) and Other
matters (optional)
This section provides a list of corrections and modifications for the previous edition from the viewpoint
of security, as a result of further studies.
In correcting articles, we adopted the outcomes of our studies and the valuable opinions of those who
read the former editions of this guidebook.
Especially, taking in readers’ opinions is considered as a key factor in making the document highly
practical.
We recommend, for those who use a previous edition of the document as a reference, taking a look at
the list below. Note that the list does not include the following kinds of changes and error corrections:
fixes of typos, organizational changes, and improvements in expression.
Any comments, opinions or suggestions on this guidebook are greatly appreciated.
3
Secure Coding Guide Documentation Release 2019-12-01
4
Secure Coding Guide Documentation Release 2019-12-01
5
Secure Coding Guide Documentation Release 2019-12-01
Many guidebooks that have been written on secure coding include warnings about harmful coding prac-
tices and their suggested revisions. Although this approach can be useful at the time of reviewing the
source code that has already been coded, it can be confusing for developers that are about to start
coding, as they do not know which article to refer to.
The Guidebook has focused on the developer’s context of “What is a developer trying to do at this
moment?” Equally, we have taken steps to prepare articles that are aligned with the developer’s context.
For example, we have divided articles into project units by presuming that a developer will be involved
in operations such as “Creating/Using Activities”, “Using SQLite”, etc.
We believe that by publishing articles that support the developer’s context, developers will be able to
easily locate necessary articles that will be instantly useful in their projects.
Each article is comprised of three sections: Sample Code, Rule Book, and Advanced Topics. If you are
in a hurry, please look up the Sample Code and Rule Book sections. The content is provided in a way
where it can be reused to a certain degree. For those who have issues that go beyond these, please refer
the Advanced Topics section. We have given descriptions that will be helpful in finding solutions for
individual cases.
Unless it is specifically noted, our focus of development will be targeted to platforms concerning Android
4.0.3 (API Level 15) and later. Since we have not verified the operational capability of any versions
pertaining to Android versions under 4.0.3 (API Level 15), the measures described may prove ineffective
on these older systems. In addition, even for versions that are covered under the scope of focus, it is
important to verify their operational capability by testing them on your own environment before releasing
them publically.
Also, for the sample code presented in this document, set targetSdkVersion to API level 28 or higher.
This is used to comply with the following requirements specified by Google.
• August 2019: New apps are required to target API level 28 (Android 9) or higher.
• November 2019: Updates to existing apps are required to target API level 28 or higher.
6
Secure Coding Guide Documentation Release 2019-12-01
Sample code that serves as the basic model within the developer’s context and functions as the theme
of an article is published in the Sample Code section. If there are multiple patterns, we have provided
source code for the different patterns and classified them accordingly. We have strived to make our
commentaries as simple as possible. For example, when we want to direct the reader’s attention to a
security issue that requires attention, a bullet-point number will appear next to “Point” in the article.
We will also comment on the sample code that corresponds to the bullet-point number by writing “***
Point (Number) ***.” Please note that a single point may correspond to multiple pieces of sample
code. There are sections throughout the entire source code, albeit very little compared to the entire
code, which requires our attention for security. In order to be able to survey the sections that call for
scrutiny, we try to post the entire class unit of sample code.
Please note that only a portion of sample code is posted in the Guidebook. A compressed file, which
contains the entire sample code, is made public in the URL listed below. It is made public by the Apache
License, Version 2.0; therefore, please feel free to copy and paste it. Please note that we have minimized
the code for error processing in the sample code to prevent it from becoming too long.
• https://www.jssec.org/dl/android_securecoding_en.zip Sample Codes Archive
The projects/keystore file that is attached in the sample code is the keystore file that contains the
developer key for the signature of the APK. The password is “android.” Please use it when singing the
APK in the In-house sample code.
We have provided the keystore file, debug.keystore, for debugging purposes. When using Android Studio
for development, it is convenient for verifying the operational capability of the In-house sample code if
the keystore is set for each project. In addition, for sample code that is comprised of multiple APKs, it is
necessary to match the android:debuggable setting contained inside each AndroidManifest.xml in order
to verify the cooperation between each APK. If the android:debuggable setting is not explicit set when
installing the APK from Android Studio, it will automatically become android:debuggable= “true.”
For embedding the sample code as well as keystore file into Android Studio, please refer to “2.5. Steps
to Install Sample Codes into Android Studio”.
Rules and matters that need to be considered regarding security within the developer’s context will be
published in the Rule Book section. Rules to be handled in that section will be listed in a table format at
the beginning and will be divided into two levels: “Required” and “Recommended.” The rules will consist
of two types of affirmative and negative statements. For example, an affirmative statement that expresses
that a rule is required will say “Required.” An affirmative statement that expresses a recommendation
will say “Recommended.” For a negative statement that expresses the requisite nature of the rule would
say, “Definitely not do.” For a negative sentence that expresses a recommendation would say, “Not
recommended.” Since these differentiations of levels are based on the subjective viewpoint of the author,
it should only be used as a point of reference.
Sample code that is posted in the Sample Code section reflect these rules and matters that need to be
considered, and a detailed explanation on them is available in the Rule Book section. Furthermore, rules
and matters that need to be considered that are not dealt with in the Sample Code section are handled
in the Rule Book section.
Items that require our attention, but that could not be covered in the Sample Code and Rule Book
sections within the developer’s context will be published in the Advanced Topics section. The Advanced
Topics section can be utilized to explore ways to solve separate issues that could not be solved in the
Sample Code or Rule Book sections. For example, subject matters that contain personal opinions as
well as topics on the limitations of Android OS in relation the developer’s context will be covered in the
Advanced Topics section.
7
Secure Coding Guide Documentation Release 2019-12-01
Developers are always busy. Many developers are expected to have basic knowledge of security and
produce many Android applications as quickly as possible in a somewhat safe manner rather than to
really understand the deep security matters. However, there are certain applications out there that
require a high level of security design and implementation from the beginning. For developers of such
applications, it is necessary for them to have a deep understanding concerning the security of Android
OS.
In order to benefit both developers who emphasize development speed and also those who emphasize
security, all articles of the Guidebook are divided into the three sections of Sample Code, Rule Book,
and Advanced Topics. The aim of the Sample Code and Rule Book sections is to provide generalizations
about security that anyone can benefit from and source code that will work with a minimal amount
of customization and hopefully by just copying and pasting. In the Advanced Topics section, we offer
materials that will help developers think in a certain way when they are facing specific problems. It is
the aim of the Advanced Topics section to help developers examine optimal secure design and coding
when they are involved in building individual applications.
The purpose of the Guidebook is to collect security best practices that are necessary for general Android
application developers. Consequently, our scope is focused mainly on security tips (The “Application
Security” section in figure below) for the development of Android applications that are distributed
primarily in a public market.
Security regarding the implementation of components in the “Device Security” of the above figure is
outside the scope of this guidebook. There are differences in the viewpoint of security between general
applications that are installed by users and pre-installed applications by device manufacturers. The
Guidebook only handles the former and does not deal with the latter. In the current version, tips
only on the implementation by Java are posted, but in future versions, we plan on posting tips on JNI
implementations as well.
Also as of now we do not handle threats that results from an attacker obtaining root privileges. We will
assume the premise of a secure Android device in which it is not possible to obtain root privileges and
base our security advice on utilizing the Android OS security model. For handling of assets and threats,
we have provided a detailed description on “3.1.3. Asset Classification and Protective Countermeasures.”
8
Secure Coding Guide Documentation Release 2019-12-01
Since we are not able to discuss all of Android’s secure coding in the Guidebook, we recommend that
you read the literature mentioned below in conjunction with the Guidebook.
• Android Security: Anzenna Application Wo Sakusei Surutameni (Secured Programming in An-
droid) Author: Tao Software Co., Ltd. ISBN: 978-4-8443-3134-6 http://www.amazon.co.jp/dp/
4844331345/
• The CERT Oracle Secure Coding Standard for Java Authors: Fred Long, Dhruv Mohindra, Robert
C. Seacord, Dean F. Sutherland, David Svoboda http://www.amazon.com/dp/0321803957
This section explains how to install sample code into Android Studio. Sample code is divided into
multiple projects depending on the purpose. Installing the sample code is described in, “2.5.1. Installing
the Sample Project”. After the installation is completed, please refer to “2.5.2. Setup the debug.keystore
to run and test the Sample Code” and install the debug.keystore file into Android Studio. We have
verified the following steps in the following environment:
• OS
– Windows 10 Pro
• Android Studio
– 3.5
• Android SDK
– Android 10.0(API 29)
∗ Sample projects can be built through Android 10.0 (API 29) unless otherwise stated.
Acquire the sample code from the URL shown in “2.2.1. Sample Code”
Right click on the sample code that has been compressed into zip file, and click on “Extract All” as
shown below.
Create a workspace under the name “C:\android_securecoding” by designating “C:\” and clicking on
the “Extract” button.
9
Secure Coding Guide Documentation Release 2019-12-01
After clicking on the “Extract” button, right underneath “C:\” a folder called “android_securecod-
ing_en” will be created.
android_securecoding
Create Use Activity
Activity PartnerActivity
In this way, the sample code project will be located under the chapter title in the “android_securecoding”
folder.
Launch Android Studio from the start menu or from a desktop icon.
10
Secure Coding Guide Documentation Release 2019-12-01
If you have already opened a project, the window is displayed, and so close the displayed project by
selecting “File -> Close Project” from the menu.
11
Secure Coding Guide Documentation Release 2019-12-01
Click “Open an existing Android Studio project” from the dialog that is displayed.
12
Secure Coding Guide Documentation Release 2019-12-01
If the version of Gradle in the Android Studio you are using differs from the version assumed by the
sample code projects in this guidebook, Gradle will be optimized.
Following the on-screen instructions, click “Update” to initiate the update of the Android Gradle Plugin.
The message shown below is displayed. Click “Fix Gradle wrapper and re-import project Gradle setting”
to update the Gradle wrapper.
13
Secure Coding Guide Documentation Release 2019-12-01
Android Studio, unlike Eclipse, will display a single project in a window. If you want to open a different
project, click “File -> Open ...”.
14
Secure Coding Guide Documentation Release 2019-12-01
2.5.2 Setup the debug.keystore to run and test the Sample Code
15
Secure Coding Guide Documentation Release 2019-12-01
Select a project from Module list in left pane, selecting “Signing” tab, and click “+” button, then change
the default name “config” to “debug”.
Click the button inside the red circle in Fig. 2.5.15, and set “Store File.” Debug.keystore is contained in
the sample code (underneath the android_securecoding folder)
16
Secure Coding Guide Documentation Release 2019-12-01
Select the Build Types tab, select signing name typed in the previous step, and then click “OK”.
17
Secure Coding Guide Documentation Release 2019-12-01
The path of debug.keystore file you selected is displayed in signingConfigs, signingConfig appears in
debug section of buildTypes.
18
Secure Coding Guide Documentation Release 2019-12-01
There is a commonly accepted way of thinking when examining security issues concerning systems or
applications. First, we need to have a grasp over the objects we want to protect. We will call these
“assets”. Next, we want to gain an understanding over the possible attacks that can take place on an
asset. We will call these “threats”. Finally, we will examine and implement measures to protect “assets”
from the various “threats”. We will call these “countermeasures”.
What we mean by “countermeasures” here is secure design and secure coding, and will deal with these
subjects after Chapter 4. In this section, we will focus on explaining “assets” and “threats”.
There are two types of “objects of protection” within a system or an application: “information” and
“functions”. We will call these “information assets” and “function assets”. “An information asset” refers
to the type of information that can be referred to or changed only by people who have permission. It is a
type of information that cannot be referred to or changed by anyone who does not have the permission.
“A function asset” refers to a function that can be used only by people who have permission and no one
else.
Below, we will introduce types of information assets and functional assets that exist in Android smart-
phones and tablets. We would like you to use the following as a point of reference to deliberate on
matters with regard to assets when developing a system that utilizes Android applications or Android
smartphones/tablets. For the sake of simplicity, we will collectively call Android smartphones/tablets as
Android smartphones.
Table 3.1.1 and Table 3.1.2 represent examples of information contained on an Android smartphone. Ap-
propriate protection is necessary since this information is equivalent to personal information, confidential
information or information that belongs to both.
19
Secure Coding Guide Documentation Release 2019-12-01
The type of information seen in Table 3.1.1 is mainly the type of information that is stored on the
Android smartphone itself or on an SD card. Similarly, the type of information seen in Table 3.1.2 is
primarily managed by an application. In particular, the type of information seen in Table 3.1.2 grows in
proportion to the number of applications installed on the device.
Table 3.1.3 is the amount of information contained in one entry case of contacts. The information here
is not of the smartphone user’s, but of the smartphone user’s friends. In other words, we must be aware
that a smartphone not only contains information on the user, but of other people too.
Until now, we have primarily focused on information about smartphone users, however, application
possesses other important information as well. Fig. 3.1.1 displays a typical view of the information
inside an application divided into the program portion and data portion. The program portion mainly
20
Secure Coding Guide Documentation Release 2019-12-01
consists of information about the application developer, and the data portion mostly pertains to user
information. Since there could be information that an application developer may not want a user to have
access to, it is important to provide protective countermeasures to prohibit a user from referring to or
making changes to such information.
Table 3.1.4 shows examples of features that an Android OS provides to an application. When these
features are exploited by a malware, etc., damages in the form of unexpected charges or loss of privacy
may be incurred by a user. Therefore, appropriate protective counter-measures that are equal the one
extended to information asset should be set in place.
In addition to the functions that the Android OS provides to an application, the inter-application com-
munication components of Android applications are included as part of the function assets as well.
Android applications can allow other applications to utilize features by accessing their internal compo-
nents. We call this inter-application communication. This is a convenient feature, however, there have
been instances where access to functions that should only be used inside a particular application are
21
Secure Coding Guide Documentation Release 2019-12-01
mistakenly given to other applications due the lack of knowledge regarding secure coding on the part of
the developer. There are functions provided by the application that could be exploited by malware that
resides locally on the device. Therefore, it is necessary to have appropriate protective countermeasures
to only allow legitimate applications to access these functions.
In the previous section, we talked about the assets of an Android smartphone. In this section, we will
explain about attacks that can threaten an asset. Put simply, a threat to an asset is when a third party
who should not have permission, accesses, changes, deletes or creates an information asset or illicitly uses
a function asset. The act of directly or indirectly attacking such assets is called a “threat”. Furthermore,
the malicious person or applications that commit these acts are referred to as “the source of the threats”.
Malicious attackers and malware are the sources of threats but are not the threats themselves. The
relationship between our definitions of assets, threats, threat sources, vulnerabilities, and damage are
shown below in Fig. 3.1.2.
Fig. 3.1.2: Relation between Asset, Threat, Threat Source, Vulnerability, and Damage
Fig. 3.1.3 shows a typical environment that an Android application behaves in. From now on, in order
to expand on the explanation concerning the type of threats an Android application faces by using this
figure as a base, we will first learn how to view this figure.
22
Secure Coding Guide Documentation Release 2019-12-01
The figure above depicts the smartphone on the left and server on the right. The smartphone and server
communicate through the Internet over 3G/4G/Wi-Fi. Although multiple applications exist within
a smartphone, we are only showing a single application in the figure in order to explain the threats
clearly. Smartphone-based applications mainly handle user information, but the server-based web services
collectively manage information of all of its users. Consequently, there is no change the importance of
server security as usual. We will not touch upon issues relating to server security as it falls outside of
the scope of the Guidebook.
We will use the following figure to describe the type of threats that exist towards Android applications.
Generally, a smartphone application manages user information on a server so the information assets will
move between the networks connecting them. As indicated in Fig. 3.1.4, a network-based malicious third
party may access (sniff) any information during this communication or try to change information (data
manipulation). The malicious attacker in the middle (also referred to as “Man in The Middle”) can also
pretend to be the real server tricking the application. Without saying, network-based malicious third
parties will usually try to attack the server as well.
23
Secure Coding Guide Documentation Release 2019-12-01
The biggest selling point of a smartphone is in its ability to acquire numerous applications from the
market in order to expand on its features. The downside to users being able to freely install many
applications is that they will sometimes mistakenly install malware. As shown in Fig. 3.1.5, malware
may exploit the inter-application communication functions or a vulnerability in the application in order
to gain access to information or function assets.
Fig. 3.1.6: Attack from Malicious Files that Exploit a Vulnerability in an Application
Various types of files such as music, images, videos and documents are widely available on the Internet
and typically users will download many files to their SD card in order to use them on their smartphone.
Furthermore, it is also common to download attached files sent in an e-mail. These files are later opened
by a viewing or editing application.
If there is any vulnerability in the function of an application that processes these files, an attacker can
use a malicious file to exploit it and gain access to information or function assets of the application. In
particular, vulnerabilities are often present in processing a file format with a complex data structure.
The attacker can fulfill many different goals when exploiting an application in this way.
As shown in Fig. 3.1.6, an attack file stays dormant until it is opened by a vulnerable application. Once it
is opened, it will start causing havoc by taking advantage of an application’s vulnerability. In comparison
to an active attack, we call this attack method a “Passive Attack.”
24
Secure Coding Guide Documentation Release 2019-12-01
With regard to application development for an Android smartphone, the environment as well as features
that help to develop and analyze an application are openly provided to the general user. Among the
features that are provided, the useful ADB debugging feature can be accessed by anyone without regis-
tration or screening. This feature allows an Android smartphone user to easily perform OS or application
analysis.
As it is shown in Fig. 3.1.7, a smartphone user with malicious intent can analyze an application by taking
advantage of the debugging feature of ADB and try to gain access to information or function assets of
an application. If the actual asset contained in the application belongs to the user, it poses no problem,
but if the asset belongs to someone other than the user, such as the application developer, then it will
become a concern. Accordingly, we need to be aware that the legitimate smartphone user can maliciously
target the assets within an application.
Fig. 3.1.8: Attacks from a Malicious Third Party in the Proximity of a Smartphone
Due to face that most smartphones possess a variety of near-field communication mechanisms, such as
NFC, Bluetooth and Wi-Fi, we must not forget that attacks can occur from a malicious attacker who
25
Secure Coding Guide Documentation Release 2019-12-01
is in physical proximity of a smartphone. An attacker can shoulder surf a password while peeping over
a user who is inputting it in. Or, as indicated in Fig. 3.1.8, an attacker can be more sophisticated and
attack the Bluetooth functionality of an application from a remote distance. There is also the threat
that a malicious person could steal the smartphone creating a risk of data leakage or even destroy the
smartphone causing a loss of critical information. Developers need to take these risks into consideration
as well as early as the design stage.
Fig. 3.1.9 summarizes the main types of threats explained in the previous sections. Smartphones are
surrounded by a wide variety of threats and the figure above does not include all of them. Through
our daily information gathering, we need to spread the awareness concerning the various threats that
surround an Android application and be aware of them during the application’s secure design and coding.
The following literature that was created by Japan’s Smartphone Security Association (JSSEC) contains
other valuable information on the threats to smartphone security.
• Security Guidebook for Using Smartphones and Tablets https://www.jssec.org/dl/guide-
lines_v2.pdf [Version 2] (Japanese) https://www.jssec.org/dl/guidelines2012Enew_v1.0.pdf [Ver-
sion 1] (English)
• Implementation Guidebook for Smartphone Network Security [Version 1] https://www.jssec.org/
dl/NetworkSecurityGuide1.pdf (Japanese)
• Cloud Usage Guidebook for Business Purposes of Smartphones [Beta Version]
https://www.jssec.org/dl/cloudguide2012_beta.pdf (Japanese)
• Guidebook for Reviewing the Implementation/Operation of MDM [Version 1] https://www.jssec.
org/dl/MDMGuideV1.pdf (Japanese)
As was discussed in the previous sections, Android smartphones are surrounded by a variety of threats.
Protecting every asset in an application from such threats could prove to be very difficult given the time
it takes for development and due to technical limitations. Consequently, Android application developers
should examine feasible countermeasures for their assets. This should be done according to priority
level based on the developer’s judgement criteria. This is a subjective matter that is based on how the
importance of an asset is viewed and what the accepted level of damage is.
In order to help decide on the protective countermeasures for each asset, we will classify them and
stipulate the level of protective countermeasures for each group. This will be achieved by examining the
26
Secure Coding Guide Documentation Release 2019-12-01
legal basis, pertaining to the level of importance regarding the impact of any damages that can occur and
the social responsibility of the developer (or organization). These will prove to be the judgement criteria
when deciding on how to handle each asset and the implementation of the type of countermeasures. Since
this will become a standard for application developers and organizations on determining how to handle
an asset and provide protective countermeasures, it is necessary to specify the classification methods and
pertaining countermeasures in accordance the application developer’s (or organization’s) circumstances.
Asset classification and protective countermeasure levels that are adopted in the Guidebook are shown
below for reference:
Asset classification and protective countermeasures described in the Guidebook are proposed under the
premise of a secure Android device where root privilege has not been compromised. Furthermore, it
is based on the security measures that utilize the security model of Android OS. Specifically, we are
hypothetically devising protective countermeasures by utilizing the Android OS security model on the
premise of a functioning Android OS security model against assets that are classified lower than or equal
to the medium level asset.
The term “sensitive information”, instead of information asset, will be used from now on in the Guide-
book. As it has been aforementioned in the previous section, we have to determine the asset level and
the level of protective countermeasures for each information asset that an application handles.
1 We also believe in the necessity of protecting high level assets from attacks that are caused due the breaching of the
Android OS security model. Such attacks include the compromise of root privileges and attacks that analyze or alter
the APK binary. To protect these types of assets, we need to design sophisticated defensive countermeasures against such
threats through the combination of multiple methods such as encryption, obfuscation, hardware support and server support.
As the collection of know-how regarding these defenses cannot be easily written in this guidebook, and since appropriate
defensive design differ in accordance to individual circumstances, we have deemed them to be outside of the Guidebook’s
scope. We recommend that you consult with a security specialist who is well versed in tamper resistant designs of Android
if your device requires protection from sophisticated attacks that include attacks resulting from the compromise of root
privileges or attacks caused by the analysis or alteration of an APK file.
27
Secure Coding Guide Documentation Release 2019-12-01
Validating input data is the easiest and yet most effective secure coding method. All data that is inputted
into the application either directly or indirectly by an outside source needs to be properly validated. To
illustrate best practices of input data validation, the following is an example of an Activity as used in a
program that receives data from Intent.
It is possible that an Activity can receive data from an Intent that was tampered by an attacker. By
sending data with a format or a value that a programmer is not expecting, the attacker can induce a
malfunction in the application that leads to some sort of security incident. We must not forget that a
user can become an attacker as well.
Intents are configured by action, data and extras, and we must be careful when accepting all forms of
data that can be controlled by an attacker. We always need to validate the following items in any code
that handles data from an untrusted source.
(a) Does the received data match the format that was expected by the programmer and does the value
fall in the expected scope?
(b) Even if you have received the expected format and value, can you guarantee that the code which
handles that data will not behave unexpectedly?
The next example is a simple sample where HTML is acquired from a remote web page in a designated
URL and the code is displayed in TextView. However, there is a bug.
Sample Code that Displays HTML of a Remote Web page in TextView
TextView tv = (TextView) findViewById(R.id.textview);
InputStreamReader isr = null;
char[] text = new char[1024];
int read;
try {
String urlstr = getIntent().getStringExtra("WEBPAGE_URL");
URL url = new URL(urlstr);
isr = new InputStreamReader(url.openConnection().getInputStream());
while ((read=isr.read(text)) != -1) {
tv.append(new String(text, 0, read));
}
} catch (MalformedURLException e) { //...
From the viewpoint of (a), “urlstr is the correct URL”, verified through the non-occurrence of a Mal-
formedURLException by a new URL(). However, this is not sufficient. Furthermore, when a “file://...”
formatted URL is designated by urlstr, the file of the internal file system is opened and is displayed in
TextView rather than the remote web page. This does not fulfill the viewpoint of (b), since it does not
guarantee the behavior which was expected by the programmer.
The next example shows a revision to fix the security bugs. Through the viewpoint of (a), the input data
is validated by checking that “urlstr is a legitimate URL and the protocol is limited to http or https.” As
a result, even by the viewpoint of (b), the acquisition of an Internet-routed InputStream is guaranteed
through url.openConnection().getInputStream().
Revised sample code that displays HTML of Internet-based Web page in TextView
TextView tv = (TextView) findViewById(R.id.textview);
InputStreamReader isr = null;
char[] text = new char[1024];
int read;
try {
String urlstr = getIntent().getStringExtra("WEBPAGE_URL");
URL url = new URL(urlstr);
String prot = url.getProtocol();
if (!"http".equals(prot) && !"https".equals(prot)) {
throw new MalformedURLException("invalid protocol");
(continues on next page)
28
Secure Coding Guide Documentation Release 2019-12-01
Validating the safety of input data is called “Input Validation” and it is a fundamental secure coding
method. Surmising from the sense of the word of Input Validation, it is quite often the case where
the viewpoint of (a) is heeded but the viewpoint of (b) is forgotten. It is important to remember that
damage does not take place when data enters the program but when the program “uses” that data in an
incorrect way. We hope that you will refer the URLs listed below.
• The CERT Oracle Secure Coding Standard for Java https://www.securecoding.cert.org/
confluence/x/Ux (English)
• Application of CERT Oracle Secure Coding Standard for Android Application Development https:
//www.securecoding.cert.org/confluence/x/C4AiBw (English)
• Rules Applicable Only to the Android Platform (DRD) https://www.securecoding.cert.org/
confluence/x/H4ClBg (English)
• IPA “Secure Programming Course” https://www.ipa.go.jp/security/awareness/vendor/
programming/index.html (Japanese)
29
Secure Coding Guide Documentation Release 2019-12-01
The risks and countermeasures of using Activities differ depending on how that Activity is being used.
In this section, we have classified 4 types of Activities based on how the Activity is being used. You
can find out which type of activity you are supposed to create through the following chart shown below.
Since the secure coding best practice varies according to how the activity is used, we will also explain
about the implementation of the Activity as well.
30
Secure Coding Guide Documentation Release 2019-12-01
Private Activities are Activities which cannot be launched by the other applications and therefore it is
the safest Activity.
When using Activities that are only used within the application (Private Activity), as long as you use
explicit Intents to the class then you do not have to worry about accidently sending it to any other
application. However, there is a risk that a third party application can read an Intent that is used to
start the Activity. Therefore it is necessary to make sure that if you are putting sensitive information
inside an Intent used to start an Activity that you take countermeasures to make sure that it cannot be
read by a malicious third party.
Sample code of how to create a Private Activity is shown below.
Points (Creating an Activity):
1. Do not specify taskAffinity.
2. Do not specify launchMode.
3. Explicitly set the exported attribute to false.
4. Handle the received intent carefully and securely, even though the intent was sent from the same
application.
5. Sensitive information can be sent since it is sending and receiving all within the same application.
To make the Activity private, set the “exported” attribute of the Activity element in the AndroidMani-
fest.xml to false.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.privateactivity" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
31
Secure Coding Guide Documentation Release 2019-12-01
PrivateActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.privateactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.private_activity);
// *** POINT 4 *** Handle the received Intent carefully and securely,
// even though the Intent was sent from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = getIntent().getStringExtra("PARAM");
Toast.makeText(this,
String.format("Received param: \"%s\"", param),
Toast.LENGTH_LONG).show();
}
32
Secure Coding Guide Documentation Release 2019-12-01
Next, we show the sample code for how to use the Private Activity.
Point (Using an Activity):
6. Do not set the FLAG_ACTIVITY_NEW_TASK flag for intents to start an activity.
7. Use the explicit Intents with the class specified to call an activity in the same application.
8. Sensitive information can be sent only by putExtra() since the destination activity is in the same
application1 .
9. Handle the received result data carefully and securely, even though the data comes from an activity
within the same application.
PrivateUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.privateactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity);
}
33
Secure Coding Guide Documentation Release 2019-12-01
startActivityForResult(intent, REQUEST_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE:
String result = data.getStringExtra("RESULT");
// *** POINT 9 *** Handle the received data carefully and securely,
// even though the data comes from an activity within the same
// application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(this,
String.format("Received result: \"%s\"", result),
Toast.LENGTH_LONG).show();
break;
}
}
}
Public Activities are Activities which are supposed to be used by an unspecified large number of appli-
cations. It is necessary to be aware that Public Activities may receive Intents sent from malware.
In addition, when using Public Activities, it is necessary to be aware of the fact that malware can also
receive or read the Intents sent to them.
The sample code to create a Public Activity is shown below.
Points (Creating an Activity):
1. Explicitly set the exported attribute to true.
2. Handle the received intent carefully and securely.
3. When returning a result, do not include sensitive information.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.publicactivity" >
34
Secure Coding Guide Documentation Release 2019-12-01
<!-- Define intent filter to receive an implicit intent for a specified action -->
<intent-filter>
<action android:name="org.jssec.android.activity.MY_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
PublicActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.publicactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// *** POINT 2 *** Handle the received intent carefully and securely.
// Since this is a public activity, it is possible that the sending
// application may be malware.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = getIntent().getStringExtra("PARAM");
Toast.makeText(this,
String.format("Received param: \"%s\"", param),
(continues on next page)
35
Secure Coding Guide Documentation Release 2019-12-01
PublicUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.publicuser;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
36
Secure Coding Guide Documentation Release 2019-12-01
try {
// *** POINT 4 *** Do not send sensitive information.
Intent intent = new Intent("org.jssec.android.activity.MY_ACTION");
intent.putExtra("PARAM", "Not Sensitive Info");
startActivityForResult(intent, REQUEST_CODE);
} catch (ActivityNotFoundException e) {
Toast.makeText(this,
"Target activity not found.", Toast.LENGTH_LONG).show();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// *** POINT 5 *** When receiving a result, handle the data carefully and
// securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (resultCode != RESULT_OK) return;
switch (requestCode) {
case REQUEST_CODE:
String result = data.getStringExtra("RESULT");
Toast.makeText(this,
String.format("Received result: \"%s\"", result),
Toast.LENGTH_LONG).show();
break;
}
}
}
Partner activities are Activities that can only be used by specific applications. They are used between
cooperating partner companies that want to securely share information and functionality.
There is a risk that a third party application can read an Intent that is used to start the Activity.
Therefore it is necessary to make sure that if you are putting sensitive information inside an Intent used
to start an Activity that you take countermeasures to make sure that it cannot be read by a malicious
third party
Sample code for creating a Partner Activity is shown below.
Points (Creating an Activity):
1. Do not specify taskAffinity.
2. Do not specify launchMode.
3. Do not define the intent filter and explicitly set the exported attribute to true.
4. Verify the requesting application’s certificate through a predefined whitelist.
5. Handle the received intent carefully and securely, even though the intent was sent from a partner
application.
6. Only return Information that is granted to be disclosed to a partner application.
Please refer to “4.1.3.2. Validating the Requesting Application” for how to validate an application by a
white list. Also, please refer to “5.2.1.3. How to Verify the Hash Value of an Application’s Certificate”
for how to verify the certificate hash value of a destination application which is specified in the whitelist.
37
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.partneractivity" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".PartnerActivity"
android:exported="true" />
</application>
</manifest>
PartnerActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.partneractivity;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
38
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// *** POINT 5 *** Handle the received intent carefully and securely,
// even though the intent was sent from a partner application.
// Omitted, since this is a sample. Refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(this, "Accessed by Partner App", Toast.LENGTH_LONG).show();
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
(continues on next page)
39
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
(continues on next page)
40
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
41
Secure Coding Guide Documentation Release 2019-12-01
7. Verify if the certificate of the target application has been registered in a whitelist.
8. Do not set the FLAG_ACTIVITY_NEW_TASK flag for the intent that start an activity.
9. Only send information that is granted to be disclosed to a Partner Activity only by putExtra().
10. Use explicit intent to call a Partner Activity.
11. Use startActivityForResult() to call a Partner Activity.
12. Handle the received result data carefully and securely, even though the data comes from a partner
application.
Refer to “4.1.3.2. Validating the Requesting Application” for how to validate applications by white list.
Also please refer to “5.2.1.3. How to Verify the Hash Value of an Application’s Certificate” for how to
verify the certificate hash value of a destination application which is to be specified in a white list.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.partneruser" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="org.jssec.android.activity.partneruser.PartnerUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
PartnerUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.partneruser;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
(continues on next page)
42
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
try {
// *** POINT 8 *** Do not set the FLAG_ACTIVITY_NEW_TASK flag for
// the intent that start an activity.
Intent intent = new Intent();
43
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE:
String result = data.getStringExtra("RESULT");
// *** POINT 12 *** Handle the received data carefully and securely,
// even though the data comes from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(this,
String.format("Received result: \"%s\"", result),
Toast.LENGTH_LONG).show();
break;
}
}
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
(continues on next page)
44
Secure Coding Guide Documentation Release 2019-12-01
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
(continues on next page)
45
Secure Coding Guide Documentation Release 2019-12-01
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
In-house activities are the Activities which are prohibited to be used by applications other than other
in-house applications. They are used in applications developed internally that want to securely share
information and functionality.
There is a risk that a third party application can read an Intent that is used to start the Activity.
Therefore it is necessary to make sure that if you are putting sensitive information inside an Intent used
to start an Activity that you take countermeasures to make sure that it cannot be read by a malicious
third party.
46
Secure Coding Guide Documentation Release 2019-12-01
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="org.jssec.android.activity.inhouseactivity.InhouseActivity"
android:exported="true"
android:permission="org.jssec.android.activity.inhouseactivity.MY_PERMISSION" />
</application>
</manifest>
InhouseActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
(continues on next page)
47
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.activity.inhouseactivity;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Toast.LENGTH_LONG).show();
finish();
return;
}
// *** POINT 7 *** Handle the received intent carefully and securely,
// even though the intent was sent from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = getIntent().getStringExtra("PARAM");
Toast.makeText(this,
String.format("Received param: \"%s\"", param),
(continues on next page)
48
Secure Coding Guide Documentation Release 2019-12-01
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
(continues on next page)
49
Secure Coding Guide Documentation Release 2019-12-01
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
50
Secure Coding Guide Documentation Release 2019-12-01
*** Point9 *** When exporting an APK, sign the APK with the same developer key as the requesting
application.
Fig. 4.1.2: Sign the APK with the same developer key as the requesting application
51
Secure Coding Guide Documentation Release 2019-12-01
16. When exporting an APK, sign the APK with the same developer key as the destination application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.inhouseuser" >
<!-- *** POINT 10 *** Declare to use the in-house signature permission -->
<uses-permission
android:name="org.jssec.android.activity.inhouseactivity.MY_PERMISSION" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="org.jssec.android.activity.inhouseuser.InhouseUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
InhouseUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.inhouseuser;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
52
Secure Coding Guide Documentation Release 2019-12-01
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Toast.LENGTH_LONG).show();
return;
}
try {
Intent intent = new Intent();
53
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE:
String result = data.getStringExtra("RESULT");
// *** POINT 15 *** Handle the received data carefully and securely,
// even though the data came from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(this,
String.format("Received result: \"%s\"", result),
Toast.LENGTH_LONG).show();
break;
}
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
54
Secure Coding Guide Documentation Release 2019-12-01
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
(continues on next page)
55
Secure Coding Guide Documentation Release 2019-12-01
*** Point 16 *** When exporting an APK, sign the APK with the same developer key as the destination
application.
56
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.1.3: Sign the APK with the same developer key as the destination application
Be sure to follow the rules below when creating or sending an Intent to an activity.
1. Activities that are Used Only Internally to the Application Must be Set Private (Required)
2. Do Not Specify taskAffinity (Required)
3. Do Not Specify launchMode (Required)
4. Do Not Set the FLAG_ACTIVITY_NEW_TASK Flag for Intents that Start an Activity (Required)
5. Handling the Received Intent Carefully and Securely (Required)
6. Use an In-house Defined Signature Permission after Verifying that it is Defined by an In-House
Application (Required)
7. When Returning a Result, Pay Attention to the Possibility of Information Leakage of that Result
from the Destination Application (Required)
8. Use the explicit Intents if the destination Activity is predetermined. (Required)
9. Handle the Returned Data from a Requested Activity Carefully and Securely (Required)
10. Verify the Destination Activity if Linking with Another Company’s Application (Required)
11. When Providing an Asset Secondhand, the Asset should be Protected with the Same Level of Pro-
tection (Required)
12. Sending Sensitive Information Should Be Limited as much as possible (Recommended)
4.1.2.1 Activities that are Used Only Internally to the Application Must be Set Private (Required)
Activities which are only used in a single application are not required to be able to receive any Intents
from other applications. Developers often assume that Activities intended to be private will not be
attacked but it is necessary to explicitly make these Activities private in order to stop malicious Intents
from being received.
AndroidManifest.xml
<!-- Private activity -->
<!-- *** 4.1.1.1 - POINT 3 *** Explicitly set the exported attribute to false. -->
<activity
android:name=".PrivateActivity"
android:label="@string/app_name"
android:exported="false" />
57
Secure Coding Guide Documentation Release 2019-12-01
Intent filters should not be set on activities that are only used in a single application. Due to the
characteristics of Intent filters, Due to the characteristics of how Intent filters work, even if you intend to
send an Intent to a Private Activity internally, if you send the Intent through an Intent filter than you
may unintentionally start another Activity. Please see Advanced Topics “4.1.3.1. Combining Exported
Attributes and Intent Filter Settings (For Activities)” for more details.
AndroidManifest.xml(Not recommended)
<!-- Private activity -->
<!-- *** 4.1.1.1 - POINT 3 *** Explicitly set the exported attribute to false. -->
<activity
android:name=".PictureActivity"
android:label="@string/picture_name"
android:exported="false" >
<intent-filter>
<action android:name=”org.jssec.android.activity.OPEN />
</intent-filter>
</activity>
In Android OS, Activities are managed by tasks. Task names are determined by the affinity that the
root Activity has. On the other hand, for Activities other than root Activities, the task to which the
Activity belongs is not determined by the Affinity only, but also depends on the Activity’s launch mode.
Please refer to “4.1.3.4. Root Activity” for more details.
In the default setting, each Activity uses its package name as its affinity. As a result, tasks are allocated
according to application, so all Activities in a single application will belong to the same task. To change
the task allocation, you can make an explicit declaration for the affinity in the AndroidManifest.xml file
or you can set a flag in an Intent sent to an Activity. However, if you change task allocations, there is a
risk that another application could read the Intents sent to Activities belonging to another task.
Be sure not to specify android:taskAffinity in the AndroidManifest.xml file and use the default setting
keeping the affinity as the package name in order to prevent sensitive information inside sent or received
Intents from being read by another application.
Below is an example AndroidManifest.xml file for creating and using Private Activities.
AndroidManifest.xml
<!-- *** 4.1.1.1 - POINT 1 *** Do not specify taskAffinity -->
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
58
Secure Coding Guide Documentation Release 2019-12-01
Please refer to the “Google Android Programming guide”2 , the Google Developer’s API Guide “Tasks
and Back Stack”3 , “4.1.3.3. Reading Intents Sent to an Activity” and “4.1.3.4. Root Activity” for more
details about tasks and affinities.
The Activity launch mode is used to control the settings for creating new tasks and Activity instances
when starting an Activity. By default it is set to “standard”. In the “standard” setting, new instances
are always created when starting an Activity, tasks follow the tasks belonging to the calling Activity, and
it is not possible to create a new task. When a new task is created, it is possible for other applications
to read the contents of the calling Intent so it is required to use the “standard” Activity launch mode
setting when sensitive information is included in an Intent.
The Activity launch mode can be explicitly set in the android:launchMode attribute in the AndroidMan-
ifest.xml file, but because of the reason explained above, this should not be set in the Activity declaration
and the value should be kept as the default “standard”.
AndroidManifest.xml
<!-- *** 4.1.1.1 - POINT 2 *** Do not specify launchMode -->
<activity
android:name=".PrivateUserActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Please refer to “4.1.3.3. Reading Intents Sent to an Activity” and “4.1.3.4. Root Activity.”
4.1.2.4 Do Not Set the FLAG_ACTIVITY_NEW_TASK Flag for Intents that Start an Activity
(Required)
The launch mode of an Activity can be changed when executing startActivity() or startActivityForRe-
sult() and in some cases a new task may be generated. Therefore it is necessary to not change the launch
mode of Activity during execution.
To change the Activity launch mode, set the Intent flags by using setFlags() or addFlags() and use that
Intent as an argument to startActivity() or startActivityForResult(). FLAG_ACTIVITY_NEW_TASK
is the flag used to create a new task. When the FLAG_ACTIVITY_NEW_TASK is set, a new task
will be created if the called Activity does not exist in the background or foreground.
The FLAG_ACTIVITY_MULTIPLE_TASK flag can be set simultaneously with FLAG_ACTIV-
ITY_NEW_TASK. In this case, a new task will always be created. New tasks may be created with
either setting so these should not be set with Intents that handle sensitive information.
Example of sending an intent
2 Author Egawa, Fujii, Asano, Fujita, Yamada, Yamaoka, Sano, Takebata, “Google Android Programming Guide”,
59
Secure Coding Guide Documentation Release 2019-12-01
intent.setClass(this, PrivateActivity.class);
intent.putExtra("PARAM", "Sensitive Info");
startActivityForResult(intent, REQUEST_CODE);
In addition, you may think that there is a way to prevent the contents of an Intent from being read even
if a new task was created by explicitly setting the FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
flag. However, even by using this method, the contents can be read by a third party so you should avoid
any usage of FLAG_ACTIVITY_NEW_TASK.
Please refer to “4.1.3.1. Combining Exported Attributes and Intent Filter Settings (For Activities)”,
“4.1.3.3. Reading Intents Sent to an Activity” and “4.1.3.4. Root Activity”.
Risks differ depending on the types of Activity, but when processing a received Intent data, the first
thing you should do is input validation.
Since Public Activities can receive Intents from untrusted sources, they can be attacked by malware.
On the other hand, Private Activities will never receive any Intents from other applications directly,
but it is possible that a Public Activity in the targeted application may forward a malicious Intent to
a Private Activity so you should not assume that Private Activities cannot receive any malicious input.
Since Partner Activities and In-house Activities also have the risk of a malicious intent being forwarded
to them as well, it is necessary to perform input validation on these Intents as well.
Please refer to “3.2. Handling Input Data Carefully and Securely”.
4.1.2.6 Use an In-house Defined Signature Permission after Verifying that it is Defined by an In-
House Application (Required)
Make sure to protect your in-house Activities by defining an in-house signature permission when creating
the Activity. Since defining a permission in the AndroidManifest.xml file or declaring a permission request
does not provide adequate security, please be sure to refer to “5.2.1.2. How to Communicate Between
In-house Applications with In-house-defined Signature Permission.”
4.1.2.7 When Returning a Result, Pay Attention to the Possibility of Information Leakage of that
Result from the Destination Application (Required)
When you use setResult() to return data, the reliability of the destination application will depend on
the Activity type. When Public Activities are used to return data, the destination may turn out to be
malware in which case that information could be used in a malicious way. For Private and In-house
Activities, there is not much need to worry about data being returned to be used maliciously because
they are being returned to an application you control. Partner Activities are somewhat in the middle.
As above, when returning data from Activities, you need to pay attention to information leakage from
the destination application.
Example of returning data.
60
Secure Coding Guide Documentation Release 2019-12-01
4.1.2.8 Use the explicit Intents if the destination Activity is predetermined. (Required)
When using an Activity by implicit Intents, the Activity in which the Intent gets sent to is determined
by the Android OS. If the Intent is mistakenly sent to malware then Information leakage can occur. On
the other hand, when using an Activity by explicit Intents, only the intended Activity will receive the
Intent so this is much safer.
Unless it is absolutely necessary for the user to determine which application’s Activity the intent should
be sent to, you should use explicit intents and specify the destination in advance.
Using an Activity in the same application by an explicit Intent
However, even when using another application’s Public Activity by explicit Intents, it is possible that the
destination Activity could be malware. This is because even if you limit the destination by package name,
it is still possible that a malicious application can fake the same package name as the real application.
To eliminate this type of risk, it is necessary to consider using a Partner or In-house.
Please refer to “4.1.3.1. Combining Exported Attributes and Intent Filter Settings (For Activities)”.
4.1.2.9 Handle the Returned Data from a Requested Activity Carefully and Securely (Required)
While the risks differ slightly according to what type of Activity you accessing, when processing Intent
data received as a returned value, you always need to perform input validation on the received data.
Public Activities have to accept returned Intents from untrusted sources so when accessing a Public
Activity it is possible that, the returned Intents are actually sent by malware. It is often mistakenly
thought that all returned Intents from a Private Activity are safe because they are originating from
the same application. However, since it is possible that an intent received from an untrusted source is
indirectly forwarded, you should not blindly trust the contents of that Intent. Partner and In-house
Activities have a risk somewhat in the middle of Private and Public Activities. Be sure to input validate
these Activities as well.
Please refer to “3.2. Handling Input Data Carefully and Securely” for more information.
4.1.2.10 Verify the Destination Activity if Linking with Another Company’s Application (Required)
Be sure to sure a whitelist when linking with another company’s application. You can do this by sav-
ing a copy of the company’s certificate hash inside your application and checking it with the certificate
hash of the destination application. This will prevent a malicious application from being able to spoof
61
Secure Coding Guide Documentation Release 2019-12-01
Intents. Please refer to sample code section “4.1.1.3. Creating/Using Partner Activities” for the con-
crete implementation method. For technical details, please refer to “4.1.3.2. Validating the Requesting
Application.”
4.1.2.11 When Providing an Asset Secondhand, the Asset should be Protected with the Same Level
of Protection (Required)
You should not send sensitive information to untrusted parties. Even when you are linking with a specific
application, there is still a chance that you unintentionally send an Intent to a different application or
that a malicious third party can steal your Intents. Please refer to “4.1.3.5. Log Output When using
Activities.”
You need to consider the risk of information leakage when sending sensitive information to an Activity.
You must assume that all data in Intents sent to a Public Activity can be obtained by a malicious third
party. In addition, there is a variety of risks of information leakage when sending Intents to Partner
or In-house Activities as well depending on the implementation. Even when sending data to Private
Activities, there is a risk that the data in the Intent could be leaked through LogCat. Information in
the extras part of the Intent is not output to LogCat so it is best to store sensitive information there.
However, not sending sensitive data in the first place is the only perfect solution to prevent information
leakage therefore you should limit the amount of sensitive information being sent as much as possible.
When it is necessary to send sensitive information, the best practice is to only send to a trusted Activity
and to make sure the information cannot be leaked through LogCat.
In addition, sensitive information should never be sent to the root Activity. Root Activities are Activities
that are called first when a task is created. For example, the Activity which is launched from launcher
is always the root Activity.
Please refer to “4.1.3.3. Reading Intents Sent to an Activity” and “4.1.3.4. Root Activity” for more details
on root Activities.
4.1.3.1 Combining Exported Attributes and Intent Filter Settings (For Activities)
We have explained how to implement the four types of Activities in this guidebook: Private Activities,
Public Activities, Partner Activities, and In-house Activities. The various combinations of permitted
settings for each type of exported attribute defined in the AndroidManifest.xml file and the intent-filter
elements are defined in the table below. Please verify the compatibility of the exported attribute and
intent-filter element with the Activity you are trying to create.
62
Secure Coding Guide Documentation Release 2019-12-01
When the exported attribute of an Activity is left unspecified, the question of whether or not the Ac-
tivity is public is determined by the presence or absence of intent filters for that Activity4 . However, in
this guidebook it is forbidden to set the exported attribute to “unspecified”. In general, as mentioned
previously, it is best to avoid implementations that rely on the default behavior of any given API; more-
over, in cases where explicit methods — such as the exported attribute — exist for enabling important
security-related settings, it is always a good idea to make use of those methods.
The reason why “a defined intent filter and an exported attribute of false” should not be used is that
there is a loophole in Android’s behavior, and because of how Intent filters work, other application’s
Activities can be called unexpectedly. The following two figures below show this explanation. Fig. 4.1.4
is an example of normal behavior in which a Private Activity (Application A) can be called by an implicit
Intent only from the same application. The Intent filter (action = “X”) is defined to work only inside
Application A, so this is the expected behavior.
Fig. 4.1.5 below shows a scenario in which the same Intent filter (action=”X”) is defined in Application
B as well as Application A. Application A is trying to call a Private Activity in the same application
by sending an implicit Intent, but this time a dialogue box asking the user “Complete action using” is
displayed, and the Public Activity B-1 in Application B called by mistake due to the user selection5 . Due
to this loophole, it is possible that sensitive information can be sent to other applications or application
may receive an unexpected retuned value.
4 If any intent filters are defined, the Activity is public; otherwise it is private. For more information, see https:
//developer.android.com/guide/topics/manifest/activity-element.html#exported
5 For terminals running Android 8.0(API Level 26) or later, it has been confirmed that the “Complete action using”
dialog is not displayed and an automatic transition is made to the Public Activity B-1 in Application B in the figure. For
this reason, it should be prohibited to start a private activity with intent filters by an implicit intent.
63
Secure Coding Guide Documentation Release 2019-12-01
As shown above, definitely not make an implicit Intent call to Private Activity using Intent filters because
it allows information to be exchanged with an app that is not the app you want. In addition, we have
verified that this behavior does not depend on the installation order of Application A and Application
B.
Here we explain the technical information about how to implement a Partner Activity. Partner applica-
tions permit that only particular applications which are registered in a whitelist are allowed access and
all other applications are denied. Because applications other than in-house applications also need access
permission, we cannot use signature permissions for access control.
Simply speaking, we want to validate the application trying to use the Partner Activity by checking if
it is registered in a predefined whitelist and allow access if it is and deny access if it is not. Application
validation is done by obtaining the certificate from the application requesting access and comparing its
hash with the one in the whitelist.
Some developers may think that it is sufficient to just compare “the package name” without obtaining
“the certificate”, however, it is easy to spoof the package name of a legitimate application so this is
not a good method to check for authenticity. Arbitrarily assignable values should not be used for
authentication. On the other hand, because only the application developer has the developer key for
signing its certificate, this is a better method for identification. Since the certificate cannot be easily
spoofed, unless a malicious third party can steal the developer key, there is a very small chance that
malicious application will be trusted. While it is possible to store the entire certificate in the whitelist,
it is sufficient to only store the SHA-256 hash value in order to minimize the file size.
There are two restrictions for using this method.
• The requesting application has to use startActivityForResult() instead of startActivity().
• The requesting application can only call from an Activity.
64
Secure Coding Guide Documentation Release 2019-12-01
The second restriction is the restriction imposed as a result of the first restriction, so technically there
is only a single restriction.
This restriction occurs due to the restriction of Activity.getCallingPackage() which gets the package name
of the calling application. Activity.getCallingPackage() returns the package name of source (requesting)
application only in case it is called by startActivityForResult(), but unfortunately, when it is called by
startActivity(), it only returns null. Because of this, when using the method explained here, the source
(requesting) application needs to use startActivityForResult() even if it does not need to obtain a return
value. In addition, startActivityForResult() can be used only in Activity classes, so the source (requester)
is limited to Activities.
PartnerActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.partneractivity;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
65
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// *** POINT 5 *** Handle the received intent carefully and securely,
// even though the intent was sent from a partner application.
// Omitted, since this is a sample. Refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(this, "Accessed by Partner App", Toast.LENGTH_LONG).show();
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
66
Secure Coding Guide Documentation Release 2019-12-01
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
(continues on next page)
67
Secure Coding Guide Documentation Release 2019-12-01
In Android 5.0 (API Level 21) and later, the information retrieved with getRecentTasks() has been
limited to the caller’s own tasks and possibly some other tasks such as home that are known to not be
sensitive. However applications, which support the versions under Android 5.0 (API Level 21), should
protect against leaking sensitive information.
The following describes the contents of this problem occurring in Android 5.0 and earlier version.
Intents that are sent to the task’s root Activity are added to the task history. A root Activity is the first
Activity started in a task. It is possible for any application to read the Intents added to the task history
by using the ActivityManager class.
Sample code for reading the task history from an application is shown below. To browse the task history,
68
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.intent.maliciousactivity" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MaliciousActivity"
android:label="@string/title_activity_main"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
MaliciousActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.intent.maliciousactivity;
import java.util.List;
import java.util.Set;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.malicious_activity);
69
Secure Coding Guide Documentation Release 2019-12-01
You can obtain specified entries of the task history by using the getRecentTasks() function of the Acitiv-
ityManager class. Information about each task is stored in an instance of the ActivityManager.Recent-
TaskInfo class, but Intents that were sent to the task’s root Activity are stored in its member variable
baseIntent. Since the root Activity is the Activity which was started when the task was created, please
be sure to not fulfill the following two conditions when calling an Activity.
• A new task is created when the Activity is called.
• The called Activity is the task’s root Activity which already exists in the background or foreground.
The root Activity is the Activity which is the starting point of a task. In other words, this is the Activity
which was launched when task was created. For example, when the default Activity is launched by
launcher, this Activity will be the root Activity. According to the Android specifications, the contents
of Intents sent to the root Activity can be read from arbitrary applications. So, it is necessary to take
countermeasures not to send sensitive information to the root Activity. In this guidebook, the following
three rules have been made to avoid a called Activity to become root Activity.
• taskAffinity should not be specified.
• launchMode should not be specified.
• The FLAG_ACTIVITY_NEW_TASK flag should not be set in an Intent sent to an Activity.
We consider the situations that an Activity can become the root Activity below. A called Activity
becoming a root Activity depends on the following.
• The launch mode of the called Activity
• The task of a called Activity and its launch mode
70
Secure Coding Guide Documentation Release 2019-12-01
First of all, let me explain “the Launch mode of called Activity”. Launch mode of Activity can be
set by writing android:launchMode in AndroidManifest.xml. When it’s not written, it’s considered as
“standard”. In addition, launch mode can be also changed by a flag to set to Intent. Flag “FLAG_AC-
TIVITY_NEW_TASK” launches Activity by “singleTask” mode.
The launch modes that can be specified are as per below. I’ll explain about the relation with the root
activity, mainly.
standard
Activity which is called by this mode won’t be root, and it belongs to the caller side task. Every time
it’s called, Instance of Activity is to be generated.
singleTop
This launch mode is the same as “standard”, except for that the instance is not generated when launching
an Activity which is displayed in most front side of foreground task.
singleTask
This launch mode determines the task to which the activity would be belonging by Affinity value. When
task which is matched with Activity’s affinity doesn’t exist either in background or in foreground, a new
task is generated along with Activity’s instance. When task exists, neither of them is to be generated.
In the former one, the launched Activity’s Instance becomes root.
singleInstance
Same as “singleTask”, but following point is different. Only root Activity can belongs to the newly
generated task. So instance of Activity which was launched by this mode is always root activity. Now,
we need to pay attention to the case that the class name of called Activity and the class name of Activity
which is included in a task are different although the task which has the same name of called Activity’s
affinity already exists.
From as above, we can get to know that Activity which was launched by “singleTask” or “singleInstance”
has the possibility to become root. In order to secure the application’s safety, it should not be launched
by these modes.
Next, I’ll explain about “Task of the called Activity and its launch mode”. Even if Activity is called by
“standard” mode, it becomes root Activity in some cases depends on the task state to which Activity
belongs.
For example, think about the case that called Activity’s task has being run already in background.
The problem here is the case that Activity Instance of the task is launched by “singleInstance”. When
the affinity of Activity which was called by “standard” is same with the task, new task is to be generated
by the restriction of existing “singleInstance” Activity. However, when class name of each Activity is
same, task is not generated and existing activity Instance is to be used. In any cases, that called Activity
becomes root Activity.
As per above, the conditions that root Activity is called are complicated, for example it depends on the
state of execution. So when developing applications, it’s better to contrive that Activity is called by
“standard”.
As an example of that Intent which is sent to Private Activity is read out form other application, the
sample code shows the case that caller side Activity of private Activity is launched by “singleInstance”
mode. In this sample code, private activity is launched by “standard” mode, but this private Activity
becomes root Activity of new task due the “singleInstance” condition of caller side Activity. At this
71
Secure Coding Guide Documentation Release 2019-12-01
moment, sensitive information that is sent to Private Activity is recorded task history, so it can be read
out from other applications. FYI, both caller side Activity and Private Activity have the same affinity.
AndroidManifest.xml(Not recommended)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.activity.singleinstanceactivity" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
PrivateActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.singleinstanceactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
(continues on next page)
72
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.private_activity);
In caller side of Private Activity, Private Activity is launched by “standard” mode without setting flag
to Intent.
PrivateUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.activity.singleinstanceactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(continues on next page)
73
Secure Coding Guide Documentation Release 2019-12-01
startActivityForResult(intent, REQUEST_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE:
String result = data.getStringExtra("RESULT");
When using an activity, the contents of intent are output to LogCat by ActivityManager. The following
contents are to be output to LogCat, so in this case, sensitive information should not be included here.
• Destination Package name
• Destination Class name
• URI which is set by Intent#setData()
For example, when an application sent mails, the mail address is unfortunately outputted to LogCat if
the application would specify the mail address to URI. So, better to send by setting Extras.
When sending a mail as below, mail address is shown to the logCat.
MainActivity.java
// URI is output to the LogCat.
Uri uri = Uri.parse("mailto:test@gmail.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
MainActivity.java
// Contents which was set to Extra, is not output to the LogCat.
Uri uri = Uri.parse("mailto:");
(continues on next page)
74
Secure Coding Guide Documentation Release 2019-12-01
However, there are cases where other applications can read the Extras data of intent using Activity-
Manager#getRecentTasks(). Please refer to “4.1.2.2. Do Not Specify taskAffinity (Required)”, “4.1.2.3.
Do Not Specify launchMode (Required)” and “4.1.2.4. Do Not Set the FLAG_ACTIVITY_NEW_TASK
Flag for Intents that Start an Activity (Required)”.
When a class derived from PreferenceActivity is a public Activity, a problem known as Fragment Injec-
tion 6 may arise. To prevent this problem from arising, it is necessary to override PreferenceActivity.Is-
ValidFragment() and check the validity of its arguments to ensure that the Activity does not handle any
Fragments without intention. (For more on the safety of input data, see Section “3.2. Handling Input
Data Carefully and Securely”.)
Below we show a sample in which IsValidFragment() has been overridden. Note that, if the source code
has been obfuscated, class names and the results of parameter-value comparisons may change. In this
case it is necessary to pursue alternative countermeasures.
Example of an overridden isValidFragment() method
The Autofill framework was added in Android 8.0 (API Level 26). Using this framework allows apps
to store information entered by users—such as user names, passwords, addresses, phone numbers, and
credit cards—and subsequently to retrieve this information as necessary to allow the app to fill in forms
automatically. This is a convenient mechanism that reduces data-entry burdens for users; however,
because it allows a given app to pass sensitive information such as passwords and credit cards to other
apps, it must be handled with appropriate care.
2 components
In what follows, we provide an overview of the two components7 registered by the Autofill framework.
• Apps eligible for Autofill (user apps):
6 For more information on Fragment Injection, consult this URL: https://securityintelligence.com/
new-vulnerability-android-framework-fragment-injection/
7 The “user app” and the “Autofill service” may belong to the same package (the same APK file) or to different packages.
75
Secure Coding Guide Documentation Release 2019-12-01
– Pass view information (text and attributes) to Autofill service; receive information from Aut-
ofill service as needed to auto-fill forms.
– All apps that have Activities are user apps (when in the foreground).
– It is possible for all Views of all user apps to be eligible for Autofill. It is also possible to
explicitly specify that any given individual view should be ineligible for Autofill.
– It is also possible to restrict an app’s use of Autofill to the Autofill service within the same
package.
• Services that provide Autofill (Autofill services):
– Save View information passed by an app (requires user permission); provide an app with
information needed for Autofill in a View (candidate lists).
– The Views eligible for this information saving are determined by the Autofill service. (Within
the Autofill framework, by default information on all Views contained in an Activity are passed
to the Autofill service.)
– It is also possible to construct Autofill services provided by third parties.
– It is possible for several to be present within a single terminal with only the service selected
by the user via “Settings” enabled (“None” is also a possible selection.)
– It also possible for a Service to provide a UI to validate users via password entry or other
mechanisms to protect the security of the user information handled.
Fig. 4.1.6 is a flowchart illustrating the procedural flow of interactions among Autofill-related components
during Autofill. When triggered by events such as motion of the focus in a user app’s View, information
on that View (primarily the parent-child relationships and various attributes of the View) is passed via
the Autofill framework to the Autofill service selected within “Settings”. Based on the data it receives,
the Autofill service fetches from a database the information (candidate lists) needed for Autofill, then
returns this to the framework. The framework displays a candidate list to the user, and the app carries
out the Autofill operation using the data selected by the user.
Next, Fig. 4.1.7 is a flowchart illustrating the procedural flow for saving user data via Autofill. Upon a
triggering event such as when AutofillManager#commit() is called or when an Activity is unfocused, if
any Autofilled values for the View have been modified and the user has granted permission via the Save
Permission dialog box displayed by the Autofill framework, information on the View (including text) is
passed via the Autofill framework to the Autofill service selected via “Settings”, and the Autofill service
stores information in the database to complete the procedural sequence.
76
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.1.7: Procedural flow among components for saving user data
As noted in the section “Overview of the framework” above, the security model adopted by the Autofill
framework is premised on the assumption that the user configures the “Settings” to select secure Autofill
services and makes appropriate decisions regarding which data to pass to which Autofill service when
storing data.
However, if a user unwittingly selects a non-secure Autofill service, there is a possibility that the user
may permit the storage of sensitive information that should not be passed to the Autofill service. In
what follows we discuss the damage that could result in such a scenario.
When saving information, if the user selects an Autofill service and grants it permission via the Save
Permission dialog box, information for all Views contained in the Activity currently displayed by the app
in use may be passed to the Autofill service. If the Autofill service is malware, or if other security issues
arise—for example, if View information is stored by the Autofill service on an external storage medium
or on an insecure cloud service—this could create the risk that information handled by the app might
be leaked.
On the other hand, during Autofill, if the user has selected a piece of malware as the Autofill service,
values transmitted by the malware may be entered as input. At this point, if the security of the data
input is not adequately validated by the app or by the cloud services to which the app sends data, risks
of information leakage and/or termination of the app or the service may arise.
Note that, as discussed above in the section “2 components”, apps with Activities are automatically
eligible for Autofill, and thus all developers of apps with Activities must take the risks described above
into account when designing and implementing apps. In what follows we will present countermeasures
to mitigate the risks described above we recommend that these be adopted as appropriate based on a
consideration of the countermeasures required by an app—referring to “3.1.3. Asset Classification and
Protective Countermeasures” and other relevant resources.
As discussed above, security within the Autofill framework is ultimately guaranteed only at the user’s
discretion. For this reason, the range of countermeasures available to apps is somewhat limited. However,
there is one way to mitigate the concerns described above: Setting the importantForAutofill attribute
for a view to “no” ensures that no View information is passed to the Autofill service (i.e. the View is
made ineligible for Autofill), even if the user cannot make appropriate selections or permissions (such as
77
Secure Coding Guide Documentation Release 2019-12-01
“no” No Yes
IMPORTANT_FOR_AUTOFILL_NO
“noExcludeDescendants” No No
IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DE-
SCENDANTS
“yesExcludeDescendants” Yes No
IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DE-
SCENDANTS
DisableForOtherServiceActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
(continues on next page)
8 Even after taking this step, in some cases it may not be possible to avoid the security concerns described above—for
example, if the user intentionally uses Autofill. Implementing the steps described in “Steps to mitigate risk: 2” will improve
security in these cases.
9 Determined by Autofill framework
78
Secure Coding Guide Documentation Release 2019-12-01
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
import android.widget.TextView;
import org.jssec.android.autofillframework.R;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.disable_for_other_service_activity);
mUsernameEditText = (EditText)findViewById(R.id.field_username);
mPasswordEditText = (EditText)findViewById(R.id.field_password);
findViewById(R.id.button_login).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
findViewById(R.id.button_clear).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
resetFields();
}
});
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
updateAutofillStatus();
View rootView = this.getWindow().getDecorView();
if (!mIsAutofillEnabled) {
//If not using Autofill service within the same package,
// make all Views ineligible for Autofill
(continues on next page)
79
Secure Coding Guide Documentation Release 2019-12-01
finish();
}
mIsAutofillEnabled = mgr.hasEnabledAutofillServices();
Even in cases where an app has implemented the steps described in the previous section (“Steps to mitigate
risk: 1”), the user can forcibly enable the use of Autofill by long-pressing the View, displaying the floating
toolbar or a similar control interface, and selecting “Automatic input”. In this case, information for all
Views—including Views for which the importantForAutofill attribute has been set to “no,” or for which
similar steps have been taken—will be passed to the Autofill service.
It is possible to avoid the risk of information leakage even in circumstances such as these by deleting the
“Automatic Input” option from the floating-toolbar menu and other control interfaces; this step is to be
carried out in addition to the procedures described in “Steps to mitigate risk: 1”.
Sample code for this purpose is shown below.
80
Secure Coding Guide Documentation Release 2019-12-01
DisableAutofillActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.autofillframework.autofillapp;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.widget.EditText;
import org.jssec.android.autofillframework.R;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.disable_autofill_activity);
findViewById(R.id.button_login).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
findViewById(R.id.button_clear).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
resetFields();
}
});
81
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
removeAutofillFromMenu(menu);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
};
void setMenu() {
if (mActionModeCallback == null) {
return;
}
//Register callback for all editable TextViews contained in Activity
mUsernameEditText
.setCustomInsertionActionModeCallback(mActionModeCallback);
mPasswordEditText
.setCustomInsertionActionModeCallback(mActionModeCallback);
}
82
Secure Coding Guide Documentation Release 2019-12-01
EnableOnlyWhitelistedServiceActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.autofillframework.autofillapp;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.autofillframework.R;
83
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.enable_only_whitelisted_service_activity);
mUsernameEditText = (EditText)findViewById(R.id.field_username);
mPasswordEditText = (EditText)findViewById(R.id.field_password);
findViewById(R.id.button_login).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
findViewById(R.id.button_clear).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
resetFields();
}
});
// Because the floating-toolbar is not supported for this Activity,
// Autofill may be used by selecting "Automatic Input"
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
updateAutofillStatus();
View rootView = this.getWindow().getDecorView();
if (!mIsAutofillEnabled) {
// If the Autofill Service is not on white list,
// exclude all Views from the target of Autofill
rootView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_
˓→DESCENDANTS);
} else {
(continues on next page)
84
Secure Coding Guide Documentation Release 2019-12-01
finish();
}
85
Secure Coding Guide Documentation Release 2019-12-01
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
86
Secure Coding Guide Documentation Release 2019-12-01
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
87
Secure Coding Guide Documentation Release 2019-12-01
Creating Broadcast Receiver is required to receive Broadcast. Risks and countermeasures of using Broad-
cast Receiver differ depending on the type of the received Broadcast.
You can find your Broadcast Receiver in the following judgment flow. The receiving applications can-
not check the package names of Broadcast-sending applications that are necessary for linking with the
partners. As a result, Broadcast Receiver for the partners cannot be created.
In addition, Broadcast Receiver can be divided into 2 types based on the definition methods, Static
Broadcast Receiver and Dynamic Broadcast Receiver. The differences between them can be found in
the following figure. In the sample code, an implementation method for each type is shown. The
implementation method for sending applications is also described because the countermeasure for sending
information is determined depending on the receivers.
88
Secure Coding Guide Documentation Release 2019-12-01
Private Broadcast Receiver is the safest Broadcast Receiver because only Broadcasts sent from within
the application can be received. Dynamic Broadcast Receiver cannot be registered as Private, so Private
Broadcast Receiver consists of only Static Broadcast Receivers.
Points (Receiving Broadcasts):
1. Explicitly set the exported attribute to false.
2. Handle the received intent carefully and securely, even though the intent was sent from within the
same application.
3. Sensitive information can be sent as the returned results since the requests come from within the
same application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.broadcast.privatereceiver" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
(continues on next page)
10 As exceptions to this rule, some implicit Broadcast Intents sent by the system may use Broadcast Receivers. For more
information, consult the following URL. https://developer.android.com/guide/components/broadcast-exceptions.html
89
Secure Coding Guide Documentation Release 2019-12-01
PrivateReceiver.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.privatereceiver;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
@Override
public void onReceive(Context context, Intent intent) {
// *** POINT 2 *** Handle the received intent carefully and securely,
// even though the intent was sent from within the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = intent.getStringExtra("PARAM");
Toast.makeText(context,
String.format("Received param: \"%s\"", param),
Toast.LENGTH_SHORT).show();
The sample code for sending Broadcasts to private Broadcast Receiver is shown below.
Points (Sending Broadcasts):
90
Secure Coding Guide Documentation Release 2019-12-01
4. Use the explicit Intent with class specified to call a receiver within the same application.
5. Sensitive information can be sent since the destination Receiver is within the same application.
6. Handle the received result data carefully and securely, even though the data came from the Receiver
within the same application.
PrivateSenderActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.privatereceiver;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
// *** POINT 5 *** Sensitive information can be sent since the destination
// Receiver is within the same application.
intent.putExtra("PARAM", "Sensitive Info from Sender");
sendBroadcast(intent);
}
// *** POINT 5 *** Sensitive information can be sent since the destination
// Receiver is within the same application.
intent.putExtra("PARAM", "Sensitive Info from Sender");
sendOrderedBroadcast(intent, null, mResultReceiver, null, 0, null, null);
}
// *** POINT 6 *** Handle the received result data carefully and
(continues on next page)
91
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
Public Broadcast Receiver is the Broadcast Receiver that can receive Broadcasts from unspecified large
number of applications, so it’s necessary to pay attention that it may receive Broadcasts from malware.
Points (Receiving Broadcasts):
1. Explicitly set the exported attribute to true.
2. Handle the received Intent carefully and securely.
3. When returning a result, do not include sensitive information.
Public Receiver which is the sample code for public Broadcast Receiver can be used both in static
Broadcast Receiver and Dynamic Broadcast Receiver.
PublicReceiver.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.publicreceiver;
import android.app.Activity;
(continues on next page)
92
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onReceive(Context context, Intent intent) {
// *** POINT 2 *** Handle the received Intent carefully and securely.
// Since this is a public broadcast receiver, the requesting application
// may be malware.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (MY_BROADCAST_PUBLIC.equals(intent.getAction())) {
String param = intent.getStringExtra("PARAM");
Toast.makeText(context,
String.format("%s:\nReceived param: \"%s\"",
getName(), param),
Toast.LENGTH_SHORT).show();
}
Static Broadcast Receive is defined in AndroidManifest.xml. Note with caution that—depending on the
terminal version—reception of implicit Broadcast Intents may be restricted, as in Table 4.2.2.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.broadcast.publicreceiver" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
93
Secure Coding Guide Documentation Release 2019-12-01
<service
android:name=".DynamicReceiverService"
android:exported="false" />
<activity
android:name=".PublicReceiverActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
DynamicReceiverService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.publicreceiver;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.widget.Toast;
94
Secure Coding Guide Documentation Release 2019-12-01
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
@Override
public void onDestroy() {
super.onDestroy();
PublicReceiverActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.publicreceiver;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
(continues on next page)
95
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Next, the sample code for sending Broadcasts to public Broadcast Receiver is shown. When sending
Broadcasts to public Broadcast Receiver, it’s necessary to pay attention that Broadcasts can be received
by malware.
Points (Sending Broadcasts):
4. Do not send sensitive information.
5. When receiving a result, handle the result data carefully and securely.
PublicSenderActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.publicsender;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
96
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 5 *** When receiving a result, handle the result data
// carefully and securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String data = getResultData();
PublicSenderActivity.this
.logLine(String.format("Received result: \"%s\"", data));
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
97
Secure Coding Guide Documentation Release 2019-12-01
In-house Broadcast Receiver is the Broadcast Receiver that will never receive any Broadcasts sent from
other than in-house applications. It consists of several in-house applications, and it’s used to protect the
information or functions that in-house application handles.
Points (Receiving Broadcasts):
1. Define an in-house signature permission to receive Broadcasts.
2. Declare to use the in-house signature permission to receive results.
3. Explicitly set the exported attribute to true.
4. Require the in-house signature permission by the Static Broadcast Receiver definition.
5. Require the in-house signature permission to register Dynamic Broadcast Receiver.
6. Verify that the in-house signature permission is defined by an in-house application.
7. Handle the received intent carefully and securely, even though the Broadcast was sent from an
in-house application.
8. Sensitive information can be returned since the requesting application is in-house.
9. When Exporting an APK, sign the APK with the same developer key as the sending application.
In-house Receiver which is a sample code of in-house Broadcast Receiver is to be used both in Static
Broadcast Receiver and Dynamic Broadcast Receiver.
InhouseReceiver.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.inhousereceiver;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
98
Secure Coding Guide Documentation Release 2019-12-01
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public void onReceive(Context context, Intent intent) {
return;
}
// *** POINT 7 *** Handle the received intent carefully and securely,
// even though the Broadcast was sent from an in-house application..
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (MY_BROADCAST_INHOUSE.equals(intent.getAction())) {
String param = intent.getStringExtra("PARAM");
Toast.makeText(context, String.format("%s:\nReceived param: \"%s\"", getName(),␣
˓→param), Toast.LENGTH_SHORT).show();
99
Secure Coding Guide Documentation Release 2019-12-01
on the terminal version—reception of implicit Broadcast Intents may be restricted, as in Table 4.2.2.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.broadcast.inhousereceiver" >
<!-- *** POINT 1 *** Define an in-house signature permission to receive Broadcasts -->
<permission
android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION"
android:protectionLevel="signature" />
<!-- *** POINT 2 *** Declare to use the in-house signature permission to receive results. -->
<uses-permission
android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<!-- *** POINT 3 *** Explicitly set the exported attribute to true. -->
<!-- *** POINT 4 *** Require the in-house signature permission by the Static Broadcast␣
˓→Receiver definition. -->
<receiver
android:name=".InhouseReceiver"
android:permission="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION"
android:exported="true">
<intent-filter>
<action android:name="org.jssec.android.broadcast.MY_BROADCAST_INHOUSE" />
</intent-filter>
</receiver>
<service
android:name=".DynamicReceiverService"
android:exported="false" />
<activity
android:name=".InhouseReceiverActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
InhouseReceiverActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
(continues on next page)
100
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.broadcast.inhousereceiver;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
DynamicReceiverService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.inhousereceiver;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
(continues on next page)
101
Secure Coding Guide Documentation Release 2019-12-01
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this,
"Registered Dynamic Broadcast Receiver.",
Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
mReceiver = null;
Toast.makeText(this,
"Unregistered Dynamic Broadcast Receiver.",
Toast.LENGTH_SHORT).show();
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
(continues on next page)
102
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
(continues on next page)
103
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
*** Point 9 *** When exporting an APK, sign the APK with the same developer key as the sending
application.
104
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.2.2: Sign the APK with the same developer key as the sending application
Next, the sample code for sending Broadcasts to in-house Broadcast Receiver is shown.
Points (Sending Broadcasts):
10. Define an in-house signature permission to receive results.
11. Declare to use the in-house signature permission to receive Broadcasts.
12. Verify that the in-house signature permission is defined by an in-house application.
13. Sensitive information can be returned since the requesting application is the in-house one.
14. Require the in-house signature permission of Receivers.
15. Handle the received result data carefully and securely.
16. When exporting an APK, sign the APK with the same developer key as the destination application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.broadcast.inhousesender" >
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<!-- *** POINT 10 *** Define an in-house signature permission to receive results. -->
<permission
android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION"
android:protectionLevel="signature" />
<!-- *** POINT 11 *** Declare to use the in-house signature permission to receive Broadcasts.
˓→ -->
<uses-permission
android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name="org.jssec.android.broadcast.inhousesender.InhouseSenderActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
(continues on next page)
105
Secure Coding Guide Documentation Release 2019-12-01
InhouseSenderActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.broadcast.inhousesender;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
106
Secure Coding Guide Documentation Release 2019-12-01
return;
}
return;
}
// *** POINT 15 *** Handle the received result data carefully and
// securely, even though the data came from an in-house
// application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String data = getResultData();
InhouseSenderActivity.this
.logLine(String.format("Received result: \"%s\"", data));
}
};
107
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
(continues on next page)
108
Secure Coding Guide Documentation Release 2019-12-01
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
109
Secure Coding Guide Documentation Release 2019-12-01
*** Point 16 *** When exporting an APK, sign the APK with the same developer key as the destination
application.
Fig. 4.2.3: Sign the APK with the same developer key as the destination application
110
Secure Coding Guide Documentation Release 2019-12-01
4.2.2.1 Broadcast Receiver that Is Used Only in an Application Must Be Set as Private (Required)
Broadcast Receiver which is used only in the application should be set as private to avoid from receiving
any Broadcasts from other applications unexpectedly. It will prevent the application function abuse or
the abnormal behaviors.
Receiver used only within the same application should not be designed with setting Intent-filter. Because
of the Intent-filter characteristics, a public Receiver of other application may be called unexpectedly by
calling through Intent-filter even though a private Receiver within the same application is to be called.
AndroidManifest.xml(Not recoomended)
<!-- Private Broadcast Receiver -->
<!-- *** 4.2.1.1 - POINT 1 *** Explicitly set the exported attribute to false. -->
<receiver android:name=".PrivateReceiver"
android:exported="false" >
<intent-filter>
<action android:name="org.jssec.android.broadcast.MY_ACTION" />
</intent-filter>
</receiver>
Please refer to “4.2.3.1. Combinations of the exported Attribute and the Intent-filter setting (For Re-
ceiver).”
Though risks are different depending on the types of the Broadcast Receiver, firstly verify the safety of
Intent when processing received Intent data.
Since Public Broadcast Receiver receives the Intents from unspecified large number of applications, it
may receive malware’s attacking Intents. Private Broadcast Receiver will never receive any Intent from
other applications directly, but Intent data which a public Component received from other applications
may be forwarded to Private Broadcast Receiver. So don’t think that the received Intent is totally safe
without any qualification. In-house Broadcast Receivers have some degree of the risks, so it also needs
to verify the safety of the received Intents.
Please refer to “3.2. Handling Input Data Carefully and Securely”
4.2.2.3 Use the In-house Defined Signature Permission after Verifying that it’s Defined by an In-
house Application (Required)
In-house Broadcast Receiver which receives only Broadcasts sent by an In-house application should be
protected by in-house-defined Signature Permission. Permission definition/Permission request declara-
tions in AndroidManifest.xml are not enough to protecting, so please refer to “5.2.1.2. How to Communi-
cate Between In-house Applications with In-house-defined Signature Permission.” ending Broadcasts by
specifying in-house-defined Signature Permission to receiverPermission parameter requires verification in
the same way.
111
Secure Coding Guide Documentation Release 2019-12-01
4.2.2.4 When Returning a Result Information, Pay Attention to the Result Information Leakage
from the Destination Application (Required)
The Reliability of the application which returns result information by setResult() varies depending on
the types of the Broadcast Receiver. In case of Public Broadcast Receiver, the destination application
may be malware, and there may be a risk that the result information is used maliciously. In case of
Private Broadcast Receiver and In-house Broadcast Receiver, the result destination is In-house developed
application, so no need to mind the result information handling.
Need to pay attention to the result information leakage from the destination application when result
information is returned from Broadcast Receivers as above.
4.2.2.5 When Sending Sensitive Information with a Broadcast, Limit the Receivable Receiver (Re-
quired)
Broadcast is the created system to broadcast information to unspecified large number of applications or
notify them of the timing at once. So, broadcasting sensitive information requires the careful designing
for preventing the illicit obtainment of the information by malware.
For broadcasting sensitive information, only reliable Broadcast Receiver can receive it, and other Broad-
cast Receivers cannot. The following are some examples of Broadcast sending methods.
• The method is to fix the address by Broadcast-sending with an explicit Intent for sending Broadcasts
to the intended reliable Broadcast Receivers only. There are 2 patterns in this method.
– When it’s addressed to a Broadcast Receiver within the same application, specify the address
by Intent#setClass(Context, Class). Refer to sample code section “4.2.1.1. Private Broadcast
Receiver - Receiving/Sending Broadcasts” for the concrete code.
– When it’s addressed to a Broadcast Receiver in other applications, specify the address by
Intent#setClassName(String, String). Confirm the permitted application by comparing the
developer key of the APK signature in the destination package with the white list to send
Broadcasts. Actually the following method of using implicit Intents is more practical.
• The Method is to send Broadcasts by specifying in-house-defined Signature Permission to receiver-
Permission parameter and make the reliable Broadcast Receiver declare to use this Signature Per-
mission. Refer to the sample code section “4.2.1.3. In-house Broadcast Receiver - Receiving/Sending
Broadcasts” for the concrete code. In addition, implementing this Broadcast-sending method needs
to apply the rule “4.2.2.3. Use the In-house Defined Signature Permission after Verifying that it’s
Defined by an In-house Application (Required).”
4.2.2.6 Sensitive Information Must Not Be Included in the Sticky Broadcast (Required)
Usually, the Broadcasts will be disappeared when they are processed to be received by the available
Broadcast Receivers. On the other hand, Sticky Broadcasts (hereafter, Sticky Broadcasts including Sticky
Ordered Broadcasts), will not be disappeared from the system even when they processed to be received
by the available Broadcast Receivers and will be able to be received by registerReceiver(). When Sticky
Broadcast becomes unnecessary, it can be deleted anytime arbitrarily with removeStickyBroadcast().
As it’s presupposed that Sticky Broadcast is used by the implicit Intent. Broadcasts with specified
receiverPermission Parameter cannot be sent. For this reason, information sent via Sticky Broadcasts
can be accessed by multiple unspecified apps — including malware — and thus sensitive information
must not be sent in this way. Note that Sticky Broadcast is deprecated in Android 5.0 (API Level 21).
4.2.2.7 Pay Attention that the Ordered Broadcast without Specifying the receiverPermission May
Not Be Delivered (Required)
Ordered Broadcast without specified receiverPermission Parameter can be received by unspecified large
number of applications including malware. Ordered Broadcast is used to receive the returned information
112
Secure Coding Guide Documentation Release 2019-12-01
from Receiver, and to make several Receivers execute processing one by one. Broadcasts are sent to the
Receivers in order of priority. So if the high- priority malware receives Broadcast first and executes
abortBroadcast(), Broadcasts won’t be delivered to the following Receivers.
4.2.2.8 Handle the Returned Result Data from the Broadcast Receiver Carefully and Securely (Re-
quired)
Basically the result data should be processed safely considering the possibility that received results may
be the attacking data though the risks vary depending on the types of the Broadcast Receiver which has
returned the result data.
When sender (source) Broadcast Receiver is public Broadcast Receiver, it receives the returned data
from unspecified large number of applications. So it may also receive malware’s attacking data. When
sender (source) Broadcast Receiver is private Broadcast Receiver, it seems no risk. However the data
received by other applications may be forwarded as result data indirectly. So the result data should not
be considered as safe without any qualification. When sender (source) Broadcast Receiver is In-house
Broadcast Receiver, it has some degree of the risks. So it should be processed in a safe way considering
the possibility that the result data may be an attacking data.
Please refer to “3.2. Handling Input Data Carefully and Securely”
4.2.2.9 When Providing an Asset Secondarily, the Asset should be protected with the Same Pro-
tection Level (Required)
When information or function assets protected by Permission are provided to other applications secon-
darily, it’s necessary to keep the protection standard by claiming the same Permission of the destination
application. In the Android Permission security models, privileges are managed only for the direct ac-
cess to the protected assets from applications. Because of the characteristics, acquired assets may be
provided to other applications without claiming Permission which is necessary for protection. This is
actually same as re-delegating Permission, as it is called, Permission re-delegation problem. Please refer
to “5.2.3.4. Permission Re-delegation Problem.”
4.2.3.1 Combinations of the exported Attribute and the Intent-filter setting (For Receiver)
Table 4.2.3 represents the permitted combination of export settings and Intent-filter elements when
implementing Receivers. The reason why the usage of “exported=”false” with Intent-filter definition” is
principally prohibited, is described below.
When the exported attribute of a Receiver is left unspecified, the question of whether or not the Re-
ceiver is public is determined by the presence or absence of intent filters for that Receiver11 . However,
in this guidebook it is forbidden to set the exported attribute to “unspecified”. In general, as mentioned
previously, it is best to avoid implementations that rely on the default behavior of any given API; more-
over, in cases where explicit methods — such as the exported attribute — exist for enabling important
security-related settings, it is always a good idea to make use of those methods.
11 If any intent filters are defined then the Receiver is public; otherwise it is private. For more information, see https:
//developer.android.com/guide/topics/manifest/receiver-element.html#exported
113
Secure Coding Guide Documentation Release 2019-12-01
Public Receivers in other applications may be called unexpectedly even though Broadcasts are sent to
the private Receivers within the same applications. This is the reason why specifying exported=”false”
with Intent-filter definition is prohibited. The following 2 figures show how the unexpected calls occur.
Fig. 4.2.4 is an example of the normal behaviors which a private Receiver (application A) can be called
by implicit Intent only within the same application. Intent-filter (in the figure, action=”X”) is defined
only in application A, so this is the expected behavior.
Fig. 4.2.5 is an example that Intent-filter (see action=”X” in the figure) is defined in the application B
as well as in the application A. First of all, when another application (application C) sends Broadcasts
by implicit Intent, they are not received by a private Receiver (A-1) side. So there won’t be any security
problem. (See the orange arrow marks in the Figure.)
From security point of view, the problem is application A’s call to the private Receiver within the same
application. When the application A broadcasts implicit Intent, not only Private Receiver within the
same application, but also public Receiver (B-1) with the same Intent-filter definition can also receive
the Intent. (Red arrow marks in the Figure). In this case, sensitive information may be sent from the
application A to B. When the application B is malware, it will cause the leakage of sensitive information.
When the Broadcast is Ordered Broadcast, it may receive the unexpected result information.
114
Secure Coding Guide Documentation Release 2019-12-01
However, “exported=”false” with Intent-filter definition” should be used when Broadcast Receiver to re-
ceive only Broadcast Intent sent by the system is implemented. Other combination should not be used.
This is based on the fact that Broadcast Intent sent by the system can be received by exported=”false”.
If other applications send Intent which has same ACTION with Broadcast Intent sent by system, it
may cause an unexpected behavior by receiving it. However, this can be prevented by specifying ex-
ported=”false”.
It is important to note carefully that a Broadcast Receiver defined statically in AndroidManifest.xml will
not be automatically enabled upon installation12 . Apps are able to receive Broadcasts only after they
have been launched the first time; thus, it is not possible to use the receipt of a Broadcast after installation
as a trigger to initiate operations. However, if the Intent.FLAG_INCLUDE_STOPPED_PACKAGES
flag set when sending a Broadcast, that Broadcast will be received even by apps that have not yet been
launched for the first time.
4.2.3.3 Private Broadcast Receiver Can Receive the Broadcast that Was Sent by the Same UID
Application
Same UID can be provided to several applications. Even if it’s private Broadcast Receiver, the Broadcasts
sent from the same UID application can be received.
However, it won’t be a security problem. Since it’s guaranteed that applications with the same UID have
the consistent developer keys for signing APK. It means that what private Broadcast Receiver receives
is only the Broadcast sent from In-house applications.
12 In versions prior to Android 3.0, Receivers were registered automatically simply by installing apps.
115
Secure Coding Guide Documentation Release 2019-12-01
Regarding Broadcasts, there are 4 types based on the combination of whether it’s Ordered or not, and
Sticky or not. Based on Broadcast sending methods, a type of Broadcast to send is determined. Note
that Sticky Broadcast is deprecated in Android 5.0 (API Level 21).
From the Broadcast characteristic behavior point of view, above table is conversely arranged in the
following one.
116
Secure Coding Guide Documentation Release 2019-12-01
Basically sending/receiving Broadcasts is not output to LogCat. However, the error log will be output
when lacking Permission causes errors in receiver/sender side. Intent information sent by Broadcast is
included in the error log, so after an error occurs it’s necessary to pay attention that Intent information
is displayed in LogCat when Broadcast is sent.
Erorr of lacking Permission in sender side
4.2.3.6 Items to Keep in Mind When Placing an App Shortcut on the Home Screen
In what follows we discuss a number of items to keep in mind when creating a shortcut button for
launching an app from the home screen or for creating URL shortcuts such as bookmarks in web browsers.
As an example, we consider the implementation shown below.
Place an app shortcut on the home screen
117
Secure Coding Guide Documentation Release 2019-12-01
// Use Broadcast to send the system our request for shortcut creation
context.sendBroadcast(intent);
In the Broadcast sent by the above code snippet, the receiver is the home-screen app, and it is difficult to
identify the package name; one must take care to remember that this is a transmission to a public receiver
with an implicit intent. Thus the Broadcast sent by this snippet could be received by any arbitrary app,
including malware; for this reason, the inclusion of sensitive information in the Intent may create the risk
of a damaging leak of information. It is particularly important to note that, when creating a URL-based
shortcut, secret information may be contained in the URL itself.
As countermeasures, it is necessary to follow the points listed in “4.2.1.2. Public Broadcast Receiver
- Receiving/Sending Broadcasts” and to ensure that the transmitted Intent does not contain sensitive
information.
Since the interface of ContentResolver and SQLiteDatabase are so much alike, it’s often misunderstood
that Content Provider is so closely related to SQLiteDatabase. However, actually Content Provider
simply provides the interface of inter-application data sharing, so it’s necessary to pay attention that it
does not interfere each data saving format. To save data in Content Provider, SQLiteDatabase can be
used, and other saving formats, such as an XML file format, also can be used. Any data saving process
is not included in the following sample code, so please add it if needed.
The risks and countermeasures of using Content Provider differ depending on how that Content Provider
is being used. In this section, we have classified 5 types of Content Provider based on how the Content
Provider is being used. You can find out which type of Content Provider you are supposed to create
through the following chart shown below.
118
Secure Coding Guide Documentation Release 2019-12-01
Private Content Provider is the Content Provider which is used only in the single application, and the
safest Content Provider13 .
Sample code of how to implement a private Content Provider is shown below.
Points (Creating a Content Provider):
1. Explicitly set the exported attribute to false.
2. Handle the received request data carefully and securely, even though the data comes from the same
application.
3. Sensitive information can be sent since it is sending and receiving all within the same application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.privateprovider">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".PrivateUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- *** POINT 1 *** Explicitly set the exported attribute to false. -->
<provider
android:name=".PrivateProvider"
android:authorities="org.jssec.android.provider.privateprovider"
android:exported="false" />
(continues on next page)
13 However, non-public settings for Content Provider are not functional in Android 2.2 (API Level 8) and previous
versions.
119
Secure Coding Guide Documentation Release 2019-12-01
PrivateProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.privateprovider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
// UriMatcher
private static final int DOWNLOADS_CODE = 1;
private static final int DOWNLOADS_ID_CODE = 2;
private static final int ADDRESSES_CODE = 3;
private static final int ADDRESSES_ID_CODE = 4;
private static UriMatcher sUriMatcher;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, Download.PATH, DOWNLOADS_CODE);
sUriMatcher.addURI(AUTHORITY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
(continues on next page)
120
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
// *** POINT 2 *** Handle the received request data carefully and securely,
// even though the data comes from the same application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
case ADDRESSES_ID_CODE:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// *** POINT 2 *** Handle the received request data carefully and securely,
// even though the data comes from the same application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
(continues on next page)
121
Secure Coding Guide Documentation Release 2019-12-01
case ADDRESSES_CODE:
case ADDRESSES_ID_CODE:
return sAddressCursor;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// *** POINT 2 *** Handle the received request data carefully and securely,
// even though the data comes from the same application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// *** POINT 2 *** Handle the received request data carefully and securely,
// even though the data comes from the same application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
122
Secure Coding Guide Documentation Release 2019-12-01
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 15;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// *** POINT 2 *** Handle the received request data carefully and securely,
// even though the data comes from the same application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 20;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
}
PrivateUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
(continues on next page)
123
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.provider.privateprovider;
import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
logLine("[Query]");
// *** POINT 5 *** Handle received result data carefully and securely,
// even though the data comes from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (cursor == null) {
logLine(" null cursor");
} else {
boolean moved = cursor.moveToFirst();
while (moved) {
logLine(String.format(" %d, %s", cursor.getInt(0),
cursor.getString(1)));
moved = cursor.moveToNext();
}
}
}
finally {
if (cursor != null) cursor.close();
}
}
logLine("[Insert]");
124
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 5 *** Handle received result data carefully and securely,
// even though the data comes from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(" uri:" + uri);
}
logLine("[Update]");
// *** POINT 5 *** Handle received result data carefully and securely,
// even though the data comes from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records updated", count));
}
logLine("[Delete]");
// *** POINT 5 *** Handle received result data carefully and securely,
// even though the data comes from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records deleted", count));
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
125
Secure Coding Guide Documentation Release 2019-12-01
Public Content Provider is the Content Provider which is supposed to be used by unspecified large
number of applications. It’s necessary to pay attention that since this doesn’t specify clients, it may be
attacked and tampered by Malware. For example, a saved data may be taken by select(), a data may be
changed by update(), or a fake data may be inserted/deleted by insert()/delete().
In addition, when using a custom Public Content Provider which is not provided by Android OS, it’s
necessary to pay attention that request parameter may be received by Malware which masquerades as the
custom Public Content Provider, and also the attack result data may be sent. Contacts and MediaStore
provided by Android OS are also Public Content Providers, but Malware cannot masquerades as them.
Sample code to implement a Public Content Provider is shown below.
Points (Creating a Content Provider):
1. Explicitly set the exported attribute to true.
2. Handle the received request data carefully and securely.
3. When returning a result, do not include sensitive information.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.publicprovider">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- *** POINT 1 *** Explicitly set the exported attribute to true. -->
<provider
android:name=".PublicProvider"
android:authorities="org.jssec.android.provider.publicprovider"
android:exported="true" />
</application>
</manifest>
PublicProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.publicprovider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
(continues on next page)
126
Secure Coding Guide Documentation Release 2019-12-01
// UriMatcher
private static final int DOWNLOADS_CODE = 1;
private static final int DOWNLOADS_ID_CODE = 2;
private static final int ADDRESSES_CODE = 3;
private static final int ADDRESSES_ID_CODE = 4;
private static UriMatcher sUriMatcher;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, Download.PATH, DOWNLOADS_CODE);
sUriMatcher.addURI(AUTHORITY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH, ADDRESSES_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH + "/#", ADDRESSES_ID_CODE);
}
@Override
public boolean onCreate() {
return true;
}
@Override
(continues on next page)
127
Secure Coding Guide Documentation Release 2019-12-01
switch (sUriMatcher.match(uri)) {
case DOWNLOADS_CODE:
case ADDRESSES_CODE:
return CONTENT_TYPE;
case DOWNLOADS_ID_CODE:
case ADDRESSES_ID_CODE:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// *** POINT 2 *** Handle the received request data carefully and securely.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
case ADDRESSES_ID_CODE:
return sAddressCursor;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// *** POINT 2 *** Handle the received request data carefully and securely.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
128
Secure Coding Guide Documentation Release 2019-12-01
case ADDRESSES_CODE:
return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// *** POINT 2 *** Handle the received request data carefully and securely.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 15;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// *** POINT 2 *** Handle the received request data carefully and securely.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
129
Secure Coding Guide Documentation Release 2019-12-01
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 20;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
}
PublicUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.publicuser;
import android.app.Activity;
import android.content.ContentValues;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
130
Secure Coding Guide Documentation Release 2019-12-01
logLine("[Query]");
if (!providerExists(Address.CONTENT_URI)) {
logLine(" Content Provider doesn't exist.");
return;
}
// *** POINT 5 *** When receiving a result, handle the result data
// carefully and securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (cursor == null) {
logLine(" null cursor");
} else {
boolean moved = cursor.moveToFirst();
while (moved) {
logLine(String.format(" %d, %s", cursor.getInt(0),
cursor.getString(1)));
moved = cursor.moveToNext();
}
}
}
finally {
if (cursor != null) cursor.close();
}
}
logLine("[Insert]");
if (!providerExists(Address.CONTENT_URI)) {
logLine(" Content Provider doesn't exist.");
return;
}
// *** POINT 5 *** When receiving a result, handle the result data
// carefully and securely.
(continues on next page)
131
Secure Coding Guide Documentation Release 2019-12-01
logLine("[Update]");
if (!providerExists(Address.CONTENT_URI)) {
logLine(" Content Provider doesn't exist.");
return;
}
// *** POINT 5 *** When receiving a result, handle the result data
// carefully and securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records updated", count));
}
logLine("[Delete]");
if (!providerExists(Address.CONTENT_URI)) {
logLine(" Content Provider doesn't exist.");
return;
}
// *** POINT 5 *** When receiving a result, handle the result data
// carefully and securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records deleted", count));
}
132
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
Partner Content Provider is the Content Provider which can be used only by the particular applications.
The system consists of a partner company’s application and In-house application, and it is used to
protect the information and features which are handled between a partner application and an In-house
application.
Sample code to implement a partner-only Content Provider is shown below.
Points (Creating a Content Provider):
1. Explicitly set the exported attribute to true.
2. Verify if the certificate of a requesting application has been registered in the own white list.
3. Handle the received request data carefully and securely, even though the data comes from a partner
application.
4. Information that is granted to disclose to partner applications can be returned.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.partnerprovider">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- *** POINT 1 *** Explicitly set the exported attribute to true. -->
<provider
android:name=".PartnerProvider"
android:authorities="org.jssec.android.provider.partnerprovider"
android:exported="true" />
</application>
</manifest>
PartnerProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
(continues on next page)
133
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.provider.partnerprovider;
import java.util.List;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
// UriMatcher
private static final int DOWNLOADS_CODE = 1;
private static final int DOWNLOADS_ID_CODE = 2;
private static final int ADDRESSES_CODE = 3;
private static final int ADDRESSES_ID_CODE = 4;
private static UriMatcher sUriMatcher;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, Download.PATH, DOWNLOADS_CODE);
sUriMatcher.addURI(AUTHORITY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH, ADDRESSES_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH + "/#", ADDRESSES_ID_CODE);
}
(continues on next page)
134
Secure Coding Guide Documentation Release 2019-12-01
135
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case DOWNLOADS_CODE:
case ADDRESSES_CODE:
return CONTENT_TYPE;
case DOWNLOADS_ID_CODE:
case ADDRESSES_ID_CODE:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from a partner application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
case ADDRESSES_ID_CODE:
return sAddressCursor;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
136
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from a partner application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from a partner application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 15;
case ADDRESSES_ID_CODE:
(continues on next page)
137
Secure Coding Guide Documentation Release 2019-12-01
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from a partner application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 20;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
}
PartnerUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
(continues on next page)
138
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.provider.partneruser;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
// *** POINT 4 *** Verify if the certificate of the target application has
// been registered in the own white list.
private static PkgCertWhitelists sWhitelists = null;
private static void buildWhitelists(Context context) {
boolean isdebug = Utils.isDebuggable(context);
sWhitelists = new PkgCertWhitelists();
139
Secure Coding Guide Documentation Release 2019-12-01
logLine("[Query]");
// *** POINT 4 *** Verify if the certificate of the target application has
// been registered in the own white list.
if (!checkPartner(this, providerPkgname(Address.CONTENT_URI))) {
logLine(" The target content provider is not served by partner applications.");
return;
}
// *** POINT 6 *** Handle the received result data carefully and
// securely, even though the data comes from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (cursor == null) {
logLine(" null cursor");
} else {
boolean moved = cursor.moveToFirst();
while (moved) {
logLine(String.format(" %d, %s", cursor.getInt(0),
cursor.getString(1)));
moved = cursor.moveToNext();
}
}
}
finally {
if (cursor != null) cursor.close();
}
}
logLine("[Insert]");
// *** POINT 4 *** Verify if the certificate of the target application has
// been registered in the own white list.
if (!checkPartner(this, providerPkgname(Address.CONTENT_URI))) {
logLine(" The target content provider is not served by partner applications.");
return;
}
140
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 6 *** Handle the received result data carefully and securely,
// even though the data comes from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(" uri:" + uri);
}
logLine("[Update]");
// *** POINT 4 *** Verify if the certificate of the target application has
// been registered in the own white list.
if (!checkPartner(this, providerPkgname(Address.CONTENT_URI))) {
logLine(" The target content provider is not served by partner applications.");
return;
}
// *** POINT 6 *** Handle the received result data carefully and securely,
// even though the data comes from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records updated", count));
}
logLine("[Delete]");
// *** POINT 4 *** Verify if the certificate of the target application has
// been registered in the own white list.
if (!checkPartner(this, providerPkgname(Address.CONTENT_URI))) {
logLine(" The target content provider is not served by partner applications.");
return;
}
// *** POINT 6 *** Handle the received result data carefully and securely,
// even though the data comes from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records deleted", count));
}
141
Secure Coding Guide Documentation Release 2019-12-01
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
142
Secure Coding Guide Documentation Release 2019-12-01
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
143
Secure Coding Guide Documentation Release 2019-12-01
In-house Content Provider is the Content Provider which prohibits to be used by applications other than
In house only applications.
Sample code of how to implement an In house only Content Provider is shown below.
Points (Creating a Content Provider):
1. Define an in-house signature permission.
2. Require the in-house signature permission.
3. Explicitly set the exported attribute to true.
4. Verify if the in-house signature permission is defined by an in-house application.
5. Verify the safety of the parameter even if it’s a request from In house only application.
6. Sensitive information can be returned since the requesting application is in-house.
7. When exporting an APK, sign the APK with the same developer key as that of the requesting
application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.inhouseprovider">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
144
Secure Coding Guide Documentation Release 2019-12-01
InhouseProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.inhouseprovider;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
145
Secure Coding Guide Documentation Release 2019-12-01
// UriMatcher
private static final int DOWNLOADS_CODE = 1;
private static final int DOWNLOADS_ID_CODE = 2;
private static final int ADDRESSES_CODE = 3;
private static final int ADDRESSES_ID_CODE = 4;
private static UriMatcher sUriMatcher;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, Download.PATH, DOWNLOADS_CODE);
sUriMatcher.addURI(AUTHORITY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH, ADDRESSES_CODE);
sUriMatcher.addURI(AUTHORITY, Address.PATH + "/#", ADDRESSES_ID_CODE);
}
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
146
Secure Coding Guide Documentation Release 2019-12-01
case DOWNLOADS_ID_CODE:
case ADDRESSES_ID_CODE:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// *** POINT 5 *** Handle the received request data carefully and securely,
// even though the data came from an in-house application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
case ADDRESSES_ID_CODE:
return sAddressCursor;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// *** POINT 5 *** Handle the received request data carefully and securely,
(continues on next page)
147
Secure Coding Guide Documentation Release 2019-12-01
case ADDRESSES_CODE:
return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// *** POINT 5 *** Handle the received request data carefully and securely,
// even though the data came from an in-house application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 15;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
(continues on next page)
148
Secure Coding Guide Documentation Release 2019-12-01
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// *** POINT 5 *** Handle the received request data carefully and securely,
// even though the data came from an in-house application.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 20;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
(continues on next page)
149
Secure Coding Guide Documentation Release 2019-12-01
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
(continues on next page)
150
Secure Coding Guide Documentation Release 2019-12-01
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
*** Point 7 *** When exporting an APK, sign the APK with the same developer key as the requesting
application.
151
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.3.2: Sign the APK with the same developer key as the requesting application
Next is the example of Activity which uses In house only Content Provider.
Point (Using a Content Provider):
8. Declare to use the in-house signature permission.
9. Verify if the in-house signature permission is defined by an in-house application.0
10. Verify if the destination application is signed with the in-house certificate.
11. Sensitive information can be sent since the destination application is in-house one.
12. Handle the received result data carefully and securely, even though the data comes from an in-house
application.
13. When exporting an APK, sign the APK with the same developer key as that of the destination
application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.inhouseuser">
<!-- *** POINT 8 *** Declare to use the in-house signature permission. -->
<uses-permission
android:name="org.jssec.android.provider.inhouseprovider.MY_PERMISSION" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".InhouseUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
InhouseUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
(continues on next page)
152
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.provider.inhouseuser;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
} else {
// Certificate hash value of "my company key" in the keystore.
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
(continues on next page)
153
Secure Coding Guide Documentation Release 2019-12-01
logLine("[Query]");
return;
}
// *** POINT 12 *** Handle the received result data carefully and
// securely, even though the data comes from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (cursor == null) {
logLine(" null cursor");
} else {
boolean moved = cursor.moveToFirst();
while (moved) {
logLine(String.format(" %d, %s", cursor.getInt(0),
cursor.getString(1)));
moved = cursor.moveToNext();
}
}
}
finally {
if (cursor != null) cursor.close();
}
}
154
Secure Coding Guide Documentation Release 2019-12-01
return;
}
// *** POINT 12 *** Handle the received result data carefully and securely,
// even though the data comes from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(" uri:" + uri);
}
logLine("[Update]");
return;
}
155
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 12 *** Handle the received result data carefully and securely,
// even though the data comes from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records updated", count));
}
logLine("[Delete]");
// *** POINT 12 *** Handle the received result data carefully and securely,
// even though the data comes from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
logLine(String.format(" %s records deleted", count));
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
(continues on next page)
156
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
(continues on next page)
157
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
158
Secure Coding Guide Documentation Release 2019-12-01
*** Point 13 *** When exporting an APK, sign the APK with the same developer key as that of the
destination application.
Fig. 4.3.3: Sign the APK with the same developer key as the destination application
Temporary permit Content Provider is basically a private Content Provider, but this permits the partic-
ular applications to access the particular URI. By sending an Intent which special flag is specified to the
target applications, temporary access permission is provided to those applications. Contents provider
side application can give the access permission actively to other applications, and it can also give access
permission passively to the application which claims the temporary access permission.
Sample code of how to implement a temporary permit Content Provider is shown below.
Points (Creating a Content Provider):
1. Explicitly set the exported attribute to false.
2. Specify the path to grant access temporarily with the grant-uri-permission.
3. Handle the received request data carefully and securely, even though the data comes from the
application granted access temporarily.
4. Information that is granted to disclose to the temporary access applications can be returned.
5. Specify URI for the intent to grant temporary access.
6. Specify access rights for the intent to grant temporary access.
7. Send the explicit intent to an application to grant temporary access.
8. Return the intent to the application that requests temporary access.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.provider.temporaryprovider">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
(continues on next page)
159
Secure Coding Guide Documentation Release 2019-12-01
<activity
android:name=".TemporaryActiveGrantActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- *** POINT 2 *** Specify the path to grant access temporarily with the grant-uri-
˓→ permission. -->
<grant-uri-permission android:path="/addresses" />
</provider>
<activity
android:name=".TemporaryPassiveGrantActivity"
android:label="@string/app_name"
android:exported="true" />
</application>
</manifest>
TemporaryProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.temporaryprovider;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
160
Secure Coding Guide Documentation Release 2019-12-01
// UriMatcher
private static final int DOWNLOADS_CODE = 1;
private static final int DOWNLOADS_ID_CODE = 2;
private static final int ADDRESSES_CODE = 3;
private static final int ADDRESSES_ID_CODE = 4;
private static UriMatcher sUriMatcher;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITIY, Download.PATH, DOWNLOADS_CODE);
sUriMatcher.addURI(AUTHORITIY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
sUriMatcher.addURI(AUTHORITIY, Address.PATH, ADDRESSES_CODE);
sUriMatcher.addURI(AUTHORITIY, Address.PATH + "/#", ADDRESSES_ID_CODE);
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case DOWNLOADS_CODE:
case ADDRESSES_CODE:
return CONTENT_TYPE;
161
Secure Coding Guide Documentation Release 2019-12-01
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from the application granted access
// temporarily.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
case ADDRESSES_ID_CODE:
return sAddressCursor;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from the application granted access
// temporarily.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case ADDRESSES_CODE:
return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
162
Secure Coding Guide Documentation Release 2019-12-01
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from the application granted access
// temporarily.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
case ADDRESSES_CODE:
return 15;
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// *** POINT 3 *** Handle the received request data carefully and securely,
// even though the data comes from the application granted access
// temporarily.
// Here, whether uri is within expectations or not, is verified by
// UriMatcher#match() and switch case.
// Checking for other parameters are omitted here, due to sample.
// Please refer to "3.2 Handle Input Data Carefully and Securely."
case DOWNLOADS_ID_CODE:
return 1;
163
Secure Coding Guide Documentation Release 2019-12-01
case ADDRESSES_ID_CODE:
return 1;
default:
throw new IllegalArgumentException("Invalid URI:" + uri);
}
}
}
TemporaryActiveGrantActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.temporaryprovider;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.active_grant);
}
// *** POINT 5 *** Specify URI for the intent to grant temporary
// access.
intent.setData(TemporaryProvider.Address.CONTENT_URI);
(continues on next page)
164
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 6 *** Specify access rights for the intent to grant
// temporary access.
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (ActivityNotFoundException e) {
Toast.makeText(this,
"User Activity not found.", Toast.LENGTH_LONG).show();
}
}
}
TemporaryPassiveGrantActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.temporaryprovider;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
// *** POINT 5 *** Specify URI for the intent to grant temporary access.
intent.setData(TemporaryProvider.Address.CONTENT_URI);
// *** POINT 6 *** Specify access rights for the intent to grant temporary
// access.
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// *** POINT 8 *** Return the intent to the application that requests
(continues on next page)
165
Secure Coding Guide Documentation Release 2019-12-01
TemporaryUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.provider.temporaryuser;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
166
Secure Coding Guide Documentation Release 2019-12-01
logLine("[Query]");
// *** POINT 10 *** When receiving a result, handle the result data
// carefully and securely.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
if (cursor == null) {
logLine(" null cursor");
} else {
boolean moved = cursor.moveToFirst();
while (moved) {
logLine(String.format(" %d, %s", cursor.getInt(0),
cursor.getString(1)));
moved = cursor.moveToNext();
}
}
} catch (SecurityException ex) {
logLine(" Exception:" + ex.getMessage());
}
finally {
if (cursor != null) cursor.close();
}
}
// In the case that this application requests temporary access to the Content
// Provider and the Content Provider passively grants temporary access
// permission to this application.
public void onGrantRequestClick(View view) {
Intent intent = new Intent();
intent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);
try {
startActivityForResult(intent, REQUEST_CODE);
} catch (ActivityNotFoundException e) {
logLine("Content Provider's Activity not found.");
}
}
167
Secure Coding Guide Documentation Release 2019-12-01
// In the case that the Content Provider application grants temporary access
// to this application actively.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView)findViewById(R.id.logview);
}
Be sure to follow the rules below when Implementing or using a content provider.
1. Content Provider that Is Used Only in an Application Must Be Set as Private (Required)
2. Handle the Received Request Parameter Carefully and Securely (Required)
3. Use an In-house Defined Signature Permission after Verifying that it is Defined by an In-house
Application (Required)
4. When Returning a Result, Pay Attention to the Possibility of Information Leakage of that Result
from the Destination Application (Required)
5. When Providing an Asset Secondarily, the Asset should be Protected with the Same Level of Pro-
tection (Required)
And user side should follow the below rules, too.
6. Handle the Returned Result Data from the Content Provider Carefully and Securely (Required)
4.3.2.1 Content Provider that Is Used Only in an Application Must Be Set as Private (Required)
Content Provider which is used only in a single application is not necessary to be accessed by other
applications, and the access which attacks the Content Provider is not often considered by developers. A
Content Provider is basically the system to share data, so it’s handled as public by default. A Content
Provider which is used only in a single application should be set as private explicitly, and it should be
a private Content Provider. In Android 2.3.1 (API Level 9) or later, a Content Provider can be set as
private by specifying android:exported=”false” in provider element.
AndroidManifest.xml
<!-- 4.3.1.1 - *** POINT 1 *** Explicitly set the exported attribute to false. -->
<provider
android:name=".PrivateProvider"
android:authorities="org.jssec.android.provider.privateprovider"
android:exported="false" />
168
Secure Coding Guide Documentation Release 2019-12-01
4.3.2.2 Handle the Received Request Parameter Carefully and Securely (Required)
Risks differ depending on the types of Content Providers, but when processing request parameters, the
first thing you should do is input validation.
Although each method of a Content Provider has the interface which is supposed to receive the component
parameter of SQL statement, actually it simply hands over the arbitrary character string in the system,
so it’s necessary to pay attention that Contents Provider side needs to suppose the case that unexpected
parameter may be provided.
Since Public Content Providers can receive requests from untrusted sources, they can be attacked by
malware. On the other hand, Private Content Providers will never receive any requests from other
applications directly, but it is possible that a Public Activity in the targeted application may forward a
malicious Intent to a Private Content Provider so you should not assume that Private Content Providers
cannot receive any malicious input.
Since other Content Providers also have the risk of a malicious intent being forwarded to them as well,
it is necessary to perform input validation on these requests as well.
Please refer to “3.2. Handling Input Data Carefully and Securely”.
4.3.2.3 Use an In-house Defined Signature Permission after Verifying that it is Defined by an In-
house Application (Required)
Make sure to protect your in-house Content Providers by defining an in-house signature permission when
creating the Content Provider. Since defining a permission in the AndroidManifest.xml file or declaring
a permission request does not provide adequate security, please be sure to refer to “5.2.1.2. How to
Communicate Between In-house Applications with In-house-defined Signature Permission.”
4.3.2.4 When Returning a Result, Pay Attention to the Possibility of Information Leakage of that
Result from the Destination Application (Required)
In case of query() or insert(), Cursor or Uri is returned to the request sending application as a result
information. When sensitive information is included in the result information, the information may be
leaked from the destination application. In case of update() or delete(), number of updated/deleted
records is returned to the request sending application as a result information. In rare cases, depending
on some application specs, the number of updated/deleted records has the sensitive meaning, so please
pay attention to this.
4.3.2.5 When Providing an Asset Secondarily, the Asset should be Protected with the Same Level
of Protection (Required)
4.3.2.6 Handle the Returned Result Data from the Content Provider Carefully and Securely (Re-
quired)
Risks differ depending on the types of Content Provider, but when processing a result data, the first
thing you should do is input validation.
In case that the destination Content Provider is a public Content Provider, Malware which masquerades
as the public Content Provider may return the attack result data. On the other hand, in case that
169
Secure Coding Guide Documentation Release 2019-12-01
the destination Content Provider is a private Content Provider, it is less risk because it receives the
result data from the same application, but you should not assume that private Content Providers cannot
receive any malicious input. Since other Content Providers also have the risk of a malicious data being
returned to them as well, it is necessary to perform input validation on that result data as well.
Please refer to “3.2. Handling Input Data Carefully and Securely”
The risks and countermeasures of using Services differ depending on how that Service is being used. You
can find out which type of Service you are supposed to create through the following chart shown below.
Since the secure coding best practice varies according to how the service is created, we will also explain
about the implementation of the Service as well.
There are several implementation methods for Service, and you will select the method which matches
with the type of Service that you suppose to create. The items of vertical columns in the table show the
implementation methods, and these are divided into 5 types. “OK” stands for the possible combination
and others show impossible/difficult combinations in the table.
Please refer to “4.4.3.2. How to Implement Service” and Sample code of each Service type (with * mark
in a table) for detailed implementation methods of Service.
170
Secure Coding Guide Documentation Release 2019-12-01
Sample code for each security type of Service are shown as below, by using combination of * mark in
Table 4.4.2.
Private Services are Services which cannot be launched by the other applications and therefore it is the
safest Service.
When using Private Services that are only used within the application, as long as you use explicit Intents
to the class then you do not have to worry about accidently sending it to any other application.
Sample code of how to use the startService type Service is shown below.
Points (Creating a Service):
1. Explicitly set the exported attribute to false.
2. Handle the received intent carefully and securely, even though the intent was sent from the same
application.
3. Sensitive information can be sent since the requesting application is in the same application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.service.privateservice" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name=".PrivateUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
(continues on next page)
171
Secure Coding Guide Documentation Release 2019-12-01
</manifest>
PrivateStartService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.privateservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
// The onCreate gets called only one time when the service starts.
@Override
public void onCreate() {
Toast.makeText(this, "PrivateStartService - onCreate()",
Toast.LENGTH_SHORT).show();
}
// The onStartCommand gets called each time after the startService gets called.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// *** POINT 2 *** Handle the received intent carefully and securely,
// even though the intent was sent from the same application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = intent.getStringExtra("PARAM");
Toast.makeText(this,
String.format("PrivateStartService\nReceived param: \"%s\"",
param),
Toast.LENGTH_LONG).show();
return Service.START_NOT_STICKY;
}
// The onDestroy gets called only one time when the service stops.
@Override
public void onDestroy() {
Toast.makeText(this,
"PrivateStartService - onDestroy()",
Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
(continues on next page)
172
Secure Coding Guide Documentation Release 2019-12-01
PrivateUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.privateservice;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.privateservice_activity);
}
// *** POINT 5 *** Sensitive information can be sent since the destination
// service is in the same application.
intent.putExtra("PARAM", "Sensitive information");
startService(intent);
}
// -- StopService control --
(continues on next page)
173
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onStop() {
super.onStop();
// Stop service if the service is running.
doStopService();
}
// *** POINT 5 *** Sensitive information can be sent since the destination
// service is in the same application.
intent.putExtra("PARAM", "Sensitive information");
startService(intent);
}
}
Public Service is the Service which is supposed to be used by the unspecified large number of applications.
It’s necessary to pay attention that it may receive the information (Intent etc.) which was sent by
Malware. In addition, since an Intent to start Service may be received by Malware, explicit Intent
should be used for launching Public Service, and <intent-filter> should not be declared in Service.
Sample code of how to use the startService type Service is shown below.
Points (Creating a Service):
1. Explicitly set exported = “true” without defining the intent filter.
2. Handle the received intent carefully and securely.
3. When returning a result, do not include sensitive information.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.service.publicservice" >
<application
android:icon="@drawable/ic_launcher"
(continues on next page)
174
Secure Coding Guide Documentation Release 2019-12-01
</application>
</manifest>
PublicIntentService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.publicservice;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.widget.Toast;
/**
* Default constructor must be provided when a service extends
* IntentService class.
* If it does not exist, an error occurs.
*/
public PublicIntentService() {
super("CreatingTypeBService");
}
// The onCreate gets called only one time when the Service starts.
@Override
public void onCreate() {
super.onCreate();
(continues on next page)
175
Secure Coding Guide Documentation Release 2019-12-01
Toast.makeText(this,
this.getClass().getSimpleName() + " - onCreate()",
Toast.LENGTH_SHORT).show();
}
// The onHandleIntent gets called each time after the startService gets called.
@Override
protected void onHandleIntent(Intent intent) {
if (Build.VERSION.SDK_INT >= 26) {
Context context = getApplicationContext();
String title = context.getString(R.string.app_name);
NotificationChannel default_channel =
new NotificationChannel(INTENT_CHANNEL, "Intent Channel",
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager =
(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(default_channel);
Notification notification =
new Notification.Builder(context, INTENT_CHANNEL)
.setContentTitle(title)
.setSmallIcon(android.R.drawable.btn_default)
.setContentText("Intent Channel")
.setAutoCancel(true)
.setWhen(System.currentTimeMillis())
.build();
startForeground(1, notification);
}
// *** POINT 2 *** Handle intent carefully and securely.
// Since it's public service, the intent may come from malicious
// application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = intent.getStringExtra("PARAM");
Toast.makeText(this,
String.format("Recieved parameter \"%s\"", param),
Toast.LENGTH_LONG).show();
}
// The onDestroy gets called only one time when the service stops.
@Override
public void onDestroy() {
Toast.makeText(this,
this.getClass().getSimpleName() + " - onDestroy()",
Toast.LENGTH_SHORT).show();
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
(continues on next page)
176
Secure Coding Guide Documentation Release 2019-12-01
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name=".PublicUserActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
PublicUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.publicserviceuser;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.publicservice_activity);
}
(continues on next page)
177
Secure Coding Guide Documentation Release 2019-12-01
startService(intent);
// *** POINT 6 *** When receiving a result, handle the result data
// carefully and securely.
// This sample code uses startService(), so receiving no result.
}
@Override
public void onStop(){
super.onStop();
// Stop service if the service is running.
doStopService();
}
// Stop service
private void doStopService() {
Intent intent = new Intent("org.jssec.android.service.publicservice.action.startservice
˓→");
178
Secure Coding Guide Documentation Release 2019-12-01
stopService(intent);
}
}
Partner Service is Service which can be used only by the particular applications. System consists of
partner company’s application and In house application, this is used to protect the information and
features which are handled between a partner application and In house application.
Following is an example of AIDL bind type Service.
Points (Creating a Service):
1. Explicitly set exported = “true” without defining the intent filter.
2. Verify that the certificate of the requesting application has been registered in the own white list.
3. Do not (Cannot) recognize whether the requesting application is partner or not by onBind (on-
StartCommand, onHandleIntent).
4. Handle the received intent carefully and securely, even though the intent was sent from a partner
application.
5. Return only information that is granted to be disclosed to a partner application.
In addition, refer to “5.2.1.3. How to Verify the Hash Value of an Application’s Certificate” for how to
verify the certification hash value of destination application which is specified to white list.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.service.partnerservice.aidl" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<service
android:name="org.jssec.android.service.partnerservice.aidl.PartnerAIDLService"
android:exported="true" />
</application>
</manifest>
In this example, 2 AIDL files are to be created. One is for callback interface to give data from Service
to Activity. The other one is Interface to give data from Activity to Service and to get information. In
addition, package name that is described in AIDL file should be consistent with directory hierarchy in
which AIDL file is created, same like package name described in java file.
IPartnerAIDLServiceCallback.aidl
package org.jssec.android.service.partnerservice.aidl;
interface IPartnerAIDLServiceCallback {
(continues on next page)
179
Secure Coding Guide Documentation Release 2019-12-01
IPartnerAIDLService.aidl
package org.jssec.android.service.partnerservice.aidl;
import org.jssec.android.service.partnerservice.aidl.IExclusiveAIDLServiceCallback;
interface IPartnerAIDLService {
/**
* Register Callback
*/
void registerCallback(IPartnerAIDLServiceCallback cb);
/**
* Get Information
*/
String getInfo(String param);
/**
* Unregister Callback
*/
void unregisterCallback(IPartnerAIDLServiceCallback cb);
}
PartnerAIDLService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.partnerservice.aidl;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.widget.Toast;
180
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 2 *** Verify that the certificate of the requesting application
// has been registered in the own white list.
private static PkgCertWhitelists sWhitelists = null;
private static void buildWhitelists(Context context) {
boolean isdebug = Utils.isDebuggable(context);
sWhitelists = new PkgCertWhitelists();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MSG: {
if(mCallbacks == null){
return;
}
// Start broadcast
// To call back on to the registered clients, use beginBroadcast().
// beginBroadcast() makes a copy of the currently registered
// callback list.
final int N = mCallbacks.beginBroadcast();
(continues on next page)
181
Secure Coding Guide Documentation Release 2019-12-01
} catch (RemoteException e) {
// Callbacks are managed by RemoteCallbackList, do not
// unregister callbacks here.
// RemoteCallbackList.kill() unregister all callbacks
}
}
// finishBroadcast() cleans up the state of a broadcast previously
// initiated by calling beginBroadcast().
mCallbacks.finishBroadcast();
182
Secure Coding Guide Documentation Release 2019-12-01
@Override
public IBinder onBind(Intent intent) {
// *** POINT 3 *** Verify that the certificate of the requesting
// application has been registered in the own white list.
// So requesting application must be validated in methods defined
// in AIDL every time.
return mBinder;
}
@Override
public void onCreate() {
Toast.makeText(this,
this.getClass().getSimpleName() + " - onCreate()",
Toast.LENGTH_SHORT).show();
@Override
public void onDestroy() {
Toast.makeText(this,
(continues on next page)
183
Secure Coding Guide Documentation Release 2019-12-01
mHandler.removeMessages(REPORT_MSG);
}
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
(continues on next page)
184
Secure Coding Guide Documentation Release 2019-12-01
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
185
Secure Coding Guide Documentation Release 2019-12-01
import org.jssec.android.service.partnerservice.aidl.IPartnerAIDLService;
import org.jssec.android.service.partnerservice.aidl.IPartnerAIDLServiceCallback;
import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
(continues on next page)
186
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 6 *** Verify if the certificate of the target application has
// been registered in the own white list.
private static PkgCertWhitelists sWhitelists = null;
private static void buildWhitelists(Context context) {
boolean isdebug = Utils.isDebuggable(context);
sWhitelists = new PkgCertWhitelists();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MGS_VALUE_CHANGED: {
String info = (String)msg.obj;
Toast.makeText(mContext,
String.format("Received \"%s\" with callback.", info),
Toast.LENGTH_SHORT).show();
break;
}
(continues on next page)
187
Secure Coding Guide Documentation Release 2019-12-01
// This is called when the connection with the service has been
// established.
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
mService = IPartnerAIDLService.Stub.asInterface(service);
try{
// connect to service
mService.registerCallback(mCallback);
}catch(RemoteException e){
// service stopped abnormally
}
Toast.makeText(mContext,
"Connected to service",
Toast.LENGTH_SHORT).show();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.partnerservice_activity);
(continues on next page)
188
Secure Coding Guide Documentation Release 2019-12-01
mContext = this;
}
// -- StartService control --
public void onStartServiceClick(View v) {
// Start bindService
doBindService();
}
// -- GetInfo control --
public void onGetInfoClick(View v) {
getServiceinfo();
}
// -- StopService control --
public void onStopServiceClick(View v) {
doUnbindService();
}
@Override
public void onDestroy() {
super.onDestroy();
doUnbindService();
}
/**
* Connect to service
*/
private void doBindService() {
if (!mIsBound){
// *** POINT 6 *** Verify if the certificate of the target application
// has been registered in the own white list.
if (!checkPartner(this, TARGET_PACKAGE)) {
Toast.makeText(this,
"Destination(Requested) sevice application is not registered in white list.
˓→", Toast.LENGTH_LONG).show();
return;
}
// *** POINT 8 *** Use the explicit intent to call a partner service.
intent.setClassName(TARGET_PACKAGE, TARGET_CLASS);
/**
* Disconnect service
*/
private void doUnbindService() {
if (mIsBound) {
// Unregister callbacks which have been registered.
(continues on next page)
189
Secure Coding Guide Documentation Release 2019-12-01
unbindService(mConnection);
// *** POINT 8 *** Use the explicit intent to call a partner service.
intent.setClassName(TARGET_PACKAGE, TARGET_CLASS);
stopService(intent);
mIsBound = false;
}
}
/**
* Get information from service
*/
void getServiceinfo() {
if (mIsBound && mService != null) {
String info = null;
try {
// *** POINT 7 *** Return only information that is granted to be
// disclosed to a partner application.
info = mService.getInfo("Information disclosed to partner application (method␣
˓→from activity)");
} catch (RemoteException e) {
e.printStackTrace();
}
// *** POINT 9 *** Handle the received result data carefully and
// securely, even though the data came from a partner application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
Toast.makeText(mContext,
String.format("Received \"%s\" from service.", info),
Toast.LENGTH_SHORT).show();
}
}
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
(continues on next page)
190
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
(continues on next page)
191
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
192
Secure Coding Guide Documentation Release 2019-12-01
In-house Services are the Services which are prohibited to be used by applications other than in-house
applications. They are used in applications developed internally that want to securely share information
and functionality.
Following is an example which uses Messenger bind type Service.
Points (Creating a Service):
1. Define an in-house signature permission.
2. Require the in-house signature permission.
3. Explicitly set exported = “true” without defining the intent filter.
4. Verify that the in-house signature permission is defined by an in-house application.
5. Handle the received intent carefully and securely, even though the intent was sent from an in-house
application.
6. Sensitive information can be returned since the requesting application is in-house.
7. When exporting an APK, sign the APK with the same developer key as the requesting application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.service.inhouseservice.messenger" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<service
android:name="org.jssec.android.service.inhouseservice.messenger.
˓→InhouseMessengerService"
android:exported="true"
android:permission="org.jssec.android.service.inhouseservice.messenger.MY_PERMISSION" /
˓→>
</application>
</manifest>
InhouseMessengerService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
(continues on next page)
193
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.service.inhouseservice.messenger;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;
} else {
// Certificate hash value of keystore "my company key"
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
194
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void handleMessage(Message msg){
switch(msg.what){
case CommonValue.MSG_REGISTER_CLIENT:
// Add messenger received from client
mClients.add(msg.replyTo);
break;
case CommonValue.MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case CommonValue.MSG_SET_VALUE:
// Send data to client
sendMessageToClients(mClients);
break;
default:
super.handleMessage(msg);
break;
}
}
}
/**
* Send data to client
*/
private static void sendMessageToClients(ArrayList<Messenger> mClients){
} catch (RemoteException e) {
// If client does not exits, remove it from a list.
ite.remove();
}
}
}
@Override
public IBinder onBind(Intent intent) {
195
Secure Coding Guide Documentation Release 2019-12-01
return null;
}
// *** POINT 5 *** Handle the received intent carefully and securely,
// even though the intent was sent from an in-house application.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
String param = intent.getStringExtra("PARAM");
Toast.makeText(this,
String.format("Received parameter \"%s\".", param),
Toast.LENGTH_LONG).show();
return mMessenger.getBinder();
}
@Override
public void onCreate() {
Toast.makeText(this, "Service - onCreate()", Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
Toast.makeText(this, "Service - onDestroy()", Toast.LENGTH_SHORT).show();
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
196
Secure Coding Guide Documentation Release 2019-12-01
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
197
Secure Coding Guide Documentation Release 2019-12-01
*** Point 7 *** When exporting an APK, sign the APK with the same developer key as the requesting
application.
Fig. 4.4.2: Sign the APK with the same developer key as the requesting application
198
Secure Coding Guide Documentation Release 2019-12-01
Next is the sample code of Activity which uses in house only Service.
Points (Using a Service):
8. Declare to use the in-house signature permission.
9. Verify that the in-house signature permission is defined by an in-house application.
10. Verify that the destination application is signed with the in-house certificate.
11. Sensitive information can be sent since the destination application is in-house.
12. Use the explicit intent to call an in-house service.
13. Handle the received result data carefully and securely, even though the data came from an in-house
application.
14. When exporting an APK, sign the APK with the same developer key as the destination application.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.service.inhouseservice.messengeruser" >
<!-- *** POINT 8 *** Declare to use the in-house signature permission. -->
<uses-permission
android:name="org.jssec.android.service.inhouseservice.messenger.MY_PERMISSION" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name="org.jssec.android.service.inhouseservice.messengeruser.
˓→InhouseMessengerUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
InhouseMessengerUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.service.inhouseservice.messengeruser;
199
Secure Coding Guide Documentation Release 2019-12-01
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Toast;
} else {
// Certificate hash value of keystore "my company key"
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
200
Secure Coding Guide Documentation Release 2019-12-01
// This is called when the connection with the service has been
// established.
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
mServiceMessenger = new Messenger(service);
Toast.makeText(mContext,
"Connect to service",
Toast.LENGTH_SHORT).show();
try {
// Send own messenger to service
Message msg =
Message.obtain(null, CommonValue.MSG_REGISTER_CLIENT);
msg.replyTo = mActivityMessenger;
mServiceMessenger.send(msg);
} catch (RemoteException e) {
// Service stopped abnormally
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.inhouseservice_activity);
mContext = this;
(continues on next page)
201
Secure Coding Guide Documentation Release 2019-12-01
// -- GetInfo control --
public void onGetInfoClick(View v) {
getServiceinfo();
}
// -- StopService control --
public void onStopServiceClick(View v) {
doUnbindService();
}
@Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
/**
* Connect to service
*/
void doBindService() {
if (!mIsBound){
// *** POINT 9 *** Verify that the in-house signature permission is
// defined by an in-house application.
if (!SigPerm.test(this, MY_PERMISSION, myCertHash(this))) {
Toast.makeText(this, "In-house defined signature permission is not defined by␣
˓→in-house application.", Toast.LENGTH_LONG).show();
return;
}
return;
}
/**
(continues on next page)
202
Secure Coding Guide Documentation Release 2019-12-01
/**
* Get information from service
*/
void getServiceinfo() {
if (mServiceMessenger != null) {
try {
// Request sending information
Message msg = Message.obtain(null, CommonValue.MSG_SET_VALUE);
mServiceMessenger.send(msg);
} catch (RemoteException e) {
// Service stopped abnormally
}
}
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
203
Secure Coding Guide Documentation Release 2019-12-01
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
}
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
204
Secure Coding Guide Documentation Release 2019-12-01
*** Point14 *** When exporting an APK, sign the APK with the same developer key as the destination
application.
Fig. 4.4.3: Sign the APK with the same developer key as the destination application
205
Secure Coding Guide Documentation Release 2019-12-01
4.4.2.1 Service that Is Used Only in an application, Must Be Set as Private (Required)
Service that is used only in an application (or in same UID) must be set as Private. It avoids the
application from receiving Intents from other applications unexpectedly and eventually prevents from
damages such as application functions are used or application behavior becomes abnormal.
All you have to do in implementation is set exported attribute false when defining Service in Android-
Manifest.xml.
AndroidManifest.xml
<!-- Private Service derived from Service class -->
<!-- *** 4.4.1.1 - POINT 1 *** Explicitly set the exported attribute to false. -->
<service android:name=".PrivateStartService" android:exported="false"/>
In addition, this is a rare case, but do not set Intent Filter when service is used only within the application.
The reason is that, due to the characteristics of Intent Filter, public service in other application may be
called unexpectedly though you intend to call Private Service within the application.
AndroidManifest.xml(Not recommended)
<!-- Private Service derived from Service class -->
<!-- *** 4.4.1.1 - POINT 1 *** Explicitly set the exported attribute to false. -->
<service android:name=".PrivateStartService" android:exported="false">
<intent-filter>
<action android:name=”org.jssec.android.service.OPEN />
</intent-filter>
</service>
See “4.4.3.1. Combination of Exported Attribute and Intent-filter Setting (In the Case of Service)”.
Same like Activity, In case of Service, when processing a received Intent data, the first thing you should
do is input validation. Also in Service user side, it’s necessary to verify the safety of result information
from Service. Please refer to “4.1.2.5. Handling the Received Intent Carefully and Securely (Required)”
and “4.1.2.9. Handle the Returned Data from a Requested Activity Carefully and Securely (Required).”
In Service, you should also implement calling method and exchanging data by Message carefully.
Please refer to “3.2. Handling Input Data Carefully and Securely”
206
Secure Coding Guide Documentation Release 2019-12-01
4.4.2.3 Use the In-house Defined Signature Permission after Verifying If it’s Defined by an In-house
Application (Required)
Make sure to protect your in-house Services by defining in-house signature permission when creating the
Service. Since defining a permission in the AndroidManifest.xml file or declaring a permission request
does not provide adequate security, please be sure to refer to “5.2.1.2. How to Communicate Between
In-house Applications with In-house-defined Signature Permission.”
4.4.2.4 Do Not Determine Whether the Service Provides its Functions, in onCreate (Required)
Security checks such as Intent parameter verification or in-house-defined Signature Permission verification
should not be included in onCreate, because when receiving new request during Service is running, process
of onCreate is not executed. So, when implementing Service which is started by startService, judgment
should be executed by onStartCommand (In case of using IntentService, judgment should be executed by
onHandleIntent.) It’s also same in the case when implementing Service which is started by bindService,
judgment should be executed by onBind.
4.4.2.5 When Returning a Result Information, Pay Attention the Result Information Leakage from
the Destination Application (Required)
Depends on types of Service, the reliability of result information destination application (callback re-
ceiver side/ Message destination) are different. Need to consider seriously about the information leakage
considering the possibility that the destination may be Malware.
See, Activity “4.1.2.7. When Returning a Result, Pay Attention to the Possibility of Information Leakage
of that Result from the Destination Application (Required)”, for details.
4.4.2.6 Use the Explicit Intent if the Destination Service Is fixed (Required)
When using a Service by implicit Intents, in case the definition of Intent Filter is same, Intent is sent to
the Service which was installed earlier. If Malware with the same Intent Filter defined intentionally was
installed earlier, Intent is sent to Malware and information leakage occurs. On the other hand, when
using a Service by explicit Intents, only the intended Service will receive the Intent so this is much safer.
There are some other points which should be considered, please refer to “4.1.2.8. Use the explicit Intents
if the destination Activity is predetermined. (Required).”
4.4.2.7 Verify the Destination Service If Linking with the Other Company’s Application (Required)
Be sure to sure a whitelist when linking with another company’s application. You can do this by saving
a copy of the company’s certificate hash inside your application and checking it with the certificate
hash of the destination application. This will prevent a malicious application from being able to spoof
Intents. Please refer to sample code section “4.4.1.3. Creating/Using Partner Services” for the concrete
implementation method.
4.4.2.8 When Providing an Asset Secondarily, the Asset should be protected with the Same Level
Protection (Required)
When an information or function asset, which is protected by permission, is provided to another applica-
tion secondhand, you need to make sure that it has the same required permissions needed to access the
asset. In the Android OS permission security model, only an application that has been granted proper
permissions can directly access a protected asset. However, there is a loophole because an application
with permissions to an asset can act as a proxy and allow access to an unprivileged application. Substan-
tially this is the same as re-delegating permission so it is referred to as the “Permission Re-delegation”
problem. Please refer to “5.2.3.4. Permission Re-delegation Problem.”
207
Secure Coding Guide Documentation Release 2019-12-01
4.4.3.1 Combination of Exported Attribute and Intent-filter Setting (In the Case of Service)
We have explained how to implement the four types of Services in this guidebook: Private Services,
Public Services, Partner Services, and In-house Services. The various combinations of permitted settings
for each type of exported attribute defined in the AndroidManifest.xml file and the intent-filter elements
are defined in the table below. Please verify the compatibility of the exported attribute and intent-filter
element with the Service you are trying to create.
If the exported attribute is not unspecified in a Service, the question of whether or not the Service
is public is determined by whether or not intent filters are defined14 ; however, in this guidebook it is
forbidden to set a Service’s exported attribute to “unspecified”. In general, as mentioned previously, it
is best to avoid implementations that rely on the default behavior of any given API; moreover, in cases
where explicit methods exist for configuring important security-related settings such as the exported
attribute, it is always a good idea to make use of those methods.
In “Table 4.4.3 Combination of Exported Attributes and Intent-filter Setting”, all “Intent Filter defined”
are set to “(Do not Use)”. This is because when a Service is started using an implicit Intent, it is not
possible to know which Service responds to the Intent, and a malicious Service may respond.
And the reason why “a defined intent filter and an exported attribute of false” should not be used is
that there is a loophole in Android’s behavior, and because of how Intent filters work, other application’s
Services can be called unexpectedly.
Concretely, Android behaves as per below, so it’s necessary to consider carefully when application de-
signing.
• When multiple Services define the same content of intent-filter, the definition of Service within
application installed earlier is prioritized.
• In case explicit Intent is used, prioritized Service is automatically selected and called by OS.
The system that unexpected call is occurred due to Android’s behavior is described in the three figures
below. Fig. 4.4.4 is an example of normal behavior that Private Service (application A) can be called
by implicit Intent only from the same application. Because only application A defines Intent-filter
(action=”X” in the Figure), it behaves normally. This is the normal behavior.
14 If any intent filters are defined then the Service is public; otherwise it is private. For more information, see https:
//developer.android.com/guide/topics/manifest/service-element#exported
208
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.4.5 and Fig. 4.4.6 below show a scenario in which the same Intent filter (action=”X”) is defined
in Application B as well as Application A.
Fig. 4.4.5 shows the scenario that applications are installed in the order, application A -> application B.
In this case, when application C sends implicit Intent, calling Private Service (A-1) fails. On the other
hand, since application A can successfully call Private Service within the application by implicit Intent
as expected, there won’t be any problems in terms of security (counter-measure for Malware).
Fig. 4.4.5: Applications are installed in the order, application A -> application B
Fig. 4.4.6 shows the scenario that applications are installed in the order, applicationB -> applicationA.
209
Secure Coding Guide Documentation Release 2019-12-01
There is a problem here, in terms of security. It shows an example that applicationA tries to call Private
Service within the application by sending implicit Intent, but actually Public Activity (B-1) in application
B which was installed earlier, is called. Due to this loophole, it is possible that sensitive information
can be sent from applicationA to applicationB. If applicationB is Malware, it will lead the leakage of
sensitive information.
Fig. 4.4.6: Applications are installed in the order, applicationB -> applicationA
As shown above, using Intent filters to send implicit Intents to Private Service may result in unexpected
behavior so it is best to avoid this setting.
Because methods for Service implementation are various and should be selected with consideration of
security type which is categorized by sample code, each characteristics are briefly explained. It’s divided
roughly into the case using startService and the case using bindService. And it’s also possible to create
Service which can be used in both startService and bindService. Following items should be investigated
to determine the implementation method of Service.
• Whether to disclose Service to other applications or not (Disclosure of Service)
• Whether to exchange data during running or not (Mutual sending/receiving data)
• Whether to control Service or not (Launch or complete)
• Whether to execute as another process (communication between processes)
• Whether to execute multiple processes in parallel (Parallel process)
Table 4.4.4 shows category of implementation methods and feasibility of each item.
“NG” stands for impossible case or case that another frame work which is different from the provided
function is required.
210
Secure Coding Guide Documentation Release 2019-12-01
startService type
This is the most basic Service. This inherits Service class, and executes processes by onStartCommand.
In user side, specify Service by Intent, and call by startService. Because data such as results cannot be
returned to source of Intent directly, it should be achieved in combination with another method such as
Broadcast. Please refer to “4.4.1.1. Creating/Using Private Services” for the concrete example.
Checking in terms of security should be done by onStartCommand, but it cannot be used for partner
only Service since the package name of the source cannot be obtained.
IntentService type
IntentService is the class which was created by inheriting Service. Calling method is same as startService
type. Following are characteristics compared with standard service (startService type.)
• Processing Intent is done by onHandleIntent (onStartCommand is not used.)
• It’s executed by another thread.
• Process is to be queued.
Call is immediately returned because process is executed by another thread, and process towards Intents
is sequentially executed by Queuing system. Each Intent is not processed in parallel, but it is also
selectable depending on the product’s requirement, as an option to simplify implementation. Since data
such as results cannot be returned to source of Intent, it should be achieved in combination with another
method such as Broadcast. Please refer to “4.4.1.2. Creating/Using Public Services” for the concrete
example of implementation.
Checking in terms of security should be done by onHandleIntent, but it cannot be used for partner only
Service since the package name of the source cannot be obtained.
This is a method to implement local Service which works only within the process same as an application.
Define the class which was derived from Binder class, and prepare to provide the feature (method) which
was implemented in Service to caller side.
From user side, specify Service by Intent and call Service by using bindService. This is the most simple
implementation method among all methods of binding Service, but it has limited usages since it cannot
be launched by another process and also Service cannot be disclosed. See project “Service PrivateSer-
viceLocalBind” which is included in Sample code, for the concrete implementation example.
211
Secure Coding Guide Documentation Release 2019-12-01
From the security point of view, only private Service can be implemented.
This is the method to achieve the linking with Service by using Messenger system.
Since Messenger can be given as a Message destination from Service user side, the mutual data exchanging
can be achieved comparatively easily. In addition, since processes are to be queued, it has a characteristic
that behaves “thread-safely”. Parallel process for each process is not possible, but it is also selectable as
an option to simplify the implementation depending on the product’s requirement. Regarding user side,
specify Service by Intent, and call Service by using bindService. See “4.4.1.4. Creating/Using In-house
Services” for the concrete implementation example.
Security check in onBind or by Message Handler is necessary, however, it cannot be used for partner
only Service since package name of source cannot be obtained.
This is a method to achieve linking with Service by using AIDL system. Define interface by AIDL,
and provide features that Service has as a method. In addition, call back can be also achieved by
implementing interface defined by AIDL in user side, Multi-thread calling is possible, but it’s necessary
to implement explicitly in Service side for exclusive process.
User side can call Service, by specifying Intent and using bindService. Please refer to “4.4.1.3. Creat-
ing/Using Partner Services” for the concrete implementation example.
Security must be checked in onBind for In-house only Service and by each method of interface defined
by AIDL for partner only Service.
This can be used for all security types of Service which are described in this Guidebook.
Herein after, some cautions in terms of security when creating/operating database by using SQLite. Main
points are appropriate setting of access right to database file, and counter-measures for SQL injection.
Database which permits reading/writing database file from outside directly (sharing among multiple
applications) is not supposed here, but suppose the usage in backend of Content Provider and in an
application itself. In addition, it is recommended to adopt counter-measures mentioned below in case of
handling not so much sensitive information, though handling a certain level of sensitive information is
supposed here.
When handling database in Android application, appropriate arrangements of database files and access
right setting (Setting for denying other application’s access) can be achieved by using SQLiteOpen-
Helper15 . Here is an example of easy application that creates database when it’s launched, and executes
searching/adding/changing/deleting data through UI. Sample code is what counter-measure for SQL
injection is done, to avoid from incorrect SQL being executed against the input from outside.
15 As regarding file storing, the absolute file path can be specified as the 2nd parameter (name) of SQLiteOpenHelper
constructor. Therefore, need attention that the stored files can be read and written by the other applications if the SD
Card path is specified.
212
Secure Coding Guide Documentation Release 2019-12-01
Points:
1. SQLiteOpenHelper should be used for database creation.
2. Use place holder.
3. Validate the input value according the application requirements.
SampleDbOpenHelper.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.sqlite;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;
(continues on next page)
213
Secure Coding Guide Documentation Release 2019-12-01
} catch (SQLException e) {
//In case failed to construct database, output to log
Log.e(mContext.getClass().toString(),
mContext.getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));
Toast.makeText(mContext,
R.string.DATABASE_OPEN_ERROR_MESSAGE,
Toast.LENGTH_LONG).show();
}
}
//Database Close
public void closeDatabase() {
try {
if (mSampleDb != null && mSampleDb.isOpen()) {
mSampleDb.close();
}
} catch (SQLException e) {
//In case failed to construct database, output to log
(continues on next page)
214
Secure Coding Guide Documentation Release 2019-12-01
//Remember Context
private Context mContext;
@Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(CREATE_TABLE_COMMANDS); //Execute DB construction command
} catch (SQLException e) {
//In case failed to construct database, output to log
Log.e(this.getClass().toString(),
mContext.getString(R.string.DATABASE_CREATE_ERROR_MESSAGE));
}
}
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// It's to be executed when database version up. Write processes like data
// transition.
}
}
package org.jssec.android.sqlite.task;
(continues on next page)
215
Secure Coding Guide Documentation Release 2019-12-01
import org.jssec.android.sqlite.CommonData;
import org.jssec.android.sqlite.DataValidator;
import org.jssec.android.sqlite.MainActivity;
import org.jssec.android.sqlite.R;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.util.Log;
@Override
protected Cursor doInBackground(String... params) {
String idno = params[0];
String name = params[1];
String info = params[2];
String cols[] = {"_id", "idno","name","info"};
Cursor cur;
//*** POINT 3 *** Validate the input value according the application
// requirements.
if (!DataValidator.validateData(idno, name, info))
{
return null;
}
try {
//*** POINT 2 *** Use place holder.
cur = mSampleDB.query(CommonData.TABLE_NAME, cols,
(continues on next page)
216
Secure Coding Guide Documentation Release 2019-12-01
//Other than above, execute partly match searching with the condition
// of info.
//Escape @ in info which was received as input.
String argString = info.replaceAll("@", "@@");
//Escape % in info which was received as input.
argString = argString.replaceAll("%", "@%");
//Escape _ in info which was received as input.
argString = argString.replaceAll("_", "@_");
String selectionArgs[] = {argString};
try {
//*** POINT 2 *** Use place holder.
cur = mSampleDB.query(CommonData.TABLE_NAME, cols,
"info LIKE '%' || ? || '%' ESCAPE '@'",
selectionArgs, null, null, null);
} catch (SQLException e) {
Log.e(DataSearchTask.class.toString(),
mActivity.getString(R.string.SEARCHING_ERROR_MESSAGE));
return null;
}
return cur;
}
@Override
protected void onPostExecute(Cursor resultCur) {
mActivity.updateCursor(resultCur);
}
}
DataValidator.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
(continues on next page)
217
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.sqlite;
return true;
}
return true;
}
218
Secure Coding Guide Documentation Release 2019-12-01
Considering the protection of DB file data, DB file location and access right setting is the very important
elements that need to be considered together.
For example, even if file access right is set correctly, a DB file can be accessed from anybody in case that
it is arranged in a location which access right cannot be set, e.g. SD card. And in case that it’s arranged
in application directory, if the access right is not correctly set, it will eventually allow the unexpected
access. Following are some points to be met regarding the correct allocation and access right setting,
and the methods to realize them.
About location and access right setting, considering in terms of protecting DB file (data), it’s necessary
to execute 2 points as per below.
1. Location
Locate in file path that can be obtained by Context#getDatabasePath(String name), or in some cases,
directory that can be obtained by Context#getFilesDir16 .
2. Access right
Set to MODE_PRIVATE ( = it can be accessed only by the application which creates file) mode.
By executing following 2 points, DB file which cannot be accessed by other applications can be created.
Here are some methods to execute them.
1. Use SQLiteOpenHelper
2. Use Context#openOrCreateDatabase
When creating DB file, SQLiteDatabase#openOrCreateDatabase can be used. However, when using this
method, DB files which can be read out from other applications are created, in some Android smartphone
devices. So it is recommended to avoid this method, and using other methods. Each characteristics for
the above 2 methods are as per below.
16 Both methods provide the path under (package) directory which is able to be read and written only by the specified
application.
219
Secure Coding Guide Documentation Release 2019-12-01
Using SQLiteOpenHelper
When using SQLiteOpenHelper, developers don’t need to be worried about many things. Create a class
derived from SQLiteOpenHelper, and specify DB name (which is used for file name)17 to constructer’s
parameter, then DB file which meets above security requirements, are to be created automatically.
Refer to specific usage method for “4.5.1.1. Creating/Operating Database” for how to use.
Using Context#openOrCreateDatabase
When creating DB by using Context#openOrCreateDatabase method, file access right should be specified
by option, in this case specify MODE_PRIVATE explicitly.
Regarding file arrangement, specifying DB name (which is to be used to file name) can be done as same as
SQLiteOpenHelper, a file is to be created automatically, in the file path which meets the above mentioned
security requirements. However, full path can be also specified, so it’s necessary to pay attention that
when specifying SD card, even though specifying MODE_PRIVATE, other applications can also access.
Example to execute access permission setting to DB explicitly: MainActivity.java
//Construct database
try {
//Create DB by setting MODE_PRIVATE
db = Context.openOrCreateDatabase("Sample.db", MODE_PRIVATE, null);
} catch (SQLException e) {
//In case failed to construct DB, log output
Log.e(this.getClass().toString(),
getString(R.string.DATABASE_OPEN_ERROR_MESSAGE));
return;
}
//Omit other initial process
}
There are three possible settings for access privileges: MODE_PRIVATE, MODE_WORLD_READ-
ABLE, and MODE_WORLD_WRITEABLE. These constants can be specified together by “OR” op-
erator. However, all settings other than MODE_PRIVATE are deprecated in API Level 17 and later
versions, and will result in a security exception in API Level 24 and later versions. Even for apps intended
for API Level 15 and earlier, it is generally best not to use these flags18 .
• MODE_PRIVATE Only creator application can read and write
• MODE_WORLD_READABLE Creator application can read and write, Others can only read in
• MODE_WORLD_WRITEABLE Creator application can read and write, Others can only write
in
4.5.2.2 Use Content Provider for Access Control When Sharing DB Data with Other Application
(Required)
The method to share DB data with other application is that create DB file as WORLD_READABLE,
WORLD_WRITEABLE, to other applications to access directly. However, this method cannot limit
applications which access to DB or operations to DB, so data can be read-in or written by unexpected
17 (Undocumented in Android reference) Since the full file path can be specified as the database name in SQLiteOpen-
Helper implementation, need attention that specifying the place (path) which does not have access control feature (e.g. SD
cards) unintentionally.
18 For more information as to MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE and points of cau-
tion regarding their use, see Section “4.6.3.2. Access Permission Setting for the Directory”.
220
Secure Coding Guide Documentation Release 2019-12-01
party (application). As a result, it can be considered that some problems may occur in confidentiality
or consistency of data, or it may be an attack target of Malware.
As mentioned above, when sharing DB data with other applications in Android, it’s strongly recom-
mended to use Content Provider. By using Content Provider, there are some merits, not only the merits
from the security point of view which is the access control on DB can be achieved, but also merits from
the designing point of view which is DB scheme structure can be hidden into Content Provider.
4.5.2.3 Place Holder Must Be Used in the Case Handling Variable Parameter during DB Operation.
(Required)
In the sense that preventing from SQL injection, when incorporating the arbitrary input value to SQL
statement, placeholder should be used. There are 2 methods as per below to execute SQL using place-
holder.
1. Get SQLiteStatement by using SQLiteDatabase#compileStatement(), and after that place param-
eter to placeholder by using SQLiteStatement#bindString() or bindLong() etc.
2. When calling execSQL(), insert(), update(), delete(), query(), rawQuery() and replace() in SQLite-
Database class, use SQL statement which has placeholder.
In addition, when executing SELECT command, by using SQLiteDatabase#compileStatement(), there
is a limitation that “only the top 1 element can be obtained as a result of SELECT command”, so usages
are limited.
In either method, the data content which is given to placeholder is better to be checked in advance
according the application requirements. Following is the further explanation for each method.
@Override
protected Void doInBackground(String... params) {
String idno = params[0];
String name = params[1];
String info = params[2];
// *** POINT 3 *** Validate the input value according the application
// requirements.
if (!DataValidator.validateData(idno, name, info))
{
(continues on next page)
221
Secure Coding Guide Documentation Release 2019-12-01
This is a type that SQL statement to be executed as object is created in advance, and parameters are
allocated to it. The process to execute is fixed, so there’s no room for SQL injection to occur. In addition,
there is a merit that process efficiency is enhanced by reutilizing SQLiteStatement object.
In the Case Using Method for Each Process which SQLiteDatabase provides:
There are 2 types of DB operation methods that SQLiteDatabase provides. One is what SQL statement
is used, and another is what SQL statement is not used. Methods that SQL statement is used are
SQLiteDatabase#execSQL()/rawQuery() and it’s executed in the following steps.
1. Prepare SQL statement which includes placeholder.
2. Create data to allocate to placeholder.
3. Send SQL statement and data as parameter, and execute a method for process.
On the other hand, SQLiteDatabase#insert()/update()/delete()/query()/replace() is the method that
SQL statement is not used. When using them, data should be sent as per the following steps.
1. In case there’s data to insert/update to DB, register to ContentValues.
2. Send ContentValues as parameter, and execute a method for each process (In the following example,
SQLiteDatabase#insert())
Use case of metod for each process (SQLiteDatabase#insert())
222
Secure Coding Guide Documentation Release 2019-12-01
// Execute Insert
try {
mSampleDb.insert("SampleTable", null, insertValues);
} catch (SQLException e) {
Log.e(this.getClass().toString(),
getString(R.string.DB_INSERT_ERROR_MESSAGE));
return;
}
}
In this example, SQL command is not directly written, for instead, a method for inserting which SQLite-
Database provides, is used. SQL command is not directly used, so there’s no room for SQL injection in
this method, too.
4.5.3.1 When Using Wild Card in LIKE Predicate of SQL Statement, Escape Process Should Be
Implemented
When using character string which includes wild card (%, _) of LIKE predicate, as input value of place
holder, it will work as a wild card unless it is processed properly, so it’s necessary to implement escape
process in advance according the necessity. It is the case which escape process is necessary that wild
card should be used as a single character (“%” or “_”).
The actual escape process is executed by using ESCAPE clause as per below sample code.
Example of ESCAPE process in case of using LIKE
@Override
protected Cursor doInBackground(String... params) {
String idno = params[0];
String name = params[1];
String info = params[2];
String cols[] = {"_id", "idno","name","info"};
Cursor cur;
223
Secure Coding Guide Documentation Release 2019-12-01
try {
// Point: Use place holder
cur = mSampleDB.query("SampleTable", cols,
"info LIKE '%' || ? || '%' ESCAPE '@'",
selectionArgs, null, null, null);
} catch (SQLException e) {
Toast.makeText(mActivity,
R.string.SERCHING_ERROR_MESSAGE, Toast.LENGTH_LONG).show();
return null;
}
return cur;
}
@Override
protected void onPostExecute(Cursor resultCur) {
mProgressDialog.dismiss();
mActivity.updateCursor(resultCur);
}
}
4.5.3.2 Use External Input to SQL Command in which Place Holder Cannot Be Used
When executing SQL statement which process targets are DB objects like table creation/deletion etc.,
placeholder cannot be used for the value of table name. Basically, DB should not be designed using
arbitrary character string which was input from outside in case that placeholder cannot be used for the
value.
When placeholder cannot be used due to the restriction of specifications or features, whether the Input
value is dangerous or not, should be verified before execution, and it’s necessary to implement necessary
processes.
Basically,
1. When using as character string parameter, escape or quote process for character should be made.
2. When using as numeric value parameter, verify that characters other than numeric value are not
included.
3. When using as identifier or command, verify whether characters which cannot be used are not
included, along with 1.
should be executed.
Reference: https://www.ipa.go.jp/security/vuln/documents/website_security_sql.pdf
(Japanese)
object cannot be generated due to disc full etc., it will return Read- only object. (getWritableDatabase() will be execution
error under the situation like disc full etc.)
224
Secure Coding Guide Documentation Release 2019-12-01
Reference: https://developer.android.com/reference/android/database/sqlite/SQLiteOpen-
Helper.html - getReadableDatabase()
4.5.3.4 Verify the Validity of Input/Output Data of DB, According to Application’s Requirement
SQLite is the database which is tolerant types, and it can store character type data into columns which is
declared as Integer in DB. Regarding data in database, all data including numeric value type is stored in
DB as character data of plain text. So searching of character string type, can be executed to Integer type
column. (LIKE ‘%123%’ etc.) In addition, the limitation for the value in SQLite (validity verification)
is untrustful since data which is longer than limitation can be input in some case, e.g. VARCHAR(100).
So, applications which use SQLite, need to be very careful about this characteristics of DB, and it is
necessary take actions according to application requirements, not to store unexpected data to DB or not
to get unexpected data. Countermeasures are as per below 2 points.
1. When storing data in database, verify that type and length are matched.
2. When getting the value from database, verify whether data is beyond the supposed type and length,
or not.
Following is an example of the code which verifies that the Input value is more than 1.
Verify that the Input value is more than 1 (Extract from MainActivity.java)
public class MainActivity extends Activity {
225
Secure Coding Guide Documentation Release 2019-12-01
226
Secure Coding Guide Documentation Release 2019-12-01
Developed by Zetetic LLC, SQLCipher provides transparent 256-bit AES encryption of SQLite databases.
It is an SQLite extension library implemented in C language, and it uses OpenSSL for encryption. It
also provides APIs for Obj-C, Java, Python, and other languages. In addition to the commercial version,
an open source version (called “community edition”) is also available, and it can be used for commercial
purposes with a BSD license. It supports a wide range of platforms including Windows, Linux, macOS,
and more, and in the mobile space, besides Android, it is also widely used in Nokia / QT and Apple’s
iOS.
Among these versions, SQLCipher for Android was packaged specifically for Android use20 . Although
content can be created by compiling from the available source code, a library is also distributed in
AAR format (android-database-sqlcipher-xxxx.aar), and this may convenient for simple usage21 . Some
standard SQLite APIs can be changed to match SQLCipher to enable developers to use databases
encrypted with the same coding as usual. This section provides a brief introduction of how to use
libraries in AAR format.
Reference: https://www.zetetic.net/sqlcipher/
How to Use
dependencies {
:
implementation 'net.zetetic:android-database-sqlcipher:3.5.9@aar'
:
}
package android.jssec.org.samplesqlcipher;
import android.content.Context;
// instead of the normal android.database.sqlite*, import net.sqlcipher.database*
import net.sqlcipher.database.SQLiteDatabase;
import java.io.File;
227
Secure Coding Guide Documentation Release 2019-12-01
According to Android security designing idea, files are used only for making information persistence
and temporary save (cache), and it should be private in principle. Exchanging information between
applications should not be direct access to files, but it should be exchanged by inter-application linkage
system, like Content Provider or Service. By using this, inter-application access control can be achieved.
Since enough access control cannot be performed on external memory device like SD card etc., so it
should be limited to use only when it’s necessary by all means in terms of function, like when handling
huge size files or transferring information to another location (PC etc.). Basically, files that include
sensitive information should not be saved in external memory device. In case sensitive information needs
to be saved in a file of external device at any rate, counter-measures like encryption are necessary, but
it’s not referred here.
As mentioned above, files should be private in principle. However, sometimes files should be read
out/written by other applications directly for some reasons. File types which are categorized from the
security point of view and comparison are shown in Table 4.6.1. These are categorized into 4 types of
files based on the file storage location or access permission to other application. Sample code for each
file category is shown below and explanation for each of them are also added there.
228
Secure Coding Guide Documentation Release 2019-12-01
229
Secure Coding Guide Documentation Release 2019-12-01
This is the case to use files that can be read/written only in the same application, and it is a very safe
way to use files. In principle, whether the information stored in the file is public or not, keep files private
as much as possible, and when exchanging the necessary information with other applications, it should
be done using another Android system (Content Provider, Service.)
Points:
1. Files must be created in application directory.
2. The access privilege of file must be set private mode in order not to be used by other applications.
3. Sensitive information can be stored.
4. Regarding the information to be stored in files, handle file data carefully and securely.
PrivateFileActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.file.privatefile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.file);
/**
* Create file process
*
(continues on next page)
230
Secure Coding Guide Documentation Release 2019-12-01
finish();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
fis = openFileInput(FILE_NAME);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("PrivateFileActivity",
"failed to read file");
} finally {
if (fis != null) {
try {
(continues on next page)
231
Secure Coding Guide Documentation Release 2019-12-01
/**
* Delete file process
*
* @param view
*/
public void onDeleteFileClick(View view) {
mFileView.setText(R.string.file_view);
}
}
PrivateUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.file.privatefile;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
(continues on next page)
232
Secure Coding Guide Documentation Release 2019-12-01
startActivity(intent);
}
/**
* Call file Activity process
*
* @param view
*/
public void onCallFileActivityClick(View view) {
callFileActivity();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
fis = openFileInput(FILE_NAME);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("PrivateUserActivity",
"failed to read file");
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
android.util.Log.e("PrivateUserActivity",
"failed to close file");
}
}
}
}
/**
* Rewrite file process
(continues on next page)
233
Secure Coding Guide Documentation Release 2019-12-01
callFileActivity();
}
}
This is the case to use files to disclose the contents to unspecified large number of applications. If you
implement by following the below points, it’s also comparatively safe file usage method. Note that using
the MODE_WORLD_READABLE variable to create a public file is deprecated in API Level 17 and
later versions, and will trigger a security exception in API Level 24 and later versions, therefore, the
following sample code does not work ;thus file-sharing methods using Content Provider are preferable.
Points:
1. Files must be created in application directory.
2. The access privilege of file must be set to read only to other applications.
3. Sensitive information must not be stored.
4. Regarding the information to be stored in files, handle file data carefully and securely.
PublicFileActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
(continues on next page)
234
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.file.publicfile.readonly;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.file);
/**
* Create file process
*
* @param view
*/
public void onCreateFileClick(View view) {
FileOutputStream fos = null;
try {
// *** POINT 1 *** Files must be created in application directory.
// *** POINT 2 *** The access privilege of file must be set to read
// only to other applications.
// (MODE_WORLD_READABLE is deprecated API Level 17,
// don't use this mode as much as possible and exchange data by using
// ContentProvider().)
fos = openFileOutput(FILE_NAME, MODE_WORLD_READABLE);
235
Secure Coding Guide Documentation Release 2019-12-01
finish();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
fis = openFileInput(FILE_NAME);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("PublicFileActivity",
"failed to read file");
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
android.util.Log.e("PublicFileActivity",
"failed to close file");
}
}
}
}
/**
* Delete file process
*
* @param view
*/
public void onDeleteFileClick(View view) {
236
Secure Coding Guide Documentation Release 2019-12-01
mFileView.setText(R.string.file_view);
}
}
PublicUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.file.publicuser.readonly;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user);
mFileView = (TextView) findViewById(R.id.file_view);
}
237
Secure Coding Guide Documentation Release 2019-12-01
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
mFileView.setText("(File Activity does not exist)");
}
}
/**
* Call file Activity process
*
* @param view
*/
public void onCallFileActivityClick(View view) {
callFileActivity();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
File file = new File(getFilesPath(FILE_NAME));
fis = new FileInputStream(file);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
android.util.Log.e("PublicUserActivity", "no file");
} catch (IOException e) {
android.util.Log.e("PublicUserActivity", "failed to read file");
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
android.util.Log.e("PublicUserActivity",
"failed to close file");
}
}
}
}
/**
* Rewrite file process
*
* @param view
*/
public void onWriteFileClick(View view) {
(continues on next page)
238
Secure Coding Guide Documentation Release 2019-12-01
if (!exception)
callFileActivity();
}
try {
Context ctx = createPackageContext(TARGET_PACKAGE,
Context.CONTEXT_RESTRICTED);
File file = new File(ctx.getFilesDir(), filename);
path = file.getPath();
} catch (NameNotFoundException e) {
android.util.Log.e("PublicUserActivity", "no file");
}
return path;
}
}
This is the usage of the file which permits read-write access to unspecified large number of application.
Unspecified large number of application can read and write, means that needless to say. Malware can
also read and write, so the credibility and safety of data will be never guaranteed. In addition, even in
case of not malicious intention, data format in file or timing to write in cannot be controlled. So this
type of file is almost not practical in terms of functionality.
As above, it’s impossible to use read-write files safely from both security and application designing points
of view, so using read-write files should be avoided.
Point:
1. Must not create files that be allowed to read/write access from other applications.
239
Secure Coding Guide Documentation Release 2019-12-01
This is the case when storing files in an external memory like SD card. It’s supposed to be used when
storing comparatively huge information (placing file which was downloaded from Web), or when bring
out the information to outside (backup etc.).
“External memory file (Read Write public)” has the equal characteristics with “Read Write public file” to
unspecified large number of applications. In addition, it has the equal characteristics with “Read Write
public file” to applications which declares to use android.permission.WRITE_EXTERNAL_STORAGE
Permission. So, the usage of “External memory file (Read Write public) file” should be minimized as
less as possible.
A Backup file is most probably created in an external memory device as Android application’s cus-
tomary practice. However, as mentioned as above, files in an external memory have the risk that is
tampered/deleted by other applications including malware. Hence, in applications which output backup,
some contrivances to minimize risks in terms of application spec or designing like displaying a caution
“Copy Backup files to the safety location like PC etc., a.s.a.p.”, are necessary.
Because the filtered view for external storage (see “4.6.3.6. About specifications related to access to external
storage in Android 10 (API Level 29)”) is used as the default in Android 10 (API level 29), the following
sample code (user side) does not run. However, the manifest attribute requestLegacyExternalStorage
can be set to temporarily opt out of the scoped storage function. This is used only for temporary
applications before the app is fully compatible or before app testing, and its use in the release version
and other versions is not allowed. In the next major platform release, it is expected that scoped storage
will be required in all apps regardless of the target SDK level.
Points:
1. Sensitive information must not be stored.
2. Files must be stored in the unique directory per application.
3. Regarding the information to be stored in files, handle file data carefully and securely.
4. Writing file by the requesting application should be prohibited as the specification.
Sample code for create
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.file.externalfile" >
directories on external storage media, need not to require the permission and it should␣
˓→declare
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name=".ExternalFileActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
240
Secure Coding Guide Documentation Release 2019-12-01
ExternalFileActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.file.externalfile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.file);
/**
* Create file process
*
* @param view
*/
public void onCreateFileClick(View view) {
FileOutputStream fos = null;
try {
// *** POINT 1 *** Sensitive information must not be stored.
(continues on next page)
241
Secure Coding Guide Documentation Release 2019-12-01
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("ExternalFileActivity",
"failed to read file");
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
android.util.Log.e("ExternalFileActivity",
"failed to close file");
}
}
}
finish();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
File file = new File(getExternalFilesDir(TARGET_TYPE), FILE_NAME);
fis = new FileInputStream(file);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("ExternalFileActivity",
"failed to read file");
} finally {
if (fis != null) {
try {
fis.close();
(continues on next page)
242
Secure Coding Guide Documentation Release 2019-12-01
/**
* Delete file process
*
* @param view
*/
public void onDeleteFileClick(View view) {
mFileView.setText(R.string.file_view);
}
}
<!-- In Android 4.0.3 (API Level 14) and later, the permission for reading external storages
has been defined and the application should decalre that it requires the permission.
In fact in Android 4.4 (API Level 19) and later, that must be declared to read other␣
˓→directories
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name=".ExternalUserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
ExternalUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
(continues on next page)
243
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.file.externaluser;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user);
mFileView = (TextView) findViewById(R.id.file_view);
// Android 6.0 (API level 23) or later requires dangerous permission
// (in this case READ_EXTERNAL_STORAGE permission)
// must be granted at runtime by user.
// (Refer to “5.2.3.6. Modification to the Permission model Specifications
// in Android versions 6.0 and later”)
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
}
}
(continues on next page)
244
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[],
int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
finish();
}
}
}
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
mFileView.setText("(File Activity does not exist)");
}
}
/**
* Call file Activity process
*
* @param view
*/
public void onCallFileActivityClick(View view) {
callFileActivity();
}
/**
* Read file process
*
* @param view
*/
public void onReadFileClick(View view) {
FileInputStream fis = null;
try {
File file = new File(getFilesPath(FILE_NAME));
fis = new FileInputStream(file);
fis.read(data);
mFileView.setText(str);
} catch (FileNotFoundException e) {
mFileView.setText(R.string.file_view);
} catch (IOException e) {
android.util.Log.e("ExternalUserActivity",
"failed to read file");
} finally {
(continues on next page)
245
Secure Coding Guide Documentation Release 2019-12-01
/**
* Rewrite file process
*
* @param view
*/
public void onWriteFileClick(View view) {
@Override
public void onClick(DialogInterface dialog, int which) {
callFileActivity();
}
});
alertDialogBuilder.create().show();
try {
Context ctx = createPackageContext(TARGET_PACKAGE,
Context.CONTEXT_IGNORE_SECURITY);
File file = new File(ctx.getExternalFilesDir(TARGET_TYPE), filename);
path = file.getPath();
} catch (NameNotFoundException e) {
android.util.Log.e("ExternalUserActivity", "no file");
}
return path;
}
}
246
Secure Coding Guide Documentation Release 2019-12-01
2. Must Not Create Files that Be Allowed to Read/Write Access from Other Applications (Required)
3. Using Files Stored in External Device (e.g. SD Card) Should Be Requisite Minimum (Required)
4. Application Should Be Designed Considering the Scope of File (Required)
As mentioned in “4.6. Handling Files” and “4.6.1.1. Using Private Files,” regardless of the contents of the
information to be stored, files should be set private, in principle. From Android security designing point
of view, exchanging information and its access control should be done in Android system like Content
Provider and Service, etc., and in case there’s a reason that is impossible, it should be considered to be
substituted by file access permission as alternative method.
Please refer to sample code of each file type and following rule items.
4.6.2.2 Must Not Create Files that Be Allowed to Read/Write Access from Other Applications
(Required)
As mentioned in “4.6.1.3. Using Public Read/Write Files,” when permitting other applications to
read/write files, information stored in files cannot be controlled. So, sharing information by using
read/write public files should not be considered from both security and function/designing points of
view.
4.6.2.3 Using Files Stored in External Device (e.g. SD Card) Should Be Requisite Minimum (Re-
quired)
As mentioned in “4.6.1.4. Using Eternal Memory (Read Write Public) Files,” storing files in external
memory device like SD card, leads to holding the potential problems from security and functional points
of view. On the other hand, SD card can handle files which have longer scope, compared with application
directory, and this is the only one storage that can be always used to bring out the data to outside of
application. So, there may be many cases that cannot help using it, depends on application’s spec.
When storing files in external memory device, considering unspecified large number of applications and
users can read/write/delete files, so it’s necessary that application is designed considering the points as
per below as well as the points mentioned in sample code.
• Sensitive information should not be saved in a file of external memory device, in principle.
• In case sensitive information is saved in a file of external memory device, it should be encrypted.
• In case saving in a file of external memory device information that will be trouble if it’s tampered
by other application or users, it should be saved with electrical signature.
• When reading in files in external memory device, use data after verifying the safety of data to read
in.
• Application should be designed supposing that files in external memory device can be always
deleted.
Please refer to “4.6.2.4. Application Should Be Designed Considering the Scope of File (Required).”
Data saved in application directory is deleted by the following user operations. It’s consistent with the
application’s scope, and it’s distinctive that it’s shorter than the scope of application.
• Uninstalling application.
• Delete data and cache of each application. (“Setting” > “Apps” > “select target application”)
247
Secure Coding Guide Documentation Release 2019-12-01
Files that were saved in external memory device like SD card, it’s distinctive that the scope of the file
is longer than the scope of the application. In addition, the following situations are also necessary to be
considered.
• File deletion by user
• Pick off/replace/unmount SD card
• File deletion by Malware
As mentioned above, since scope of files are different depends on the file saving location, not only from
the viewpoint to protect sensitive information, but also form view point to achieve the right behavior as
application, it’s necessary to select the file save location.
There is a method to share files through file descriptor, not letting other applications access to public
files. This method can be used in Content Provider and in Service. Opponent application can read/write
files through file descriptors which are got by opening private files in Content Provider or in Service.
Comparison between the file sharing method of direct access by other applications and the file sharing
method via file descriptor, is as per below Table 4.6.2. Variation of access permission and range of
applications that are permitted to access, can be considered as merits. Especially, from security point of
view, this is a great merit that, applicaions that are permitted to accesss can be controlled in detail.
This is common in both of above file sharing methods, when giving write permission for files to other
applications, integrity of file contents are difficult to be guaranteed. When several applications write in
in parallel, there’s a risk that data structure of file contents are destroyed, and application doesn’t work
normally. So, in sharing files with other applications, giving only read only permission is preferable.
Herein below an implementation example of file sharing by Content Provider and its sample code, are
published.
Point
1. The source application is In house application, so sensitive information can be saved.
2. Even if it’s a result from In house only Content Provider application, verify the safety of the result
data.
InhouseProvider.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
(continues on next page)
248
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.file.inhouseprovider;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
} else {
// Certificate hash value of keystore "my company key"
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public boolean onCreate() {
File dir = getContext().getFilesDir();
FileOutputStream fos = null;
(continues on next page)
249
Secure Coding Guide Documentation Release 2019-12-01
} catch (IOException e) {
android.util.Log.e("InhouseProvider", "failed to read file");
} finally {
try {
fos.close();
} catch (IOException e) {
android.util.Log.e("InhouseProvider", "failed to close file");
}
}
return true;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
@Override
public String getType(Uri uri) {
return "";
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
250
Secure Coding Guide Documentation Release 2019-12-01
InhouseUserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.file.inhouseprovideruser;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.View;
import android.widget.TextView;
if (sMyCertHash == null) {
if (Utils.isDebuggable(context)) {
// Certificate hash value of debug.keystore "androiddebugkey"
(continues on next page)
251
Secure Coding Guide Documentation Release 2019-12-01
}
}
return sMyCertHash;
}
logLine("[ReadFile]");
return;
}
if (pfd != null) {
FileInputStream fis = new FileInputStream(pfd.getFileDescriptor());
if (fis != null) {
try {
byte[] buf = new byte[(int) fis.getChannel().size()];
fis.read(buf);
// *** POINT 2 *** Handle received result data carefully and
// securely, even though the data came from in-house
// applications.
(continues on next page)
252
Secure Coding Guide Documentation Release 2019-12-01
} else {
logLine(" null file descriptor");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLogView = (TextView) findViewById(R.id.logview);
}
Herein above, security considerations are explained, focusing on files. It’s also necessary to consider the
security for directory which is a file container. Herein below, security considerations of access permission
setting for directory are explained.
In Android, there are some methods to get/create subdirectory in application directory. The major ones
are as per below Table 4.6.3.
253
Secure Coding Guide Documentation Release 2019-12-01
Here especially what needs to pay attention is access permission setting by Context#getDir(). As
explained in file creation, basically directory also should be set private from the security designing point
of view. When sharing information depends on access permission setting, there may be an unexpected
side effect, so other methods should be taken as information sharing.
MODE_WORLD_READABLE
This is a flag to give all applications read-only permission to directory. So all applications can get file
list and individual file attribute information in the directory. Because secret files may not be placed in
these directories, in general this flag must not be used.22
MODE_WORLD_WRITEABLE
This flag gives other applications write permission to directory. All applications can cre-
ate/move23 /rename/delete files in the directory. These operations has no relation with access permission
setting (Read/Write/Execute) of file itself, so it’s necessary to pay attention that operations can be done
only with write permission to directory. This flag allows other apps to delete or replace files arbitrarily,
so in general it must not be used.22
Regarding Table 4.6.3 “Deletion by User”, refer to “4.6.2.4. Application Should Be Designed Considering
the Scope of File (Required).”
4.6.3.3 Access Permission Setting for Shared Preference and Database File
Shared Preference and database also consist of files. Regarding access permission setting what are
explained for files are applied here. Therefore, both Shared Preference and database, should be created
as private files same like files, and sharing contents should be achieved by the Android’s inter-application
linkage system.
Herein below, the usage example of Shared Preference is shown. Shared Preference is crated as private
file by MODE_PRIVATE.
Example of setting access restriction to Shared Preference file.
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
254
Secure Coding Guide Documentation Release 2019-12-01
4.6.3.4 Specification Change regarding External Storage Access in Android 4.4 (API Level 19) and
later
The specification regarding External Storage Access has been changed to the followings since Android
4.4 (API Level 19).
(1) In the case that the application needs read/write to its specific directories on external storage media,
the WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE permissions need not to be
declared with <uses-permission>. (Changed)
(2) In the case that the application needs read files on other directories than its specific directories
on external storage media, the READ_EXTERNAL_STORAGE permission needs to be declared with
<uses-permission>. (Changed)
(3) In the case that the application needs to write files on other directories than its specific directories
on the primary external storage media, the WRITE_EXTERNAL_STORAGE permission needs to be
declared with <uses-permission>.
(4) The application cannot write files on other directories than its specific directories on the secondary
external storage media.
In that specification, whether the permission requisitions are needed is determined according to the
version of Android OS. So in the case that the application supports the versions including Android 4.3
and 4.4, it could lead to a pleasant situation that the application requires the unnecessary permission
of users. Therefore, applications just corresponding to the paragraph (1) is recommended to use the
maxSdkVersion attribute of <uses-permission> like the below.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.file.externalfile" >
directories on external storage media, need not to require the permission and it should␣
˓→declare
<application
android:icon="@drawable/ic_launcher"
(continues on next page)
255
Secure Coding Guide Documentation Release 2019-12-01
4.6.3.5 Revised specifications in Android 7.0 (API Level 24) for accessing specific directories on
external storage media
On devices running Android 7.0 (API Level 24) or later, a new API known as Scoped Directory Ac-
cess API has been introduced. “Scoped Directory Access” allows the application to access to specific
directories on external storage media without permission.
Within Scoped Directory Access, a directory defined in the Environment class is passed as a parame-
ter to the StorageVolume#createAccessIntent method to create an Intent. By sending this Intent via
startActivityForResult, you can enable a situation in which a dialog box requesting access permission
appears on the terminal screen, and—if the user grants permission—the specified directories on each
storage volume become accessible.
If the location to be accessed by an app lies within one of the above directories, and if the app is running
on an Android 7.0 or later device, the use of Scoped Directory Access is recommended for the following
reasons. For apps that must continue to support pre-Android 7.0 devices, see the sample code in the
AndroidManifest listed in Section “4.6.3.4. Specification Change regarding External Storage Access in
Android 4.4 (API Level 19) and later”.
• When a Permission is granted to access external storage, the app is able to access directories other
than its intended destination.
• Using Storage Access Framework to require users to choose accessible directories results in a cum-
bersome procedure in which the user must configure a selector on each access. Also, when access to
the root directory of an external storage is granted, the entirety of that storage becomes accessible.
Because the StorageVolume#createAccessIntent method is deprecated as of Android 10 (API level 29),
Intent#ACTION_OPEN_DOCUMENT_TREE is used instead for generating Intent24 .
24 https://developer.android.com/reference/android/os/storage/StorageVolume
256
Secure Coding Guide Documentation Release 2019-12-01
4.6.3.6 About specifications related to access to external storage in Android 10 (API Level 29)
The method that apps access files in external storage of devices running Android 10 (API level 29) or
higher has been changed25 .
For apps targeting Android 10, a filtered view for displaying files in external storage is provided by
default. Each app can save the app files in the app-specific directory and constantly has read-write
access permissions for created files, and so permission does not need to be declared. When an app is
uninstalled, all files in the app-specific directory are cleaned up, and if any files will be left, they must
be saved within MediaStore.
To access files that other apps have created, both of the following conditions must be true.
1. The app has been granted the READ_EXTERNAL_STORAGE permission
2. The files reside in one of the following media collections: MediaStore.Images, MediaStore.Video,
or MediaStore.Audio
In order to access any other file (including files in a downloads directory), the app must use the Storage
Access Framework, which allows the user to select a specific file. Also, in the filtered view, the following
restrictions are imposed on media-related data.
• The ACCESS_MEDIA_LOCATION permission must be granted for referencing the Exif metadata
within image files
• The DATA column in the files in MediaStore is removed and cannot be used
• The MediaStore.Files table shows only images, videos, and audio files, and, for instance, the table
does not show PDF files
The methods for accessing files by apps that have a filtered view for external storage are summarized in
the following table.
Table 4.6.5: The methods for accessing files by apps that have a
filtered view for external storage26
File location Permission needed Method of access- Files removed when
ing27 app uninstalled?
App-specific directory None getExternalFilesDir() Yes
Media collections(pho- READ_EXTER- MediaStore No
tos, videos, audio) NAL_STORAGE
only when accessing
other apps’ files
Downloads(documents None Storage Access Frame- No
and e-books) work
(loads system’s file
picker)
In apps targeting Android 9 (API level 28) and lower, the storage default operation does not change
from previous Android versions even in Android 10.
In the next major platform release, it is expected that scoped storage will be required in all apps regardless
of the target SDK level. Therefore, you should ensure that your app works with scoped storage well in
advance.
To test whether an app targeting Android 9 or lower behaves normally when using scoped storage, set
the value for the requestLegacyExternalStorage manifest attribute to false to opt in to the scope storage
function.
25 https://developer.android.com/preview/privacy/scoped-storage
26 https://developer.android.com/preview/privacy/scoped-storage#filtered-view-summary
27 The Storage Access Framework can be used to access each of the locations shown in the preceding table without
257
Secure Coding Guide Documentation Release 2019-12-01
Android application can be designed to launch from browser corresponding with a webpage link. This
functionality is called ‘Browsable Intent.’ By specifying URI scheme in Manifest file, an application
responds the transition to the link (user tap etc.) which has its URI scheme, and the application is
launched with the link as a parameter.
In addition, the method to launch the corresponding application from browser by using URI scheme is
supported not only in Android but also in iOS and other platforms, and this is generally used for the
linkage between Web application and external application, etc. For example, following URI scheme is
defined in Twitter application or Facebook application, and the corresponding applications are launched
from the browser both in Android and in iOS.
It seems very convenient function considering the linkage and convenience, but there are some risks
that this function is abused by a malicious third party. What can be supposed are as follows, they
abuse application functions by preparing a malicious Web site with a link in which URL has incorrect
parameter, or they get information which is included in URL by cheating a smartphone owner into
installing the Malware which responds the same URI scheme.
There are some points to be aware when using ‘Browsable Intent’ against these risks.
Sample codes of an application which uses ‘Browsable Intent’ are shown below. Install ‘Starter.html’ on
the web server and run it.
Points:
1. (Webpage side) Sensitive information must not be included.
2. Handle the URL parameter carefully and securely.
Starter.html
<html>
<body>
<!-- *** POINT 1 *** Sensitive information must not be included. -->
<!-- Character strings to be passed as URL parameter, should be UTF -8 and URI encoded.␣
˓→-->
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.browsableintent" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:allowBackup="false" >
<activity
android:name=".BrowsableIntentActivity"
android:label="@string/title_activity_browsable_intent"
(continues on next page)
258
Secure Coding Guide Documentation Release 2019-12-01
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<!-- Accept implicit Intent -->
<category android:name="android.intent.category.DEFAULT" />
<!-- Accept Browsable intent -->
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accept URI 'secure://jssec' -->
<data android:scheme="secure" android:host="jssec"/>
</intent-filter>
</activity>
</application>
</manifest>
BrowsableIntentActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.browsableintent;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_browsable_intent);
259
Secure Coding Guide Documentation Release 2019-12-01
4.7.2.1 (Webpage side) Sensitive Information Must Not Be Included in Parameter of Corresponding
Link (Required)
When tapping the link in browser, an intent which has a URL value in its data (It can be retrieve by
Intent#getData) is issued, and an application which has a corresponding Intent Filter is launched from
Android system.
At this moment, when there are several applications which Intent Filter is set to receive the same URI
scheme, application selection dialogue is shown in the same way as normal launch by implicit Intent,
and an application which user selected is launched. In case that a Malware is listed in the selection
of application selection dialogue, there is a risk that user may launch the Malware by mistake and
parameters in URL are sent to Malware.
As per above, it is necessary to avoid from include sensitive information directly in URL parameter as it
is for creating general Webpage link since all parameters which are included in Webpage link URL can
be given to Malware.
Example that User ID and Password are included in URL.
insecure://sample/login?userID=12345&password=abcdef
In addition, there is a risk that user may launch a Malware and input password to it when it is defined in
specs that password input is executed in an application after being launched by ‘Browsable Intent’, even
if the URL parameter includes only non-sensitive information like User ID. So it should be considered
that specs like a whole Login process is completed within application side. It must be kept in mind when
designing an application and a service that launching application by ‘Browsable Intent’ is equivalent to
launching by implicit Intent and there is no guarantee that a valid application is launched.
URL parameters which are sent to an application are not always from a legitimate Web page, since a
link which is matched with URI scheme can be made by not only developers but anyone. In addition,
there is no method to verify whether the URL parameter is sent from a valid Web page or not.
So it is necessary to verify safety of a URL parameter before using it, e.g. check if an unexpected value
is included or not.
There’s a logging mechanism called LogCat in Android, and not only system log information but also
application log information are also output to LogCat. Log information in LogCat can be read out from
260
Secure Coding Guide Documentation Release 2019-12-01
other application in the same device28 , so the application which outputs sensitive information to Logcat,
is considered that it has the vulnerability of the information leakage. The sensitive information should
not be output to LogCat.
From a security point of view, in release version application, it’s preferable that any log should not be
output. However, even in case of release version application, log is output for some reasons in some
cases. In this chapter, we introduce some ways to output messages to LogCat in a safe manner even in a
release version application. Along with this explanation, please refer to “4.8.3.1. Two Ways of Thinking
for the Log Outputting in Release version application”.
Herein after, the method to control the Log output to LogCat by ProGuard in release version application.
ProGuard is one of the optimization tools which automatically delete the unnecessary code like unused
methods, etc.
There are five types of log output methods, Log.e(), Log.w(), Log.i(), Log.d(), Log.v(), in android.util.Log
class. Regarding log information, intentionally output log information (hereinafter referred to as the
Operation log information) should be distinguished from logging which is inappropriate for a release
version application such as debug log (hereinafter referred to as the Development log information). It’s
recommended to use Log.e()/w()/i() for outputting operation log information, and to use Log.d()/v() for
outputting development log. Refer to “4.8.3.2. Selection Standards of Log Level and Log Output Method”
for the details of proper usage of five types of log output methods, in addition, also refer to “4.8.3.3.
DEBUG Log and VERBOSE Log Are Not Always Deleted Automatically”.
here’s an example of how to use LogCat in a safe manner. This example includes Log.d() and Log.v() for
outputting debug log. If the application is for release, these two methods would be deleted automatically.
In this sample code, ProGuard is used to automatically delete code blocks where Log.d()/v() is called.
Points:
1. Sensitive information must not be output by Log.e()/w()/i(), System.out/err.
2. Sensitive information should be output by Log.d()/v() in case of need.
3. The return value of Log.d()/v() should not be used (with the purpose of substitution or compari-
son).
4. When you build an application for release, you should bring the mechanism that automatically
deletes inappropriate logging method like Log.d() or Log.v() in your code.
5. An APK file for the (public) release must be created in release build configurations.
ProGuardActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(continues on next page)
28 The log information output to LogCat can be read by applications that declare using READ_LOGS permission.
However, in Android 4.1 and later, log information that is output by other application cannot be read. But smartphone
user can read every log information output to logcat through ADB.
261
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.log.proguard;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_proguard);
proguard-project.txt
# prevent from changing class name and method name etc.
-dontobfuscate
# *** POINT 4 *** In release build, the build configurations in which Log.d()/v()
# are deleted automatically should be constructed.
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}
*** Point 5 *** An APK file for the (public) release must be created in release build configurations.
262
Secure Coding Guide Documentation Release 2019-12-01
The difference of LogCat output between development version application (debug build) and release
version application (release build) are shown in below Fig. 4.8.2
Fig. 4.8.2: Difference of LogCat output between development version application and release version
application
4.8.2.1 Sensitive Information Must Not Be Included in Operation Log Information (Required)
Log which was output to LogCat can be read out from other applications, so sensitive information like
user’s login information should not be output by release version application. It’s necessary not to write
code which outputs sensitive information to log during development, or it’s necessary to delete all of
such codes before release.
To follow this rule, first, not to include sensitive information in operation log information. In addition,
it’s recommended to construct the system to delete code which outputs sensitive information when build
263
Secure Coding Guide Documentation Release 2019-12-01
for release. Please refer to “4.8.2.2. Construct the Build System to Auto-delete Codes which Output
Development Log Information When Build for the Release (Recommended)”.
4.8.2.2 Construct the Build System to Auto-delete Codes which Output Development Log Informa-
tion When Build for the Release (Recommended)
When application development, sometimes it’s preferable if sensitive information is output to log for
checking the process contents and for debugging, for example the interim operation result in the process
of complicated logic, information of program’s internal state, communication data structure of commu-
nication protocol. It doesn’t matter to output the sensitive information as debug log during developing,
in this case, the corresponding log output code should be deleted before release, as mentioned in “4.8.2.1.
Sensitive Information Must Not Be Included in Operation Log Information (Required)”.
To delete surely the code which outputs development log information when release builds, the system
which executes code deletion automatically by using some tools, should be constructed. ProGuard,
which was described in “4.8.1. Sample Code”, can work for this method. As described below, there
are some noteworthy points on deleting code by ProGuard. Here it’s supposed to apply the system
to applications which output development log information by either of Log.d()/v(), based on “4.8.3.2.
Selection Standards of Log Level and Log Output Method”.
ProGuard deletes unnecessary code like unused methods, automatically. By specifying Log.d()/v() as
parameter of -assumenosideeffects option, call for Log.d(), Log.v() are granted as unnecessary code, and
those are to be deleted.
By specifying -assumenosideeffects to Log.d()/v(), make it auto-deletion target.
In case using this auto deletion system, pay attention that Log.v()/d() code is not deleted when using
returned value of Log.v(), Log.d(), so returned value of Log.v(), Log.d(), should not be used. For
example, Log.v() is not deleted in the next examination code.
Examination code which Log.v() that is specifeied to be deleted is not deketed.
If you’d like to reuse source code, you should keep the consistency of the project environment including
ProGuard settings. For example, source code that presupposes Log.d() and Log.v() are deleted auto-
matically by above ProGuard setting. If using this source code in another project which ProGuard is
not set, Log.d() and Log.v() are not to be deleted, so there’s a risk that the sensitive information may be
leaked. When reusing source code, the consistency of project environment including ProGuard setting
should be secured.
As mentioned in “4.8.1. Sample Code” and “4.8.3.2. Selection Standards of Log Level and Log Output
Method”, sensitive information should not be output to log through Log.e()/w()/i(). On the other hand,
in order that a developer wants to output the details of program abnormality to log, when exception
occurs, stack trace is output to LogCat by Log.e(…, Throwable tr)/w(…, Throwable tr)/i(…, Throwable
tr), in some cases. However, sensitive information may sometimes be included in the stack trace because
it shows detail internal structure of the program. For example, when SQLiteException is output as it
is, what type of SQL statement is issued is clarified, so it may give the clue for SQL injection attack.
Therefore, it’s recommended that use only Log.d()/Log.v() methods, when outputting throwable object.
264
Secure Coding Guide Documentation Release 2019-12-01
4.8.2.4 Use Only Methods of the android.util.Log Class for the Log Output (Recommended)
You may output log by System.out/err to verify the application’s behavior whether it works as expected
or not, during development. Of course, log can be output to LogCat by print()/println() method of
System.out/err, but it’s strongly recommended to use only methods of android.util.Log class, by the
following reasons.
When outputting log, generally, use the most appropriate output method properly based on the urgency
of the information, and control the output. For example, categories like serious error, caution, simple
application’s information notice, etc. are to be used. However, in this case, information which needs
to be output at the time of release (operation log information) and information which may include the
sensitive information (development log information) are output by the same method. So, it may happen
that when delete code which outputs sensitive information, it’s in danger that some deletion are dropped
by oversight.
Along with this, when using android.util.Log and System.out/err for log output, compared with using
only android.util.Log, what needs to be considered will increase, so it’s in danger that some mistakes
may occur, like some deletion are dropped by oversight.
To decrease risk of above mentioned mistakes occurrence, it’s recommended to use only methods of
android.util.Log class.
4.8.3.1 Two Ways of Thinking for the Log Outputting in Release version application
There are two ways of thinking for log output in release version application. One is any log should never
be output, and another is necessary information for later analysis should be output as log. It’s favorable
that any log should never be output in release version application from the security point of view, but
sometimes, log is output even in release version application for various reasons. Each way of thinking is
described as per below.
The former is “Any log should never be output”, this is because outputting log in release version applica-
tion is not so much valuable, and there is a risk to leak sensitive information. This comes from there’s no
method for developers to collect log information of the release version application in Android application
operation environment, which is different from many Web application operation environments. Based
on this thinking, the logging codes are used only in development phase, and all the logging codes are
deleted on building release version application.
The latter is “necessary information should be output as log for the later analysis”, as a final option
to analyze application bugs in customer support, in case of any questions or doubt to your customer
support. Based on this idea, as introduced above, it is necessary to prepare the system that prevent
human errors and bring it in your project because if you don’t have the system you have to keep in mind
to avoid logging the sensitive information in release version application.
For more details about logging method, refer to the following document.
Code Style Guidebook for Contributors / Log Sparingly
https://source.android.com/setup/contribute/code-style#log-sparingly
There are five levels of log level (ERROR, WARN, INFO, DEBUG, VERBOSE) are defined in an-
droid.util.Log class in Android. You should select the most appropriate method when using the an-
droid.util.Log class to output log messages according to Table 4.8.1 which shows the selection standards
of logging levels and methods.
265
Secure Coding Guide Documentation Release 2019-12-01
Table 4.8.1: Selection standards of log levels and log output method
Log level Method Log information to be output Cautions for application release
ERROR Log.e() Log information which is output Log information as per left may be
when application is in a fatal state. referred by users, so it could be out-
put both in development version ap-
WARN Log.w() Log information which is output plication and in release version ap-
when application faces the unex- plication. Therefore, sensitive infor-
pected serious situation. mation should not be output in these
INFO Log.i() Other than above, log information levels.
which is output to notify any re-
markable changes or results in ap-
plication state.
DEBUG Log.d() Program’s internal state information Log information as per left is only
which needs to be output temporar- for application developers. There-
ily for analyzing the cause of specific fore, this type of information should
bug when developing application. not be output in case of release ver-
VER- Log.v() Log information which is not applied sion application.
BOSE to any of above. Log information
which application developer outputs
for many purposes, is applied this.
For example, in case of outputting
server communication data to dump.
For more details about logging method, refer to the following document.
Code Style Guidelines for Contributors / Log Sparingly
https://source.android.com/setup/contribute/code-style#log-sparingly
4.8.3.3 DEBUG Log and VERBOSE Log Are Not Always Deleted Automatically
If you build the following code with ProGuard for the purpose of deleting Log.d() method, it is necessary
to remember that ProGuard keeps the statement that construct the string for logging message (the first
line of the code) even though it remove the statement of calling Log.d() method (the second line of the
code).
29 https://developer.android.com/reference/android/util/Log.html
266
Secure Coding Guide Documentation Release 2019-12-01
The following disassembly shows the result of release build of the code above with ProGuard. Actually,
there’s no Log.d() call process, but you can see that character string consistence definition like “Sensitive
information1” and calling process of String#format() method, are not deleted and still remaining there.
move-result-object v0
Actually, it’s not easy to find the particular part that disassembled APK file and assembled log output
information as above. However, in some application which handles the very confidential information,
this type of process should not be remained in APK file in some cases.
You should implement your application like below to avoid such a consequence of remaining the sensitive
information in bytecode30 . In release build, the following codes are deleted completely by the compiler
optimization.
if (BuildConfig.DEBUG) {
String debug_info = String.format("%s:%s",
"Sensitive information 1",
"Sensitive information 2");
if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info);
}
Besides, ProGuard cannot remove the log message of the following code(“result:” + value).
In this case, you can solve the problem in the following manner.
When using Activity, it’s necessary to pay attention, since ActivityManager outputs the content of Intent
to LogCat. Refer to “4.1.3.5. Log Output When using Activities”.
System.out/err method outputs all messages to LogCat. Android could send some messages to Sys-
tem.out/err even if developers did not use these methods in their code, for example, in the following
cases, Android sends stack trace to System.err method.
• When using Exception#printStackTrace()
30 The previous sample code is enclosed in an if statement with BuildConfig.DEBUG as conditional expression. The if
statement before the call to Log.d () is not necessary, but left as it is for comparison with the previous one.
267
Secure Coding Guide Documentation Release 2019-12-01
• When it’s output to System.err implicitly (When the exception is not caught by application, it’s
given to Exception#printStackTrace() by the system.)
You should handle errors and exceptions appropriately since the stack trace includes the unique infor-
mation of the application.
We introduce a way of changing default output destination of System.out/err. The following code
redirects the output of System.out/err method to nowhere when you build a release version application.
However, you should consider whether this redirection does not cause a malfunction of application
or system because the code temporarily overwrites the default behavior of System.out/err method.
Furthermore, this redirection is effective only to your application and is worthless to system processes.
OutputRedirectApplication.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.log.outputredirection;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import android.app.Application;
@Override
public void onCreate() {
// Redirect System.out/err to PrintStream which doesn't output anywhere,
// when release build.
// Restore the original stream only when debugging. (In release build,
// the following 1 line is deleted byProGuard.)
resetStreams(savedOut, savedErr);
(continues on next page)
268
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.log.outputredirection" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name=".OutputRedirectApplication"
android:allowBackup="false" >
<activity
android:name=".LogActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
proguard-project.txt
# Prevent from changing class name and method name, etc.
-dontobfuscate
The difference of LogCat output between development version application (debug build) and release
version application (release build) are shown as per below Fig. 4.8.3.
269
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.8.3: Difference of System.out/err in LogCat output, between development application and release
application.
We need to take proper action, depending on what we’d like to show through WebView although we can
easily show web site and html file by it. And also we need to consider risk from WebView’s remarkable
function; such as JavaScript-Java object bind.
Especially what we need to pay attention is JavaScript. (Please note that JavaScript is disabled as
default. And we can enable it by WebSettings#setJavaScriptEnabled()). With enabling JavaScript,
there is potential risk that malicious third party can get device information and operate your device.
The following is principle for application with WebView31 :
1. You can enable JavaScript if the application uses contents which are managed in house.
2. You should NOT enable JavaScript other than the above case.
Fig. 4.9.1 shows flow chart to choose sample code according to content characteristic.
31 Strictly speaking, you can enable JavaScript if we can say the content is safe. If the contents are managed in house,
the contents should be guaranteed of security. And the company can secure them. In other words, we need to have business
representation’s decision to enable JavaScript for other company’s contents. The contents which are developed by trusted
partner might have security guarantee. But there is still potential risk. Therefore the decision is needed by responsible
person.
270
Secure Coding Guide Documentation Release 2019-12-01
4.9.1.1 Show Only Contents Stored under assets/res Directory in the APK
You can enable JavaScript if your application shows only contents stored under assets/ and res/ directory
in apk.
The following sample code shows how to use WebView to show contents stored under assets/ and res/.
Points:
1. Disable to access files (except files under assets/ and res/ in apk).
2. You may enable JavaScript.
WebViewAssetsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.webview.assets;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
271
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 1 *** Disable to access files (except files under assets/
// and res/ in this apk)
webSettings.setAllowFileAccess(false);
You can enable JavaScript to show only contents which are managed in-house only if your web service
and your Android application can take proper actions to secure both of them.
• Web service side actions:
As Fig. 4.9.2 shows, your web service can only refer to contents which are managed in-house. In addition,
the web service is needed to take appropriate security action. Because there is potential risk if contents
which your web service refers to may have risk; such as malicious attack code injection, data manipulation,
etc.
Please refer to “4.9.2.1. Enable JavaScript Only If Contents Are Managed In-house (Required)”.
• Android application side actions:
Using HTTPS, the application should establish network connection to your managed web service only if
the certification is trusted.
The following sample code is an activity to show contents which are managed in-house.
272
Secure Coding Guide Documentation Release 2019-12-01
Points:
1. Handle SSL error from WebView appropriately.
2. (Optional) Enable JavaScript of WebView.
3. Restrict URLs to HTTPS protocol only.
4. Restrict URLs to in-house.
WebViewTrustedContentsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.webview.trustedcontents;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Bundle;
import android.webkit.SslErrorHandler;
(continues on next page)
273
Secure Coding Guide Documentation Release 2019-12-01
import java.text.SimpleDateFormat;
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view,
SslErrorHandler handler,
SslError error) {
// *** POINT 1 *** Handle SSL error from WebView appropriately
// Show SSL error dialog.
AlertDialog dialog = createSslErrorDialog(error);
dialog.show();
274
Secure Coding Guide Documentation Release 2019-12-01
return result.toString();
case SslError.SSL_IDMISMATCH:
result.append("Host name doesn't match. \n\nCN=").append(cert.getIssuedTo().
˓→getCName());
return result.toString();
case SslError.SSL_NOTYETVALID:
result.append("The certificate isn't valid yet.\n\nIt will be valid from ").
˓→append(dateFormat.format(cert.getValidNotBeforeDate()));
return result.toString();
case SslError.SSL_UNTRUSTED:
result.append("Certificate Authority which issued the certificate is not reliable.
˓→\n\nCertificate Authority\n").append(cert.getIssuedBy().getDName());
return result.toString();
default:
result.append("Unknown error occured. ");
return result.toString();
}
}
}
Don’t enable JavaScript if your application shows contents which are not managed in house because
there is potential risk to access to malicious content.
The following sample code is an activity to show contents which are not managed in-house.
This sample code shows contents specified by URL which user inputs through address bar. Please note
that JavaScript is disabled and connection is aborted when SSL error occurs. The error handling is
the same as “4.9.1.2. Show Only Contents which Are Managed In-house” for the details of HTTPS
communication. Please refer to “5.4. Communicating via HTTPS” for the details also.
Points:
1. Handle SSL error from WebView appropriately.
2. Disable JavaScript of WebView.
WebViewUntrustActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(continues on next page)
275
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.webview.untrust;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Bundle;
import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import java.text.SimpleDateFormat;
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
webView.loadUrl(url);
textUrl.setText(url);
return true;
}
276
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webView.loadUrl(getString(R.string.texturl));
textUrl = (EditText) findViewById(R.id.texturl);
buttonGo = (Button) findViewById(R.id.go);
}
switch (error.getPrimaryError()) {
case SslError.SSL_EXPIRED:
result.append("The certificate is no longer valid.\n\nThe expiration date is ").
˓→append(dateFormat.format(cert.getValidNotAfterDate()));
return result.toString();
case SslError.SSL_IDMISMATCH:
result.append("Host name doesn't match. \n\nCN=").append(cert.getIssuedTo().
˓→getCName());
277
Secure Coding Guide Documentation Release 2019-12-01
return result.toString();
case SslError.SSL_UNTRUSTED:
result.append("Certificate Authority which issued the certificate is not reliable.
˓→\n\nCertificate Authority\n").append(cert.getIssuedBy().getDName());
return result.toString();
default:
result.append("Unknown error occured. ");
return result.toString();
}
}
}
What we have to pay attention on WebView is whether we enable the JavaScript or not. As principle,
we can only enable the JavaScript only IF the application will access to services which are managed
in-house. And you must not enable the JavaScript if there is possibility to access services which are not
managed in-house.
In case that application accesses contents which are developed IN HOUSE and are distributed through
servers which are managed IN HOUSE, we can say that the contents are ONLY modified by your
company. In addition, it is also needed that each content refers to only contents stored in the servers
which have proper security.
In this scenario, we can enable JavaScript on the WebView. Please refer to “4.9.1.2. Show Only Contents
which Are Managed In-house” also.
And you can also enable JavaScript if your application shows only contents stored under assets/ and
res/ directory in the apk. Please refer to “4.9.1.1. Show Only Contents Stored under assets/res Directory
in the APK ” also.
You must NOT think you can secure safety on contents which are NOT managed IN HOUSE. Therefore
you have to disable JavaScript. Please refer to “4.9.1.3. Show Contents which Are Not Managed In-house”.
In addition, you have to disable JavaScript if the contents are stored in external storage media; such as
microSD because other application can modify the contents.
278
Secure Coding Guide Documentation Release 2019-12-01
4.9.2.2 Use HTTPS to Communicate to Servers which Are Managed In-house (Required)
You have to use HTTPS to communicate to servers which are managed in-house because there is potential
risk of spoofing the services by malicious third party.
Please refer to both “4.9.2.4. Handle SSL Error Properly (Required)”, and “5.4. Communicating via
HTTPS”.
4.9.2.3 Disable JavaScript to Show URLs Which Are Received through Intent, etc. (Required)
Don’t enable JavaScript if your application needs to show URLs which are passed from other application
as Intent, etc. Because there is potential risk to show malicious web page with malicious JavaScript.
Sample code in the section “4.9.1.2. Show Only Contents which Are Managed In-house”, uses fixed value
URL to show contents which are managed in-house, to secure safety.
If you need to show URL which is received from Intent, etc., you have to confirm that URL is in managed
URL in-house. In short, the application has to check URL with white list which is regular expression,
etc. In addition, it should be HTTPS.
You have to terminate the network communication and inform error notice to user when SSL error
happens on HTTPS communication.
SSL error shows invalid server certification risk or MTIM (man-in-the-middle attack) risk. Please note
that WebView has NO error notice mechanism regarding SSL error. Therefore your application has to
show the error notice to inform the risk to the user. Please refer to sample code in the section of “4.9.1.2.
Show Only Contents which Are Managed In-house”, and “4.9.1.3. Show Contents which Are Not Managed
In-house”.
In addition, your application MUST terminate the communication with the error notice.
In other words, you MUST NOT do following.
• Ignore the error to keep the transaction with the service.
• Retry HTTP communication instead of HTTPS.
Please refer to the detail described in “5.4. Communicating via HTTPS”.
WebView’s default behavior is to terminate the communication in case of SSL error. Therefore what we
need to add is to show SSL error notice. And then we can handle SSL error properly.
Android versions under 4.2API Level 17 have a vulnerability caused by addJavascriptInterface(), which
could allow attackers to call native Android methods (Java) via JavaScript on WebView.
As explained in “4.9.2.1. Enable JavaScript Only If Contents Are Managed In-house (Required)”,
JavaScript must not be enabled if the services could access services out of in-house control.
In Android 4.2API Level 17 or later, the measure of the vulnerability has been taken to limit access
from JavaScript to only methods with @JavascriptInterface annotation on Java source codes instead of
all methods of Java objects injected. However it is necessary to disable JavaScript if the services could
access services out of in-house control as mentioned in “4.9.2.1.”.
279
Secure Coding Guide Documentation Release 2019-12-01
In case of using WebView with default settings, all files that the app has access rights can be accessed to
by using the file scheme in web pages regardless of the page origins. For example, a malicious web page
could access the files stored in the app’s private directory by sending a request to the uri of a private file
of the app with the file scheme.
A countermeasure is to disable JavaScript as explained in “4.9.2.1. Enable JavaScript Only If Contents
Are Managed In-house (Required)” if the services could access services out of in-house control. Doing
that is to protect against sending the malicious file scheme request.
Also in case of Android 4.1 (API Level 16) or later, setAllowFileAccessFromFileURLs() and setAllowU-
niversalAccessFromFileURLs() can be used to limit access via the file scheme.
Disabling the file scheme
Android 6.0 (API Level 23) adds an API for realizing HTML5 Web Messaging. Web Messaging is a
framework defined in HTML5 for sending and receiving data between different browsing contexts32 .
The postWebMessage() method added to the WebView class is a method for processing data transmis-
sions via the Cross-domain messaging protocol defined by Web Messaging.
This method sends a message object—specified by its first parameter—from the browsing context that
has been read into WebView; however, in this case it is necessary to specify the origin of the sender as
the second parameter. If the specified origin33 does not agree with the origin in the sender context, the
message will not be sent. By placing restrictions on the sender origin in this way, this mechanism aims
to prevent the passing of messages to unintended senders.
However, it is important to note that wildcards may be specified as the origin in the postWebMessage()
method34 . If wildcards are specified, the sender origin of the message is not checked, and the message
may be sent from any arbitrary origin. In a situation in which malicious content has been read into
WebView, various types of harm or damage may result if important messages are sent without origin
restrictions. Thus, when using WebView for Web messaging, it is best to specify explicitly a specific
origin in the postWebMessage() method.
Safe Browsing is a service provided by Google that displays a warning page when the user tries to access
a malware page, phishing site, or other unsafe web page.
32 https://www.w3.org/TR/webmessaging/
33 An “origin” is a URL scheme together with a host name and port number. For the detailed definition see http:
//tools.ietf.org/html/rfc6454.
34 Note that Uri.EMPTY and Uri.parse(“”) function as wildcards (at the time of writing the September 1, 2016 version).
280
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.9.3: Warning page displayed when attempting to access an unsafe web page in Chrome for Android
Currently, the Safe Browsing function can be used not only in Chrome for Android and other browser
applications, but also in the WebView used in applications. However, careful attention is needed because
the components that can be used for WebView vary depending on the Android OS version of the system,
as a result, the degree of support for Safe Browsing also varies. Support for standard WebView and Safe
Browsing by Android OS versions are shown in the following table.
Before Android 4.3 (API level 18), a WebView that did not include the Safe Browsing function was
incorporated into the OS, and this was changed in Android 4.4 (API level 19) so that WebView included
the Safe Browsing function. Even so, care is needed because the version is old, and it does not support
use of the Safe Browsing function in the WebView of applications.
The capability to use the Safe Browsing function in applications started from Android 5.0 (API level 21)
when WebView was separated from the OS and became updated as an application.
Starting from WebView 66, Safe Browsing is enabled by default, and no special settings are required at
the application side. However, it is possible that Safe Browsing may not be enabled by default for some
281
Secure Coding Guide Documentation Release 2019-12-01
WebView versions if the user did not update WebView or if the standard WebView in the “Set WebView
implementation” option for developers was changed from the default. And so, if Safe Browsing is used,
it must be explicitly enabled as shown below.
Settings for enabling Safe Browsing in AndroidManifest.xml
<meta-data
android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
</application>
</manifest>
Also, in Android 8.0 (API level 26), several APIs for Safe Browsing were added.
The setSafeBrowsingEnabled(boolean enabled) added in the WebSettings class is a setting method for
dynamically enabling or disabling each WebView instance. Before Android 8.0 (API level 26), the Safe
Browsing function was enabled or disabled by settings in AndroidManifest, but this could only make
settings for all WebViews in an application. The setSafeBrowsingEnabled(boolean enabled) can be used
to allow dynamic enable/disable switching for each WebView instance.
if (url == IN_HOUSE_MANAGEMENT_CONTENT_URL) {
// (ex.) because in-house contents are detectable by Safe Browsing,
// diable it temporarily
webView.getSettings().setSafeBrowsingEnabled(false);
} else {
// normally, it should be enabled
webView.getSettings().setSafeBrowsingEnabled(true);
}
Also, in Android 8.1 (API level 27), classes and APIs for Safe Browsing were added. These enable
specifying of the Safe Browsing initialization process, settings for responses taken when accessing an
unsafe web page, setting of a whitelist for excluding specific sites from Safe Browsing, and more.
The startSafeBrowsing() added in the WebView class is a method that calls the Safe Browsing initial-
ization process for WebView components used for the WebView in applications. The initialization result
is passed to the callback object that is passed by the 2nd argument, and so if initialization fails, and
false is passed to the callback object, responses such as disabling WebView or not loading the URL are
recommended.
Similarly, the setSafeBrowsingWhitelist() added in the WebView class is a method that sets host names
and IP addresses that are excluded from Safe Browsing in a whitelist format. When a list of the host
282
Secure Coding Guide Documentation Release 2019-12-01
names and IP addresses to be excluded from Safe Browsing is passed as an argument, no verification is
conducted using Safe Browsing when they are accessed.
// setting the white list of the pair of host name and Ip address which is
// excluded from Safe Browsing
// (ex.) because in-house contents are detectable by Safe Browsing, register
// them to white list
WebView.setSafeBrowsingWhitelist(new ArrayList<>(Arrays.asList( IN_HOUSE_MANAGEMENT_CONTENT_
˓→HOSTNAME )),
new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean aBoolean) {
Log.i("WebView SafeBrowsing", "Whitelisted " + aBoolean.toString());
}
});
The onSafeBrowsingHit() added in the WebClient class is a callback function that is called back when
it is determined that a URL accessed in a WebView where Safe Browsing is enabled is an unsafe web
page. The object of the WebView that accessed the unsafe web page is passed to the 1st argument,
WebResourceRequest is passed to the 2nd argument, the type of threat is passed to the 3rd argument,
and the SafeBrowsingResponse object for setting the response when determining that a page is unsafe
is passed to the 4th argument.
The response when using the SafeBrowsingResponse object can be selected from the three options below.
• backToSafety(boolean report): Returns to the previous page without displaying a warning (If no
previous page is available, a blank page is displayed.)
• proceed(boolean report): Ignores the warning and displays the web page.
• showInterstitial(boolean allowReporting): Displays the warning page (default response)
For backToSafety() and proceed(), an argument can be used to set whether a report is sent to Google,
and an argument can be set for showInterstitial() to display “a checkbox for selecting whether a report
is sent to Google”.
public class MyWebViewClient extends WebViewClient {
// When Safe Browsing function is enabled, accessing unsafe web page will
// cause this callback to be ivoked
@Override
public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
int threatType, SafeBrowsingResponse callback) {
// Display warning page with a check box which selects
// "Send report to Google" or not (Recommended)
callback.showInterstitial(true);
// Without displaying warning page, return back to the safe page,
// and send a report to Google (Recommended)
callback.backToSafety(true);
// Ignoring the warning, access to the page, and send a report to
// Google (Not recommended)
callback.proceed(false);
}
}
No Android Support Library is available that supports these classes and APIs. For this reason, to operate
applications using these classes and APIs in systems that are below API level 26 or 27, the processes
must be separated based on the version or similar measures are required.
Sample code is shown below for handling of access to unsafe web pages when Safe Browsing is used in
WebView.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
(continues on next page)
283
Secure Coding Guide Documentation Release 2019-12-01
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Explicitly enable the Safe Browsing function of WebView in the application process -->
<meta-data
android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
</application>
</manifest>
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.webview.safebrowsing;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import java.util.ArrayList;
import java.util.Arrays;
284
Secure Coding Guide Documentation Release 2019-12-01
findViewById(R.id.button1).setOnClickListener(setWhiteList);
findViewById(R.id.button2).setOnClickListener(reload);
mSafeBrowsingIsInitialized = false;
// Because Safe Browsing is not supported on a device below Android 8.1,
// real implementation needs to check Android OS version of the device
WebView.startSafeBrowsing(this, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean result) {
mSafeBrowsingIsInitialized = true;
if (result) {
Log.i("WebView SafeBrowsing", "Initialized SafeBrowsing!");
webView.loadUrl("http://testsafebrowsing.appspot.com/s/malware.html");
} else {
Log.w("WebView SafeBrowsing", "SafeBrowsing initialization failed...");
// When the initilization failed, Safe Browsing might not work
// properly. In this case, it is advaisable not to load URL.
}
}
});
}
@Override
public void onReceiveValue(Boolean aBoolean) {
Log.i("WebView SafeBrowsing", "Whitelisted " + aBoolean.toString());
}
});
}
};
MyWebViewClient.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
(continues on next page)
285
Secure Coding Guide Documentation Release 2019-12-01
import android.webkit.SafeBrowsingResponse;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
// When Safe Browsing is enabled, accessing unsafe Web page will cause this
// callback to be invoked
@Override
public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
int threatType, SafeBrowsingResponse callback) {
// Do not display warningpage, and return back to safe page
callback.backToSafety(true);
Toast.makeText(view.getContext(), "Because the visiting web page is suspicious to be a␣
˓→malware site, we are returning back to the safe page.", Toast.LENGTH_LONG).show();
}
}
Android offers the Notification feature for sending messages to end users. Using a Notification causes a
region known as a status bar to appear on the screen, inside which you may display icons and messages.
286
Secure Coding Guide Documentation Release 2019-12-01
The communication functionality of Notifications is enhanced in Android 5.0 (API Level 21) to allow
messages to be displayed via Notifications even when the screen is locked, depending on user and ap-
plication settings. However, incorrect use of Notifications runs the risk that private information—which
should only be shown to the terminal user herself—may be seen by third parties. For this reason, this
functionality must be implemented with careful attention paid to privacy and security.
The possible values for the Visibility option and the corresponding behavior of Notifications is summa-
rized in the following table.
When a Notification contains private information regarding the terminal user, a message from which the
private information has been excluded must be prepared and added to be displayed in the event of a
locked screen.
287
Secure Coding Guide Documentation Release 2019-12-01
Sample code illustrating the proper use of Notifications for messages containing private data is shown
below.
Points:
1. When using Notifications for messages containing private data, prepare a version of the Notification
that is suitable for public display (to be displayed when the screen is locked).
2. Do not include private information in Notifications prepared for public display (displayed when the
screen is locked).
3. Explicitly set Visibility to Private when creating Notifications.
4. When Visibility is set to Private, Notifications may contain private information.
VisibilityPrivateNotificationActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.notification.visibilityPrivate;
import android.app.Activity;
import android.app.Notification;
(continues on next page)
288
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
289
Secure Coding Guide Documentation Release 2019-12-01
290
Secure Coding Guide Documentation Release 2019-12-01
PendingIntent pendingIntent =
PendingIntent.getBroadcast(this, 0, intent, 0);
Notification.Action actionReply =
new Notification.Action.Builder(icon, REPLY_LABEL, pendingIntent)
.addRemoteInput(remoteInput)
.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY)
.build();
privateNotificationBuilder.addAction(actionReply);
}
NotificationManager notificationManager =
(NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(mNotificationId, privateNotification);
}
}
291
Secure Coding Guide Documentation Release 2019-12-01
3. For Notifications that contain private information, Visibility must be explicitly set to Private or
Secret (Required)
4. When using Notifications with Visibility=Private, create an additional Notification with Visibil-
ity=Public for public display (Recommended)
4.10.2.1 Regardless of the Visibility setting, Notifications must not contain sensitive information
(although private information is an exception) (Required)
On terminals using Android4.3 (API Level 18) or later, users can use the Settings window to grant apps
permission to read Notifications. Apps granted this permission will be able to read all information in
Notifications; for this reason, sensitive information must not be included in Notifications. (However,
private information may be included in Notifications depending on the Visibility setting).
Information contained in Notifications may generally not be read by apps other than the app that
sent the Notification. However, users may explicitly grant permission to certain user-selected apps to
read all information in Notifications. Because only apps that have been granted user permission may
read information in Notifications, there is nothing problematic about including private information on
the user within the Notification. On the other hand, if sensitive information other than the user’s
private information (for example, secret information known only to the app developers) is include in a
Notification, the user herself may attempt to read the information contained in the Notification and may
grant applications permission to view this information as well; thus the inclusion of sensitive information
other than private user information is problematic.
For specific methods and conditions, see Section “4.10.3.1. On User-granted Permission to View Notifi-
cations”
4.10.2.2 Notifications with Visibility=Public must not contain private information (Required)
When sending Notifications with Visibility=Public, private user information must not be included in the
Notification. When a Notifications has the setting Visibility=Public, the information in the Notification
is displayed even when the screen is locked. This is because such Notifications carry the risk that private
information might be seen and stolen by a third party in physical proximity to the terminal.
VisibilityPrivateNotificationActivity.java
// Prepare a Notification for public display (to be displayed on locked
// screens) that does not contain sensitive information.
Notification.Builder publicNotificationBuilder =
new Notification.Builder(this).setContentTitle("Notification : Public");
publicNotificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
// Do not include private information in Notifications for public display
// (to be displayed on locked screens).
publicNotificationBuilder.setContentText("Visibility Public: sending notification without␣
˓→sensitive information.");
publicNotificationBuilder.setSmallIcon(R.drawable.ic_launcher);
Typical examples of private information include emails sent to the user, the user’s location data, and
other items listed in Section “5.5. Handling privacy data”.
4.10.2.3 For Notifications that contain private information, Visibility must be explicitly set to Private
or Secret (Required)
Terminals using Android 5.0 (API Level 21) or later will display Notifications even when the screen
is locked. Thus, when the Notification contains private information, its Visibility flag should be set
explicitly to “Private” or “Secret”. This is to protect against the risk of private information contained in
a Notification being displayed on a locked screen.
292
Secure Coding Guide Documentation Release 2019-12-01
At present, the default value of Visibility is set to Private for Notifications, so the aforementioned risk
will only arise if this flag is explicitly changed to Public. However, the default value of Visibility may
change in the future; for this reason, and also for the purpose of clearly communicating one’s intentions at
all times when handling information, it is mandatory to set Visibility=Private explicitly for Notifications
that contain private information.
VisibilityPrivateNotificationActivity.java
// Create a Notification that includes private information.
Notification.Builder priavteNotificationBuilder =
new Notification.Builder(this).setContentTitle("Notification : Private");
4.10.2.4 When using Notifications with Visibility=Private, create an additional Notification with
Visibility=Public for public display (Recommended)
VisibilityPrivateNotificationActivity.java
// Create a Notification that contains private information.
Notification.Builder privateNotificationBuilder =
new Notification.Builder(this).setContentTitle("Notification : Private");
As noted above in Section “4.10.2.1. Regardless of the Visibility setting, Notifications must not contain
sensitive information (although private information is an exception) (Required)”, on terminals using
Android 4.3 (API Level 18) or later, certain user-selected apps that have been granted user permission
may read information in all Notifications.
293
Secure Coding Guide Documentation Release 2019-12-01
Fig. 4.10.3: “The Access to Notifications” window, from which Notification read controls may be config-
ured
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.notification.notificationListenerService">
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<service android:name=".MyNotificationListenerService"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name=
"android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
</manifest>
MyNotificationListenerService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
(continues on next page)
294
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.notification.notificationListenerService;
import android.app.Notification;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// Notification is deleted.
outputNotificationData(sbn, "Notification Deleted : ");
}
Log.d("NotificationListen", message);
}
}
Previously, the Android OS included a shared memory mechanism, and it was provided by an-
droid.os.MemoryFile. However, it did not directly provide APIs or access control for sharing over multiple
applications, and it was difficult to use for general applications. In Android 8.1 (API level 27), the an-
droid.os.SharedMemory package was introduced, which enabled the shared memory mechanism to be
used relatively easily from general applications. At the time of Android 8.1, MemoryFile is a wrapper
of SharedMemory, and use of SharedMemory is recommended. This section describes the important
security points when using this SharedMemory API.
As described later, this API was built assuming a structure where a provided application and memory
are shared when a service of an application creates a shared memory and provides this shared memory to
other applications. And so, all the information described in “4.4. Creating/Using Services” also applies
to applications that provide shared memory and applications that use this shared memory. If you have
not already read this information, it is recommended that you read “4.4. Creating/Using Services” before
proceeding to the explanation below.
295
Secure Coding Guide Documentation Release 2019-12-01
No Android Support Library is available that supports the SharedMemory API. For this reason, to
operate applications using SharedMemory in systems that are below API level 27, measures are required
such as by implementing an equivalent virtual memory mechanism, such as by wrapping C language level
APIs using JNI, and the processes must be separated based on the version.
Shared memory is a mechanism for sharing the same physical memory area among multiple applications.
The figure above shows the appearance when using a shared memory for application A and application B.
Application A creates a shared memory object, and it is provided to application B. The role of providing
shared memory by application A is handled as a service of application A. Application B connects to this
service, requests and obtains the shared memory, and after the processes required by the shared memory
are completed, application B notifies application A that use is completed.
For example, if handling data where the maximum size (1 MB35 ) for allowable communication between
normal processes is exceeded, such as bitmap data of a large image, shared memory can be used to enable
sharing among multiple processes. Also, the amount of memory used for the entire device can be reduced
for enabling normal memory access, and this allows for extremely high-speed communication between
processes. However, because multiple applications are simultaneously accessing in parallel, consideration
must also be made for maintaining the integrity of the data in certain cases. To avoid this, exclusive
control can be performed between applications, and other careful designs are needed to ensure that the
memory area is properly divided and the accessed areas do not interfere with each other.
As mentioned above, the shared memory API of Android SDK was built so that a service creates a shared
memory object and provides it to other processes. Because the shared memory class (android.os.Shared-
Memory) is defined as parcelable, the shared memory instance can be easily passed on to other processes
through binders. An overview of the exchanges between the service and client in the sample code ap-
pearing later has the structure shown in the figure below (this can vary significantly depending on the
structure of the service).
35 https://developer.android.com/guide/components/activities/parcelables-and-bundles
296
Secure Coding Guide Documentation Release 2019-12-01
297
Secure Coding Guide Documentation Release 2019-12-01
• S7. After the message that usage is completed is received from the client, the service itself also
unmaps and closes the shared memory.
The onServiceConnected() in item C2 above is defined as a class where the android.content.ServiceCon-
nection class is implemented. For specific examples, refer to the sample code appearing later. Several
communication methods using IBinder are available, but Messenger is used in the sample code.
As described before, the side that creates the shared memory and provides it to other applications is
implemented as a service. For this reason, from the standpoint of security for functions and information
sharing, there are no fundamental differences from the information contained in “4.4. Creating/Using
Services” Based on the classifications in 4.4., the figure below shows the process for determining who the
memory will be shared with.
Table 4.4.2 in “4.4.1. Sample Code” describes how a service is implemented, but for shared memory,
sharing with other applications must be implemented using a binder. And so, shared memory cannot be
implemented as a startService or IntentService service. For this reason, it is implemented as shown in
the table below.
The overall structure is virtually identical to that in “4.4.1. Sample Code” Also, because the items
specific to shared memory are the same in all cases, in the specific sample code, the items marked with
an asterisk in the above table indicate those that apply to in-house services only. For this reason, to use
shared memory in other cases, refer to the information from “4.4.1.1. Creating/Using Private Services”
to “4.4.1.3. Creating/Using Partner Services”.
298
Secure Coding Guide Documentation Release 2019-12-01
In this case, a structure is used that shares shared memory created by a private service between multiple
processes contained in the application. Also, this private service is started as a process independent from
the main process of the application.
Points:
1. The service that creates the shared memory is explicitly set to private by exported=”false”.
2. If a process in an application references data that was written by another process, the safety is
verified even if it is a process within the same application.
3. Sensitive information can be shared because the sharing of memory is a process within the same
application.
The sample code in “4.4.1.1. Creating/Using Private Services” used services by Intent, but for shared
memory, memory resources cannot be shared through Intent, and so a method based on local bind,
Message bind, or AIDL bind must be used.
As described in “4.4.1.2. Creating/Using Public Services,” a public service is a service which is assumed
to be used by an unspecified large number of applications. As a result, use by malware must also be
assumed. Generally, attention must be paid to the points mentioned in 4.4.1.2., but those points are
rephrased below from the standpoint of shared memory.
Points (Creating a Service):
1. Explicitly set to public using exported=”true”.
2. Verify the safety of parameters and data contained in requests and other operations for starting
services and sharing memory.
3. Sensitive information must not be shared using shared memory.
Points (Using a Service):
1. Sensitive information must not be written to shared memory.
2. Safety is verified when referencing data that was written by another application.
This information is virtually identical to the information shown in “4.4.1.3. Creating/Using Partner
Services”, but this is rephrased from the standpoint of shared memory for showing the following points
(Like the sample code in 4.4.1.3., this assumes use of the AIDL bind service)
Points (Creating a Service):
1. Do not define the Intent Filter, and explicitly declare exported=”true”.
2. Verify the requesting application’s certificate through a predefined whitelist.
3. onBind(onStartCommand, onHandleIntent) cannot be used to determine whether the requester is
a partner.
4. Verify the safety of received Intent even if the Intent was sent from a partner application.
5. Writing to the shared memory is permissible only for information that is allowed to be disclosed
to the partner application.
Points (Using a Service):
1. Verify that the certificate of the requesting partner service application is registered in the whitelist.
299
Secure Coding Guide Documentation Release 2019-12-01
2. Writing to the shared memory is permissible only for information that is allowed to be disclosed
to the requesting partner application.
3. Use explicit Intent to call a partner service.
4. Verify the safety of the data even if the data was written by a partner application.
This section presents an example where shared memory is provided by a service available as public,
but the shared memory is provided to an in-house application only. Like the example in “4.4.1.4.
Creating/Using In-house Services”, a Messenger bind service is used. The principles and settings for
the background are described in 4.4.1.4., and so refer to 4.4.1.4. first if you have not already read this
information.
Points are shown below, but items 1 to 5 and 7 are presented in “4.4.1.4. Creating/Using In-house
Services,” and item 6 is the only item specific to shared memory.
Points:
1. Define an in-house signature permission.
2. Request declaration of the in-house signature permission.
3. Do not define the Intent Filter, and explicitly declare exported=”true”.
4. Verify that the in-house signature permission is defined by an in-house application.
5. Verify the safety of received Intent even if the Intent was sent from an in-house application.
6. Before passing the shared memory on to a client, use SharedMemory#setProtect() to limit the
available operations by the client.
7. Sign the APK using the same developer key as the requesting application.
For purposes of simplification, this example defines the service that allocates the shared memory and the
activity that uses the service within the same application (service is started as a separate process within
the same application). For this reason, both the signature permission definition and use declaration are
contained in the manifest file.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.sharedmemory.inhouseservice.messenger">
android:protectionLevel="signature" />
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
(continues on next page)
300
Secure Coding Guide Documentation Release 2019-12-01
<!-- For purposes of simplification, make the service which provide shared memory to be a␣
˓→different process in the same application -->
<service android:name="org.jssec.android.sharedmemory.inhouseservice.messenger.SHMService"
android:exported="true"
android:permission="org.jssec.android.sharedmemory.inhouseservice.messenger.MY_
˓→PERMISSION"
android:process=".shmService" />
</application>
</manifest>
SHMService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.sharedmemory.inhouseservice.messenger;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.util.Log;
import android.widget.Toast;
import java.nio.ByteBuffer;
301
Secure Coding Guide Documentation Release 2019-12-01
} else {
// Hash value of the certificate "my company key" in keystore
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
302
Secure Coding Guide Documentation Release 2019-12-01
return null;
}
// *** POINT 5 *** Verify the safety of received Intent even if the Intent
// was sent from an in-house application
// Omitted because this is an sample code. Refer to
(continues on next page)
303
Secure Coding Guide Documentation Release 2019-12-01
// Mapping layout
// Offset must be page boundary
public static final int SHMEM1_BUF1_OFFSET = 0;
public static final int SHMEM1_BUF1_LENGTH = 1024;
public static final int SHMEM2_BUF1_OFFSET = 0;
public static final int SHMEM2_BUF1_LENGTH = 1024;
public static final int SHMEM2_BUF2_OFFSET = PAGE_SIZE;
public static final int SHMEM2_BUF2_LENGTH = 128;
return null;
} catch (IllegalArgumentException e){
Log.e(TAG, "map failed: " + e.getMessage());
return null;
}
Log.d(TAG, "mmap success: prot=" + prot);
return tBuf;
}
304
Secure Coding Guide Documentation Release 2019-12-01
// Free SharedMemory
private void deAllocateSharedMemory () {
if (mBufferMapped) {
if (mSHMem1 != null) {
if (m1Buffer1 != null) SharedMemory.unmap(m1Buffer1);
m1Buffer1 = null;
mSHMem1.close();
mSHMem1 = null;
}
if (mSHMem2 != null) {
if (m2Buffer1 != null) SharedMemory.unmap(m2Buffer1);
if (m2Buffer2 != null) SharedMemory.unmap(m2Buffer2);
m2Buffer1 = null;
m2Buffer2 = null;
mSHMem2.close();
mSHMem2 = null;
}
mBufferMapped = false;
}
}
@Override
public void onCreate() {
super.onCreate();
// *** POINT 6 *** Before passing the shared memory on to a client, use
// SharedMemory#setProtect() to limit the available operations by the
// client.
// Client can only read from mSHMem1
mSHMem1.setProtect(PROT_READ);
try {
// Pass the SharedMemory object to the client
Message sMsg = Message.obtain(null, SHMEM1, mSHMem1);
msg.replyTo.send(sMsg);
} catch (RemoteException e) {
Log.e(TAG, "Failed to share" + e.getMessage());
}
(continues on next page)
305
Secure Coding Guide Documentation Release 2019-12-01
// Provide SHMEM2
private void shareWith2(Message msg) {
if (!mBufferMapped) return;
// *** POINT 6 *** Before passing the shared memory on to a client, use
// SharedMemory#setProtect() to limit the available operations by the
// client.
// Client can write to mSHMem2
mSHMem2.setProtect(PROT_WRITE);
// Set messages to client in each buffer
final String greeting2 = "You can write here!";
m2Buffer1.putInt(greeting2.length());
m2Buffer1.put(greeting2.getBytes());
final String greeting3 = "From this point, I'll also write.";
m2Buffer2.putInt(greeting3.length());
m2Buffer2.put(greeting3.getBytes());
try {
// Pass the shared memory objects to the client
Message sMsg = Message.obtain(null, SHMEM2, mSHMem2);
msg.replyTo.send(sMsg);
} catch (RemoteException e){
Log.e(TAG, "failed to share mSHMem2" + e.getMessage());
}
}
// In this example, server side accepts two types of message from the client
// goReply() assumes that m1Buffer1 holds a data from the client
private void gotReply(Message msg) {
m1Buffer1.rewind();
String message = extractReply(m1Buffer1);
if (!message.equals(greeting)){
Log.e(TAG, "my message was overwritten: " + message);
}
}
// got Reply2() assumes m2Buffer1 holds a data from the client
private void gotReply2(Message msg) {
m2Buffer1.rewind();
String message = extractReply(m2Buffer1);
android.util.Log.d(TAG, "got a message of length " + message.length() +
(continues on next page)
306
Secure Coding Guide Documentation Release 2019-12-01
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
(continues on next page)
307
Secure Coding Guide Documentation Release 2019-12-01
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
308
Secure Coding Guide Documentation Release 2019-12-01
*** Point 7 *** When exporting an APK, sign the APK with the same developer key as the requesting
application.
Fig. 4.11.4: Signing the APK with the same developer key as the requesting application
Points:
8. Declare use of the in-house signature permission.
9. Verify that the in-house-defined signature permission is defined by the in-house application.
10. Verify that the destination application is signed by the in-house certificate.
11. Sensitive information can be sent because the destination application is in-house.
12. Use explicit Intent to call an in-house service.
13. Sign the APK using the same developer key as the destination application.
All the points shown here are the same as the points for the client in “4.4.1.4. Creating/Using In-house
Services”, and no points are specific to shared memory. Basic points on using shared memory are shown
309
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.sharedmemory.inhouseservice.messenger">
android:protectionLevel="signature" />
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="org.jssec.android.sharedmemory.inhouseservice.messenger.
˓→MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- For purposes of simplification, make the service which provide shared memory to be a␣
˓→different process in the same application -->
<service android:name="org.jssec.android.sharedmemory.inhouseservice.messenger.SHMService"
android:exported="true"
android:permission="org.jssec.android.sharedmemory.inhouseservice.messenger.MY_
˓→PERMISSION"
android:process=".shmService" />
</application>
</manifest>
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(continues on next page)
310
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.sharedmemory.inhouseservice.messenger;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.widget.Toast;
import android.util.Log;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
// SharedMemory objects
private SharedMemory myShared1;
private SharedMemory myShared2;
// ByteBuffers for mapping SharedMemories
private ByteBuffer mBuf1;
private ByteBuffer mBuf2;
311
Secure Coding Guide Documentation Release 2019-12-01
}
}
return sMyCertHash;
}
312
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doBindService ();
}
return;
}
// *** POINT 10 *** Verify that the destination application is signed
// by the in-house certificate.
if (!PkgCert.test(this, SHM_PACKAGE, myCertHash(this))) {
Toast.makeText(this, "Binding Service is not an in-house application.", Toast.
˓→LENGTH_LONG).show();
return;
}
}
Intent it = new Intent();
// *** POINT 11 *** Sensitive information can be sent because the
// destination application is in-house.
it.putExtra("PARAM", "Sensitive Information");
313
Secure Coding Guide Documentation Release 2019-12-01
314
Secure Coding Guide Documentation Release 2019-12-01
*** Point 13 *** When exporting an APK, sign the APK using the same developer key as the destination
application.
Fig. 4.11.5: Signing the APK with the same developer key as the destination application
315
Secure Coding Guide Documentation Release 2019-12-01
When using SharedMemory, the rules contained in the rule book (4.4.2. Rule Book) for the service must
be observed. In addition to the rule book, the following rules must also be observed.
1. Permissions are set properly by the side providing the shared memory for allowing access by the
using side (required)
2. All data in the shared memory is designed assuming that it will be read by sharing applications
(required)
4.11.3.1 Permissions are set properly by the side providing the shared memory for allowing access
by the using side (required)
When memory is shared, in the design of operations allowable in the memory, each application must limit
operations to the minimum required for preventing leaking, alteration, and corruption of information.
Services that create SharedMemory objects can use SharedMemory#setProtect() to limit the allowable
operations in the entire shared memory before sharing with other applications. The initial values for
the operations allowable in the SharedMemory object are read, write, and execute. Except for special
reasons, use of executable memory areas should be avoided in order to prevent execution of invalid
code36 . Also, if other applications need to write to the shared memory, a special-purpose shared memory
is created and provided separately for enabling safe sharing of memory.
The argument of SharedMemory#setProtect() is a logical OR for the bit flags (PROT_READ,
PROT_WRITE, PROT_EXEC) corresponding to read, write, and execute, respectively. An exam-
ple is shown below for allowing reading and writing only for the SharedMemory object shMem.
shMem.setProtect(PROT_READ | PROT_WRITE)
SharedMemory#map() must be executed beforehand in order to enable access by the client to areas
(all or part) within the shared memory. During this process, the allowable operations for the memory
are specified by an argument, but operations cannot be specified above those permitted by the service
beforehand using SharedMemory#setProtect(). For example, the client cannot specify write operations
when the service permits reading only. An example is shown below where the SharedMemory object
ashMem provided by the service performs map().
ByteBuffer mbuf;
// If the Service only allows READ from ashMem,
// the following code raises an exception
mbuf = ashMem.map(offset, length, PROT_WRITE);
At the client side, setProtect() can be called to redo the settings so that operations are allowed for the
entire shared memory, but like map(), the settings cannot be made to allow operations above those that
were permitted by the service.
4.11.3.2 All data in the shared memory is designed assuming that it will be read by sharing appli-
cations (required)
As described above, when memory is shared with other applications, the service can set the access
permissions (read, write, execute) for the shared memory beforehand. However, even if the flag is
set to PROT_WRITE only to allow writing only, in certain cases, reading of the memory cannot be
prohibited. In other words, if the memory management unit (MMU) being used by the device does not
support memory access that allows writing only, allowing writing for a certain memory area will also
allow reading. It is thought that a large number of devices actually have this configuration, and as a
result, design must be performed under the assumption that the contents of the shared memory will be
known by other applications.
36 For some devices (based on the CPU architecture that is used), if a certain memory area is readable, it automatically
becomes executable. However, even in these cases, writing can be prohibited for these areas to prevent writing of executable
code in these areas by other applications.
316
Secure Coding Guide Documentation Release 2019-12-01
Although PROT_NONE can be specified for the flag to prevent all operations, this defeats the purpose
of having a shared memory.
Up to this point, the memory-sharing mechanism where memory was shared among multiple applications
was described. However, in actuality, shared memory is a mechanism that shares the same physical
memory area among multiple processes. Each process maps the shared physical memory area to its
own address space for accessing (this is performed by SharedMemory#map()). For Android shared
memory, the mapped memory area (for Java language) is a single ByteBuffer object. (If shared memory
that exceeds the page size is allocated, typically, the shared physical memory area is not divided into
consecutive areas, but instead, it is divided into multiple non-consecutive pages. However, if mapped
onto a process address space, the memory area becomes consecutive address spaces.)
In Unix-based OS, including the Android OS, the connected terminal, USB device, or other peripheral
device is abstracted using the concept of device files, and the device is handled as a virtual file. Shared
memory in the Android OS is not an exception to this, and this handling corresponds to the device file
/dev/ashmem. When this device file is opened, the file descriptor is returned in the same way as when
a normal file is opened, and through this process, the shared memory is accessed. In the same way as
normal files, this file descriptor can use mmap() to map to the process address space. In Unix-based OS,
mmap() is the standard system call, and it obtains the file descriptors for devices files for a wide range
of devices and provides a function for mapping the device to the address space of the calling process.
This is also used for the shared memory of the Android OS. The mapped address space is visible as a
byte sequence from the program (ByteBuffer for Java as mentioned above, and char * at the C language
level).
317
Secure Coding Guide Documentation Release 2019-12-01
The sharing of memory between processes in this framework is equivalent to sharing the file descriptor
of /dev/asmem corresponding to this memory area37 . As a result, this enables low costs for sharing, and
after mapping to the address space of the process, this enables access at the same efficiency as normal
memory access.
37 The file descriptor is a unique value within the process, and so when it is passed to other processes, proper conversion
is required, but this does not need to be a consideration at the Android SDK API level.
318
Secure Coding Guide Documentation Release 2019-12-01
When creating password input screen, some points to be considered in terms of security, are described
here. Only what is related to password input is mentioned, here. Regarding how to save password,
another articles is planned to be published is future edition.
319
Secure Coding Guide Documentation Release 2019-12-01
Points:
1. The input password should be mask displayed (Display with *)
2. Provide the option to display the password in a plain text.
3. Alert a user that displaying password in a plain text has a risk.
Points: When handling the last Input password, pay attention the following points along with the above
points.
4. In the case there is the last input password in an initial display, display the fixed digit numbers of
black dot as dummy in order not that the digits number of last password is guessed.
5. When the dummy password is displayed and the “Show password” button is pressed, clear the last
input password and provide the state for new password input.
6. When last input password is displayed with dummy, in case user tries to input password, clear the
last input password and treat new user input as a new password.
password_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="10dp" >
320
Secure Coding Guide Documentation Release 2019-12-01
<!-- *** POINT 2 *** Provide the option to display the password in a plain text -->
<CheckBox
android:id="@+id/password_display_check"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/display_password" />
<!-- *** POINT 3 *** Alert a user that displaying password in a plain text has a risk. -->
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/alert_password" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="onClickCancelButton"
android:text="@android:string/cancel" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="onClickOkButton"
android:text="@android:string/ok" />
</LinearLayout>
</LinearLayout>
Implementation for 3 methods which are located at the bottom of PasswordActivity.java, should be
adjusted depends on the purposes.
• private String getPreviousPassword()
• private void onClickCancelButton(View view)
• private void onClickOkButton(View view)
PasswordActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
(continues on next page)
321
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.password.passwordinputui;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.Toast;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.password_activity);
// Set Disabling Screen Capture
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
// Get View
mPasswordEdit = (EditText) findViewById(R.id.password_edit);
mPasswordDisplayCheck =
(CheckBox) findViewById(R.id.password_display_check);
322
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
/**
* Process in case password is input
*/
private class PasswordEditTextWatcher implements TextWatcher {
323
Secure Coding Guide Documentation Release 2019-12-01
/**
* Process when check of password display option is changed.
*/
private class OnPasswordDisplayCheckedChangeListener
implements OnCheckedChangeListener {
/**
* Get the last Input password
*
* @return Last Input password
*/
private String getPreviousPassword() {
// When need to restore the saved password, return password character
// string
// For the case password is not saved, return null
return "hirake5ma";
}
/**
(continues on next page)
324
Secure Coding Guide Documentation Release 2019-12-01
/**
* Process when OK button is clicked
*
* @param view
*/
public void onClickOkButton(View view) {
// Execute necessary processes like saving password or using for
// authentication
if (mIsDummyPassword) {
// When dummy password is displayed till the final moment, grant last
// input password as fixed password.
password = getPreviousPassword();
} else {
// In case of not dummy password display, grant the user input
// password as fixed password.
password = mPasswordEdit.getText().toString();
}
// Close Activity
finish();
}
}
5.1.2.1 Provide the Mask Display Feature, If the Password Is Entered (Required)
Smartphone is often used in crowded places like in a train or in a bus, and the risk that password is
peeked by someone. So the function to mask display password is necessary as an application spec.
There are two ways to display the EditText as password: specifying this statically in the layout XML,
or specifying this dynamically by switching the display from a program. The former is achieved
by specifying “textPassword” for the android:inputType attribute or by using android:password at-
325
Secure Coding Guide Documentation Release 2019-12-01
tribute. The latter is achieved by using the setInputType() method of the EditText class to add Input-
Type.TYPE_TEXT_VARIATION_PASSWORD to its input type.
Sample code of each of them is shown below.
Masking password in layout XML.
password_activity.xml
<!-- Password input item -->
<!-- Set true for the android:password attribute -->
<EditText
android:id="@+id/password_edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_password"
android:inputType="textPassword" />
PasswordActivity.java
// Set password display type
// Set TYPE_TEXT_VARIATION_PASSWORD for InputType.
EditText passwordEdit = (EditText) findViewById(R.id.password_edit);
int type = InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD;
passwordEdit.setInputType(type);
Password input in Smartphone is done by touch panel input, so compared with keyboard input in PC,
miss input may be easily happened. Because of the inconvenience of inputting, user may use the simple
password, and it makes more dangerous. In addition, when there’s a policy like account is locked due
the several times of password input failure, it’s necessary to avoid from miss input as much as possible.
As a solution of these problems, by preparing an option to display password in a plain text, user can use
the safe password.
However, when displaying password in a plain text, it may be sniffed, so when using this option. It’s
necessary to call user cautions for sniffing from behind. In addition, in case option to display in a plain
text is implemented, it’s also necessary to prepare the system to auto cancel the plain text display like
setting the time of plain display. The restrictions for password plain text display are published in another
article in future edition. So, the restrictions for password plain text display are not included in sample
code.
326
Secure Coding Guide Documentation Release 2019-12-01
By specifying InputType of EditText, mask display and plain text display can be switched.
PasswordActivity.java
/**
* Process when check of password display option is changed.
*/
private class OnPasswordDisplayCheckedChangeListener implements
OnCheckedChangeListener {
327
Secure Coding Guide Documentation Release 2019-12-01
To prevent it from a password peeping out, the default value of password display option, should be set
OFF, when Activity is launched. The default value should be always defined as safer side, basically.
5.1.2.4 When Displaying the Last Input Password, Dummy Password Must Be Displayed (Required)
When specifying the last input password, not to give the third party any hints for password, it should
be displayed as dummy with the fixed digits number of mask characters (* etc.). In addition, in the case
pressing “Show password” when dummy display, clear password and switch to plain text display mode.
It can help to suppress the risk that the last input password is sniffed low, even if the device is passed
to a third person like when it’s stolen. FYI, In case of dummy display and when a user tries to input
password, dummy display should be cancelled, it necessary to turn the normal input state.
When displaying the last Input password, display dummy password.
PasswordActivity.java
@Override
public void onCreate(Bundle savedInstanceState) {
[...]
[...]
/**
* Get the last input password.
*
* @return the last input password
*/
private String getPreviousPassword() {
// To restore the saved password, return the password character string.
// For the case password is not saved, return null.
return "hirake5ma";
}
In the case of dummy display, when password display option is turned ON, clear the displayed contents.
328
Secure Coding Guide Documentation Release 2019-12-01
PasswordActivity.java
/**
* Process when check of password display option is changed.
*/
private class OnPasswordDisplayCheckedChangeListener implements
OnCheckedChangeListener {
[...]
In case of dummy display, when user tries to input password, clear dummy display.
PasswordActivity.java
// Key to save the state
private static final String KEY_DUMMY_PASSWORD = "KEY_DUMMY_PASSWORD";
[...]
@Override
public void onCreate(Bundle savedInstanceState) {
[...]
[...]
329
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
/**
* Process when inputting password.
*/
private class PasswordEditTextWatcher implements TextWatcher {
The representative example of where password input is required is login process. Here are some Points
that need cautions in Login process.
330
Secure Coding Guide Documentation Release 2019-12-01
In login process, need to input 2 information which is ID(account) and password. When login failure,
there are 2 cases. One is ID doesn’t exist. Another is ID exists but password is incorrect. If either
of these 2 cases is distinguished and displayed in a login failure message, attackers can guess “whether
the specified ID exists or not”. To stop this kind of guess, these 2 cases should not be specified in login
failure message, and this message should be displayed as per below.
Message example: Login ID or password is incorrect.
There is a function to perform auto login by omitting login ID/password input in the next time and later,
after successful login process has been completed once. Auto login function can omit the complicated
input. So the convenience will increase, but on the other hand, when a Smartphone is stolen, the risk
which is maliciously being used by the third party, will follow.
Only the use when damages caused by the malicious third party is somehow acceptable, or only in the
case enough security measures can be taken, auto login function can be used. For example, in the case
of online banking application, when the device is operated by the third party, financial damage may
be caused. So in this case, security measures are necessary along with auto login function. There are
some possible counter-measures, like “Require re-inputting password just before financial process like
payment process occurs”, “When setting auto login, call a user for enough attentions and prompt user
to secure device lock”, etc. When using auto login, it’s necessary to investigate carefully considering the
convenience and risks along with the assumed counter measures.
When changing the password which was once set, following input items should be prepared on the screen.
• Current password
• New password
• New password (confirmation)
When auto login function is introduced, there are possibilities that third party can use an application. In
that case, to avoid from changing password unexpectedly, it’s necessary to require the current password
input. In addition, to decrease the risk of getting into unserviceable state due to miss inputting new
password, it’s necessary to require new password input 2 times.
There is a setting in Android’s setting menu, called “Make passwords visible.” In case of Android 5.0,
it’s shown as below.
There is a setting in Android’s setting menu, called “Make passwords visible.” In case of Android 5.0,
it’s shown as below.
331
Secure Coding Guide Documentation Release 2019-12-01
When turning ON “Make passwords visible” setting, the last input character is displayed in a plain text.
After the certain time (about 2 seconds) passed, or after inputting the next character, the characters
which was displayed in a plain text is masked. When turning OFF, it’s masked right after inputting. This
setting affects overall system, and it’s applied to all applications which use password display function of
EditText.
In password input screens, passwords could be displayed in the clear on the screens. In such screens
as handle personal information, they could be leaked from screenshot files stored on external storage if
the screenshot function is stayed enable as default. Thus it is recommended to disable the screenshot
332
Secure Coding Guide Documentation Release 2019-12-01
function for such screens as password input screens. Screen capture can be disabled by using addFlag to
set FLAG_SECURE in WindowManager.
There are four types of Protection Level within permission and they consist of normal, dangerous, signa-
ture, and signatureOrSystem. In addition, “development”, “system”, and “appop” exist, but since they
are not used in general applications, explanation in this chapter is omitted. Depending on the Protec-
tion Level, permission is referred to as normal permission, dangerous permission, signature permission,
or signatureOrSystem permission. In the following sections, such names are used.
Android OS has a security mechanism called “permission” that protects its user’s assets such as contacts
and a GPS feature from a malware. When an application seeks access to such information and/or
features, which are protected under Android OS, the application needs to explicitly declare a permission
in order to access them. When an application, which has declared a permission that needs user’s consent
to be used, is installed, the following confirmation screen appears1 .
From this confirmation screen, a user is able to know which types of features and/or information an
application is trying to access. If the behavior of an application is trying to access features and/or
information that are clearly unnecessary, then there is a high possibility that the application is a malware.
Hence, as your application is not suspected to be a malware, declarations of permission to use needs to
be minimized.
1 In Android 6.0 (API Level 23) and later, the granting or refusal of user permissions does not occur when an app
is installed, but instead at runtime when then app requests permissions. For more details, see Section “5.2.1.4. Methods
for using Dangerous Permissions in Android 6.0 and later” and Section “5.2.3.6. Modifications to the Permission model
specifications in Android versions 6.0 and later”.
333
Secure Coding Guide Documentation Release 2019-12-01
Points:
1. Declare a permission used in an application with uses-permission.
2. Do not declare any unnecessary permissions with uses-permission.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.permission.usespermission" >
<!-- *** POINT 1 *** Declare a permission used in an application with uses-permission -->
<!-- Permission to access Internet -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- *** POINT 2 *** Do not declare any unnecessary permissions with uses-permission -->
<!-- If declaring to use Permission that is unnecessary for application behaviors, it gives␣
˓→users a sense of distrust. -->
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5.2.1.2 How to Communicate Between In-house Applications with In-house-defined Signature Per-
mission
Besides system permissions defined by Android OS, an application can define its own permissions as well.
If using an in-house-defined permission (it is an in-house-defined signature permission to be more precise),
you can build a mechanism where only communications between in-house applications is permitted. By
providing the composite function based on inter-application communication between multiple in-house
applications, the applications get more attractive and your business could get more profitable by selling
them as series. It is a case of using in-house-defined signature permission.
The sample application “In-house-defined Signature Permission (UserApp)” launches the sample appli-
cation “In-house-defined Signature Permission (ProtectedApp)” with Context.startActivity() method.
Both applications need to be signed with the same developer key. If keys for signing them are different,
the UserApp sends no Intent to the ProtectedApp, and the ProtectedApp processes no Intent received
from the UserApp. Furthermore, it prevents malwares from circumventing your own signature permission
using the matter related to the installation order as explained in the Advanced Topic section.
334
Secure Coding Guide Documentation Release 2019-12-01
Fig. 5.2.2: Communication Between In-house Applications with In-house-defined Signature Permission
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.permission.protectedapp" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- *** POINT 2 *** For a component, enforce the permission with its permission attribute␣
˓→ -->
<activity
android:name=".ProtectedActivity"
android:exported="true"
android:label="@string/app_name"
android:permission="org.jssec.android.permission.protectedapp.MY_PERMISSION" >
<!-- *** POINT 3 *** If the component is an activity, you must define no intent-filter --
˓→ >
</activity>
</application>
</manifest>
ProtectedActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
(continues on next page)
335
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.permission.protectedapp;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
} else {
// Certificate hash value of "my company key" of keystore
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mMessageView = (TextView) findViewById(R.id.messageView);
return;
}
336
Secure Coding Guide Documentation Release 2019-12-01
}
}
SigPerm.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.os.Build;
// Compare the actual hash value of pkgname with the correct hash
// value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct check is possible
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else(API Level < 28) use the facility of PkgCert
return correctHash.equals(PkgCert.hash(ctx, pkgname));
(continues on next page)
337
Secure Coding Guide Documentation Release 2019-12-01
} catch (NameNotFoundException e) {
return false;
}
}
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
(continues on next page)
338
Secure Coding Guide Documentation Release 2019-12-01
*** Point 5 *** When exporting an APK, sign the APK with the same developer key that applications
using the component have used.
Fig. 5.2.3: Sign the APK with the same developer key that applications using the component have used
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.permission.userapp" >
<!-- *** POINT 6 *** The same signature permission that the application uses must not be␣
˓→ defined -->
<!-- *** POINT 7 *** Declare the in-house permission with uses-permission tag -->
<uses-permission
android:name="org.jssec.android.permission.protectedapp.MY_PERMISSION" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
(continues on next page)
339
Secure Coding Guide Documentation Release 2019-12-01
UserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.permission.userapp;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
340
Secure Coding Guide Documentation Release 2019-12-01
}
}
return sMyCertHash;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
return;
}
return;
}
// *** POINT 10 *** Use an explicit intent when the destination component
// is an activity.
try {
Intent intent = new Intent();
intent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);
startActivity(intent);
} catch(Exception e) {
Toast.makeText(this,
String.format("Exception occurs:%s", e.getMessage()),
Toast.LENGTH_LONG).show();
}
}
}
PkgCertWhitelists.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
(continues on next page)
341
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.os.Build;
mWhitelists.put(pkgname, sha256);
return true;
}
// Compare the actual hash value of pkgname with the correct hash value.
if (Build.VERSION.SDK_INT >= 28) {
// ** if API Level >= 28, direct checking is possible
PackageManager pm = ctx.getPackageManager();
return pm.hasSigningCertificate(pkgname,
Utils.hex2Bytes(correctHash),
CERT_INPUT_SHA256);
} else {
// else use the facility of PkgCert
return PkgCert.test(ctx, pkgname, correctHash);
}
}
}
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
(continues on next page)
342
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
343
Secure Coding Guide Documentation Release 2019-12-01
*** Point 11 *** When generating an APK by [Build] -> [Generate Signed APK], sign the APK with
the same developer key that the destination application uses.
Fig. 5.2.4: Sign the APK with the same developer key that the destination application uses
APK signature scheme V3 was introduced in Android 9.0 (API level 28) for enabling signature key
rotation. At the same time, the package signature-related APIs were also updated2 . When examining
the changes from the standpoint of application signature verification, the hasSigningCertificate() method,
which is a new method in thePackageManager class, can now be used for verification. Specifically, this
can be substituted for processes such as those where the certificate used for the signature is obtained from
the verification target package where the sample code PkgCert class of the Guide was performed and the
hash value is calculated. This is applied in the SigPerm and PkgCertWhiteLists in the sample code shown
above, and for API level 28 and higher, this new method hasSigningCertificate() is used. Differences in
signature schemes and differences in verification as a result of multiple signatures are incorporated into
hasSigningCertificate(), and so if targeting API level 28 and higher, use of this is recommended3 .
We will provide an explanation on how to verify the hash value of an application’s certificate that appears
at different points in this Guidebook. Strictly speaking, the hash value means “the SHA256 hash value
of the public key certificate for the developer key used to sign the APK.”
Using a program called keytool that is bundled with JDK, you can get the hash value (also known as
certificate fingerprint) of a public key certificate for the developer key. There are various hash methods
such as MD5, SHA1, and SHA256 due to the differences in hash algorithm. However, considering
the security strength of the encryption bit length, this Guidebook recommends the use of SHA256.
Unfortunately, the keytool bundled to JDK6 that is used in Android SDK does not support SHA256 for
calculating hash values. Therefore, it is necessary to use the keytool that is bundled to JDK7 or later.
Example of outputting the content of a debugging certicate of an Android through a keytool
2 For the specific changes, refer to the Android Developers website (https://developer.android.com/reference/android/
content/pm/PackageManager).
3 As of the time of this writing, there is currently no available Android Support Library compatible with the android.con-
344
Secure Coding Guide Documentation Release 2019-12-01
Certificate fingerprint:
MD5: 8A:1A:E5:15:9A:2A:9A:45:C1:7F:30:EF:17:70:37:D1
SHA1: 25:BC:25:91:02:A4:DD:04:7D:17:70:EC:41:35:21:00:0C:0A:C7:F1
SHA256: 0E:FB:72:36:32:83:48:A9:89:71:8B:AD:DF:57:F5:44:D5:CC:B4:AE:B9:DB:
34:BC:1E:29:DD:26:F7:7C:82:55
Signatrue algorithm name: SHA1withRSA
Subject public key algorithm: 1024-bit RSA key
Version: 3
*******************************************
*******************************************
Without installing JDK7 or later, you can easily verify the certificate hash value by using JSSEC Cer-
tificate Hash Value Checker.
345
Secure Coding Guide Documentation Release 2019-12-01
This is an Android application that displays a list of certificate hash values of applications which are
installed in the device. In the Figure above, the 64-character hexadecimal notation string that is shown on
the right of “sha-256” is the certificate hash value. The sample code folder, “JSSEC CertHash Checker”
that comes with this Guidebook is the set of source codes. If you would like, you can compile the codes
and use it.
5.2.1.4 Methods for using Dangerous Permissions in Android 6.0 and later
Android 6.0 (API Level 23) incorporates modified specifications that are relevant to the implementation
of apps—specifically, to the times at which apps are granted permission.
Under the Permission model of Android 5.1 (API Level 22) and earlier versions (See section “5.2.3.6.
Modifications to the Permission model specifications in Android versions 6.0 and later”, all Permissions
declared by an app are granted to that app at the time of installation. However, in Android 6.0 and later
versions, app developers must explicitly implement apps in such a way that, for Dangerous Permissions,
the app requests Permission at appropriate times. When an app requests a Permission, a confirmation
window like that shown below is displayed to the Android OS user, requesting a decision from the user
as to whether or not to grant the Permission in question. If the user allows the use of the Permission,
the app may execute whatever operations require that Permission.
346
Secure Coding Guide Documentation Release 2019-12-01
The specifications are also modified regarding the units in which Permissions are granted. Previously, all
Permissions were granted simultaneously; in Android 6.0 (API Level 23) and later versions, Permissions
are granted by Permission Group. In Android 8.0 (API Level 26) and later versions, Permissions are
granted individually. In conjunction with this modification, users are now shown individual confirmation
windows for each Permission, allowing users to make more flexible decisions regarding the granting or
refusal of Permissions. App developers must revisit the specifications and design of their apps with full
consideration paid to the possibility that Permissions may be refused.
For details on the Permission model in Android 6.0 and later, see Section “5.2.3.6. Modifications to the
Permission model specifications in Android versions 6.0 and later”.
Points:
1. Apps declare the Permissions they will use
2. Do not declare the use of unnecessary Permissions
3. Check whether or not Permissions have been granted to the app
4. Request Permissions (open a dialog to request permission from users)
5. Implement appropriate behavior for cases in which the use of a Permission is refused
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.permission.permissionrequestingpermissionatruntime" >
<!-- *** POINT 1 *** Apps declare the Permissions they will use -->
<!-- Permission to read information on contacts (Protection Level: dangerous) -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- *** POINT 2 *** Do not declare the use of unnecessary Permissions -->
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
(continues on next page)
347
Secure Coding Guide Documentation Release 2019-12-01
</manifest>
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.permission.permissionrequestingpermissionatruntime;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
348
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onClick(View v) {
readContacts();
}
} else {
// Permission was previously granted
showContactList();
}
}
349
Secure Coding Guide Documentation Release 2019-12-01
1. System Dangerous Permissions of Android OS Must Only Be Used for Protecting User Assets
(Required)
2. Your Own Dangerous Permission Must Not Be Used (Required)
3. Your Own Signature Permission Must Only Be Defined on the Provider-side Application (Required)
4. Verify If the In-house-defined Signature Permission Is Defined by an In-house Application (Re-
quired)
5. Your Own Normal Permission Should Not Be Used (Recommended)
6. The String for Your Own Permission Name Should Be of an Extent of the Package Name of
Application (Recommended)
5.2.2.1 System Dangerous Permissions of Android OS Must Only Be Used for Protecting User
Assets (Required)
Since the use of your own dangerous permission is not recommended (please refer to “5.2.2.2. Your Own
Dangerous Permission Must Not Be Used (Required)”, we will proceed on the premise of using system
dangerous permission of Android OS.
Unlike the other three types of permissions, dangerous permission has a feature that requires the user’s
consent to the grant of the permission to the application. When installing an application on a device
that has declared a dangerous permission to use, the following screen will be displayed. Subsequently,
the user is able to know what level of permission (dangerous permission and normal permission) the
application is trying to use. When the user taps “install”, the application will be granted the permission
and then it will be installed.
An application can handle user assets and assets that the developer wants to protect. We must be aware
that dangerous permission can protect only user assets because the user is just who the granting of
permission is entrusted to. On the other hand, assets that the developer wants to protect cannot be
protected by the method above.
For example, suppose that an application has a Component that communicates only with an In-house
application, it doesn’t permit the access to the Component from any applications of the other companies,
350
Secure Coding Guide Documentation Release 2019-12-01
and it is implemented that it’s protected by dangerous permission. When a user grants permission to an
application of another company based on the user’s judgment, in-house assets that need to be protected
may be exploited by the application granted. In order to provide protection for in-house assets in such
cases, we recommend the usage of in-house-defined signature permission.
Even when in-house-defined Dangerous Permission is used, the screen prompt “Asking for the Allowance
of Permission from User” is not displayed in some cases. This means that at times the feature that asks
for permission based on the judgment of a user, which is the characteristic of Dangerous Permission, does
not function. Accordingly, the Guidebook will make the rule “In-house -defined dangerous permission
must not be used”.
In order to explain it, we assume two types of applications. The first type of application defines an
in-house dangerous permission, and it is an application that makes a Component, which is protected
by this permission, public. We call this ProtectedApp. The other is another application which we
call AttackerApp and it tries to exploit the Component of ProtectedApp. Also we assume that the
AttackerApp not only declares the permission to use it, but also defines the same permission.
AttackerApp can use the Component of a ProtectedApp without the consent of a user in the following
cases:
1. When the user installs the AttackerApp, the installation will be completed without the screen
prompt that asks for the user to grant the application the dangerous permission.
2. Similarly, when the user installs the ProtectedApp, the installation will be completed without any
special warnings.
3. When the user launches the AttackerApp afterwards, the AttackerApp can access the Component
of the ProtectedApp without being detected by the user, which can potentially lead to damage.
The cause of this case is explained in the following. When the user tries to install the AttackerApp
first, the permission that has been declared for usage with uses-permission is not defined on the par-
ticular device yet. Finding no error, Android OS will continue the installation. Since the user consent
for dangerous permission is required only at the time of installation, an application that has already
been installed will be handled as if it has been granted permission. Accordingly, if the Component of
an application which is installed later is protected with the dangerous permission of the same name,
the application which was installed beforehand without the user permission will be able to exploit the
Component.
Furthermore, since the existence of system dangerous permissions defined by Android OS is guaranteed
when an application is installed, the user verification prompt will be displayed every time an applica-
tion with uses-permission is installed. This problem arises only in the case of self-defined dangerous
permission.
At the time of this writing, no viable method to protect the access to the Component in such cases has
been developed yet. Therefore, your own dangerous permission must not be used.
5.2.2.3 Your Own Signature Permission Must Only Be Defined on the Provider-side Application
(Required)
As demonstrated in, “5.2.1.2. How to Communicate Between In-house Applications with In-house-defined
Signature Permission”, the security can be assured by checking the signature permission at the time
of executing inter-communications between In-house applications. When using this mechanism, the
definition of the permission whose Protection Level is signature must be written in AndroidManifest.xml
of the provider-side application that has the Component, but the user-side application must not define
the signature permission.
This rule is applied to signatureOrSystem Permission as well.
The reason for this is as follows.
351
Secure Coding Guide Documentation Release 2019-12-01
We assume that there are multiple user-side applications that have been installed prior to the provider-
side application and every user-side application not only has required the signature permission that
the provider-side application has defined, but also has defined the same permission. Under these cir-
cumstances, all user-side applications will be able to access the provider-side application just after the
provider-side application is installed. Subsequently, when the user-side application that was installed
first is uninstalled, the definition of the permission also will be deleted and then the permission will turn
out to be undefined. As a result, the remaining user-side applications will be unable to access to the
provider-side application.
In this manner, when the user-side application defines a self-defined permission, it can unexpectedly
turn out the permission to be undefined. Therefore, only the provider-side application providing the
Component that needs to be protected should define the permission, and defining the permission on the
user-side must be avoided.
By doing as mentioned just above, the self-defined permission will be applied by Android OS at the time
of the installation of the provider-side application, and the permission will turn out to be undefined at the
time of the uninstallation of the application. Therefore, since the existence of the permission’s definition
always corresponds to that of the provider-side application, it is possible to provide an appropriate
Component and protect it. Please be aware that this argument stands because regarding in-house-defined
signature permission the user-side application is granted the permission regardless of the installation order
of applications in inter-communication4 .
Actuality, you cannot say to be secure enough only by declaring a signature permission through Anroid-
Manifest.xml and protecting the Component with the permission. For the details of this issue, please
refer to, “5.2.3.1. Characteristics of Android OS that Avoids Self-defined Signature Permission and Its
Counter-measures” in the Advanced Topics section.
The following are the steps for using in-house-defined signature permission securely and correctly.
First, write as the followings in AndroidManifest.xml:
1. Define an in-house signature permission in the AndroidManifest.xml of the provider-side applica-
tion. (definition of permission) Example: <permission android:name=”xxx” android:protection-
Level=”signature” />
2. Enforce the permission with the permission attribute of the Component to be protected in the
AndroidManifest.xml of the provider-side application. (enforcement of permission) Example: <ac-
tivity android:permission=”xxx” ... >...</activity>
3. Declare the in-house-defined signature permission with the uses-permission tag in the Android-
Manifest.xml of every user-side application to access the Component to be protected. (declaration
of using permission) Example: <uses-permission android:name=”xxx” />
Next, implement the followings in the source code.
4. Before processing a request to the Component, first verify that the in-house-defined signature
permission has been defined by an in-house application. If not, ignore the request. (protection in
the provider-side component)
5. Before accessing the Component, first verify that the in-house-defined signature permission has
been defined by an in-house application. If not, do not access the Component (protection in the
user-side component).
Lastly, execute the following with the Signing function of Android Studio.
6. Sign APKs of all inter-communicating applications with the same developer key.
4 If using normal/dangerous permission, the permission will not be granted the user-side application if the user-side
application is installed before the provider-side application, the permission remains undefined. Therefore, the Component
cannot be accessed even after the provider-side application has been installed.
352
Secure Coding Guide Documentation Release 2019-12-01
Here, for specific points on how to implement “Verify that the in-house-defined signature permission
has been defined by an In house application”, please refer to “5.2.1.2. How to Communicate Between
In-house Applications with In-house-defined Signature Permission”.
This rule is applied to signatureOrSystem Permission as well.
An application can use a normal permission just by declaring it with uses-permission in AndroidMan-
ifest.xml. Therefore, you cannot use a normal permission for the purpose of protecting a Component
from a malware installed.
Furthermore, in the case of inter-application communication with self-defined normal permission, whether
an application can be granted the permission depends on the order of installation. For example, when
you install an application (user-side) that has declared to use a normal permission prior to another
application (provider-side) that possesses a Component which has defined the permission, the user-side
application will not be able to access the Component protected with the permission even if the provider-
side application is installed later.
As a way to prevent the loss of inter-application communication due to the order of installation, you
may think of defining the permission in every application in the communication. By this way, even if a
user-side application has been installed prior to the provider-side application, all user-side applications
will be able to access the provider-side application. However, it will create a situation that the permission
is undefined when the user-side application installed first is uninstalled. As a result, even if there are
other user-side applications, they will not be able to gain access to the provider-side application.
As stated above, there is a concern of damaging the availability of an application, thus your own normal
permission should not be used.
5.2.2.6 The String for Your Own Permission Name Should Be of an Extent of the Package Name
of Application (Recommended)
When multiple applications define permissions under the same name, the Protection Level that has
been defined by an application installed first will be applied. Protection by signature permission will
not be available in the case that the application installed first defines a normal permission and the
application installed later defines a signature permission under the same name. Even in the absence of
malicious intent, a conflict of permission names among multiple applications could cause behavior s of
any applications as an unintended Protection Level. To prevent such accidents, it is recommended that
a permission name extends (starts with) the package name of the application defining the permission as
below.
For example, the following name would be preferred when defining a permission of READ access for the
package of org.jssec.android.sample.
org.jssec.android.sample.permission.READ
5.2.3.1 Characteristics of Android OS that Avoids Self-defined Signature Permission and Its Counter-
measures
353
Secure Coding Guide Documentation Release 2019-12-01
First, we will describe the basic usage of self-defined signature permission that is explained in the Devel-
oper Guide (https://developer.android.com/guide/topics/security/security.html) of Android. However,
as it will be explained later, there are problems with regard to the avoidance of permission. Consequently,
counter-measures that are described in this Guidebook are necessary.
The followings are the basic usage of self-defined Signature Permission.
1. Define a self-defined signature permission in the AndroidManifest.xml of the provider-side appli-
cation. (definition of permission) Example: <permission android:name=”xxx” android:protection-
Level=”signature” />
2. Enforce the permission with the permission attribute of the Component to be protected in the
AndroidManifest.xml of the provider-side application. (enforcement of permission) Example: <ac-
tivity android:permission=”xxx” ... >...</activity>
3. Declare the self-defined signature permission with the uses-permission tag in the AndroidMani-
fest.xml of every user-side application to access the Component to be protected. (declaration of
using permission) Example: <uses-permission android:name=”xxx” />
4. Sign APKs of all inter-communicating applications with the same developer key.
Actually, if the following conditions are fulfilled, this approach will create a loophole to avoid signature
permission from being performed.
For the sake of explanation, we call an application that is protected by self-defined signature permission
as ProtectedApp, and AttackerApp for an application that has been signed by a different developer key
from the ProtectedApp. What a loophole to avoid signature permission from being performed means is,
despite the mismatch of the signature for AttackerApp, it is possible to gain access to the Component
of ProtectedApp.
1. An AttackerApp also defines a normal permission (strictly speaking, signature permission is also
acceptable) under the same name as the signature permission which has been defined by the
ProtectedApp. Example: <permission android:name=” xxx” android:protectionLevel=”normal”
/>
2. The AttackerApp declares the self-defined normal permission with uses-permission. Example:
<uses-permission android:name=”xxx” />
3. The AttackerApp has installed on the device prior to the ProtectedApp.
354
Secure Coding Guide Documentation Release 2019-12-01
The permission name that is necessary to meet Condition 1 and Condition 2 can easily be known by an
attacker taking AndroidManifest.xml out from an APK file. The attacker also could satisfy Condition 3
with a certain amount of effort (e.g. deceiving a user).
There is a risk of self-defined signature permission to evade protection if only the basic usage is adopted,
and a counter-measure to prevent such loopholes is needed. Specifically, you could find how to solve
the above-mentioned issues by using the method described in “5.2.2.4. Verify If the In-house-defined
Signature Permission Is Defined by an In-house Application (Required)”.
We have already touched on the case that a Protection Level of self-defined permission could be changed
as not intended. To prevent malfunctioning due to such cases, it has been needed to implement some
sort of counter-measures on the source-code side of Java. From the viewpoint of AndroidManifest.xml
falsification, we will talk about the counter-measures to be taken on the source-code side. We will
demonstrate a simple case of installation that can detect falsifications. However, please note that these
counter-measures are little effective against professional hackers who falsify with criminal intent.
This section is about the falsification of an application and users with malicious intent. Although this is
originally outside of the scope of a Guidebook, from the fact that this is related to Permission and the
tools for such falsification are provided in public as Android applications, we decided to mention it as
“Simple counter-measures against amateur hackers”.
It must be remembered that applications that can be installed from market are applications that can
be falsified without root privilege. The reason is that applications that can rebuild and sign APK files
with altered AndroidManifest.xml are distributed. By using these applications, anyone can delete any
permission from applications they have installed.
As an example, there seems to be cases of rebuilding APKs with different signatures altering AndroidMan-
ifest.xml with INTERNET permission removed to render advertising modules attached in applications as
useless. There are some users who praise these types of tools due to the fact that no personal information
355
Secure Coding Guide Documentation Release 2019-12-01
is leaked anywhere. As these ads which are attached in applications stop functioning, such actions cause
monetary damage for developers who are counting on ad revenue. And it is believed that most of the
users don’t have any compunction.
In the following code, we show an instance of implementation that an application that has declared
INTERNET permission with uses-permission verifies if INTERNET permission is described in the An-
droidManifest.xml of itself at run time.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Detect falsification
if( checkPermissions(list) ){
// OK
Log.d("dbg", "OK.");
}else{
Log.d("dbg", "manifest file is stale.");
finish();
}
}
/**
* Acquire Permission through list that was defined in AndroidManifest.xml
* @return
*/
private List<String> getDefinedPermissionList(){
List<String> list = new ArrayList<String>();
list.add("android.permission.INTERNET");
return list;
}
/**
* Verify that Permission has not been changed Permission
* @param permissionList
* @return
*/
private boolean checkPermissions(List<String> permissionList){
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(
getPackageName(), PackageManager.GET_PERMISSIONS);
String[] permissionArray = packageInfo.requestedPermissions;
if (permissionArray != null) {
for (String permission : permissionArray) {
if(! permissionList.remove(permission)){
// Unintended Permission has been added
return false;
}
}
}
if(permissionList.size() == 0){
// OK
return true;
}
356
Secure Coding Guide Documentation Release 2019-12-01
return false;
}
}
We explained about detecting the falsification of permissions by a user in “5.2.3.2. Falsification of An-
droidManifest.xml by a User”. However, the falsification of applications is not limited to permission only,
and there are many other cases where applications are appropriated without any changes in the source
code. For example, it is a case where they distribute other developers’ applications (falsified) in the
market as if they were their own applications just by replacing resources to their own. Here, we will
show a more generic method to detect the falsification of an APK file.
In order to falsify an APK, it is needed to decode the APK file into folders and files, modify their
contents, and then rebuild them into a new APK file. Since the falsifier does not have the key of the
original developer, he would have to sign the new APK file with his own key. As the falsification of an
APK inevitably brings with a change in signature (certificate), it is possible to detect whether an APK
has been falsified at run time by comparing the certificate in the APK and the developer’s certificate
embedded in the source code as below.
The following is a sample code. Also, a professional hacker will be able to easily circumvent the detection
of falsification if this implementation example is used as it is. Please apply this sample code to your
application by being aware that this is a simple implementation example.
Points:
1. Verify that an application’s certificate belongs to the developer before major processing is started.
SignatureCheckActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.permission.signcheckactivity;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
357
Secure Coding Guide Documentation Release 2019-12-01
} else {
// Certificate hash value of "my company key" of keystore
sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2␣
˓→42E142CA";
}
}
return sMyCertHash;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.shared;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
358
Secure Coding Guide Documentation Release 2019-12-01
An application must declare to use permission when accessing contacts or GPS with its information and
features that are protected by Android OS. When the permission required is granted, the permission is
delegated to the application and the application would be able to access the information and features
protected with the permission.
Depending on how the program is designed, the application to which has been delegated (granted) the
permission is able to acquire data that is protected with the permission. Furthermore, the application
can offer another application the protected data without enforcing the same permission. This is nothing
less than permission-less application to access data that is protected by permission. This is virtually the
same thing as re-delegating the permission, and this is referred to the Permission Re-delegation Problem.
Accordingly, the specification of the permission mechanism of Android only is able to manage permission
of direct access from an application to protected data.
A specific example is shown in Fig. 5.2.9. The application in the center shows that an application which
has declared android.permission.READ_CONTACTS to use it reads contacts and then stores them into
359
Secure Coding Guide Documentation Release 2019-12-01
its own database. The Permission Re-delegation Problem occurs when information that has been stored
is offered to another application without any restriction via Content Provider.
360
Secure Coding Guide Documentation Release 2019-12-01
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".TransferPermissionActivity"
android:label="@string/title_activity_transfer_permission" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- *** Point1 *** Enforce the same permission that the rovider does. -->
<provider
android:name=".TransferPermissionContentProvider"
android:authorities="org.jssec.android.permission.transferpermission"
android:enabled="true"
android:exported="true"
android:readPermission="android.permission.READ_CONTACTS" >
</provider>
</application>
</manifest>
When an application enforces multiple permissions, the above method will not solve it. By using
Context#checkCallingPermission() or PackageManager#checkPermission() from the source code, ver-
ify whether the invoker application has declared all permissions with uses-permission in the Manifest.
In the case of an Activity
public void onCreate(Bundle savedInstanceState) {
[...]
if (checkCallingPermission("android.permission.READ_CONTACTS") ==
PackageManager.PERMISSION_GRANTED
&& checkCallingPermission("android.permission.WRITE_CONTACTS") ==
PackageManager.PERMISSION_GRANTED) {
// Processing during the time when an invoker is correctly
// declaring to use
return;
}
finish();
}
5.2.3.5 Signature check mechanism for custom permissions (Android 5.0 and later)
In versions of Android 5.0 (API Level 21) and later, the application which defines its own custom
permissions cannot be installed if the following conditions are met.
1. Another application which defines its own permission with the same name has already installed on
the device.
2. The applications are signed with different keys
361
Secure Coding Guide Documentation Release 2019-12-01
When both an application with the protected function (Component) and an application using the function
define their own permission with the same name and are signed with the same key, the above mechanism
will protect against installation of other company’s applications which define their own custom permission
with the same name. However, as mentioned in “5.2.2.3. Your Own Signature Permission Must Only Be
Defined on the Provider-side Application (Required)”, that mechanism won’t work well for checking if a
custom permission is defined by your own company because the permission could be undefined without
your intent by uninstalling applications when plural applications define the same permission.
To sum it up, also in versions of Android 5.0 (API Level 21) and later, you are required to comply with
the two rules, “5.2.2.3. Your Own Signature Permission Must Only Be Defined on the Provider-side
Application (Required)” and “5.2.2.4. Verify If the In-house-defined Signature Permission Is Defined by
an In-house Application (Required)” when your application defines your own Signature Permission.
5.2.3.6 Modifications to the Permission model specifications in Android versions 6.0 and later
Android 6.0 (API Level 23) introduces modified specifications for the Permission model that affect both
the design and specifications of apps. In this section we offer an overview of the Permission model in
Android 6.0 and later. We also describe modifications made in Android 8.0 and later.
In cases where an app declares use of permissions requiring user confirmation (Dangerous Permissions)
[see Section “5.2.2.1. System Dangerous Permissions of Android OS Must Only Be Used for Protecting
User Assets (Required)”], the specifications for Android 5.1 (API level 22) and earlier versions called for a
list of such permissions to be displayed when the app is installed, and the user must grant all permissions
for the installation to proceed. At this point, all permissions declared by the app (including permissions
other than Dangerous Permissions) were granted to the app; once these permissions were granted to the
app, they remained in effect until the app was uninstalled from the terminal.
However, in the specifications for Android 6.0 and later versions, the granting of permissions takes place
when an app is executed. The granting of permissions, and user confirmation of permissions, does
not take place when the app is installed. When an app executes a procedure that requires Dangerous
Permissions, it is necessary to check whether or not those permissions have been granted to the app in
advance; if not, a confirmation window must be displayed in Android OS to request permission from the
user5 . If the user grants permission from the confirmation window, the permissions are granted to the
app. However, permissions granted to an app by a user (Dangerous Permissions) may be revoked at any
time via the Settings menu (Fig. 5.2.10). For this reason, appropriate procedures must be implemented
to ensure that apps cause no irregular behavior even in situations in which they cannot access needed
information or functionality because permission has not been granted.
5 Because Normal Permissions and Signature Permissions are automatically granted by Android OS, there is no need to
362
Secure Coding Guide Documentation Release 2019-12-01
Multiple Permissions may be grouped together into what is known as a Permission Group based on
their functions and type of information relevant to them. For example, the Permission android.per-
mission.READ_CALENDAR, which is required to read calendar information, and the Permission an-
droid.permission.WRITE_CALENDAR, which is required to write calendar information, are both affil-
iated with the Permission Group named android.permission-group.CALENDAR.
In the Permission model for Android 6.0 (API Level 23) and later, privileges are granted or denied at
the block-unit level of the Permission Group, as shown here. However, developers must be careful to
note that the block unit may vary depending on the combination of OS and SDK (see below).
• For terminals running Android 6.0 (API Level 23) or later and app targetSdkVersion: 23~25
If android.permission.READ_CALENDAR and android.permission.WRITE_CALENDAR are listed
in the Manifest, then when the app is launched a request for android.permission.READ_CALEN-
DAR is issued; if the user grants this permission, Android OS determines that both android.permis-
sion.READ_CALENDAR and android.permission.WRITE_CALENDAR are permitted for use and thus
grants the permission.
• For terminals running Android 8.0 (API Level 26 or later and app targetSdkVersion 26 and above:
363
Secure Coding Guide Documentation Release 2019-12-01
Cases in which apps require Permission requests at runtime are restricted to situations in which the
terminal is running Android 6.0 or later and the app’s targetSDKVersion is 23 or higher. If the terminal
is running Android 5.1 or earlier, or if the app’s targetSDKVersion was 23 or lower, permissions are
requested and granted altogether at the time of installation, as was traditionally the case. However,
if the terminal is running Android 6.0 or later, then—even if the app’s targetSDKVersion is below
23—permissions that were granted by the user at installation may be revoked by the user at any time.
This creates the possibility of unintended irregular app termination. Developers must either comply
immediately with the modified specifications or set the maxSDKVersion of their app to 22 or earlier to
ensure that the app cannot be installed on terminals running Android 6.0 (API Level 23) or later.
Furthermore, in devices running Android 10, when an app targeting devices running Android 5.1 (API
level 22) or lower is executed for the first time, a warning is displayed indicating that it may not run
properly. Also, for apps that request granting of storage access and other permissions by the user, a
permission (Allow/Deny) selection screen appears before this warning7 .
However, it should be noted that the effect of maxSdkVersion is limited. When the value of maxSd-
kVersion is set 22 or earlier, Android 6.0 (API Level 23) and later of the devices are no longer listed as
an installable device of the target application in Google Play. On the other hand, because the value of
maxSdkVersion is not checked in the marketplace other than Google Play, it may be possible to install
the target application in the Android 6.0 (API Level 23) or later.
Because the effect of maxSdkVersion is limited, and further Google does not recommend the use of
maxSdkVersion, it is recommended that developers comply immediately with the modified specifications.
6 In this case as well, the app must declare usage of both android.permission.READ_CALENDAR and android.permis-
sion.WRITE_CALENDAR.
7 https://developer.android.com/preview/behavior-changes-all#low-target-sdk-warnings
364
Secure Coding Guide Documentation Release 2019-12-01
In Android 6.0 and later versions, permissions for the following network communications have their
Protection Level changed from Dangerous to Normal. Thus, even if apps declare the use of these
Permissions, there is no need to acquire explicit permission from the user, and hence the modified
specification has no impact in this case.
• android.permission.BLUETOOTH
• android.permission.BLUETOOTH_ADMIN
• android.permission.CHANGE_WIFI_MULTICAST_STATE
• android.permission.CHANGE_WIFI_STATE
• android.permission.CHANGE_WIMAX_STATE
• android.permission.DISABLE_KEYGUARD
• android.permission.INTERNET
• android.permission.NFC
Account Manager is the Android OS’s system which centrally manages account information (account
name, password) which is necessary for applications to access to online service and authentication token8 .
A user needs to register the account information to Account Manager in advance, and when an application
tries to access to online service, Account Manager will automatically provide application authentication
token after getting user’s permission. The advantage of Account Manager is that an application doesn’t
need to handle the extremely sensitive information, password.
The structure of account management function which uses Account Manager is as per below Fig. 5.3.1.
“Requesting application” is the application which accesses the online service, by getting authentication
token, and this is above mentioned application. On the other hand, “Authenticator application” is func-
tion extension of Account Manager, and by providing Account Manager of an object called Authenticator,
as a result Account Manager can manage centrally the account information and authentication token of
the online service. Requesting application and Authenticator application don’t need to be the separate
ones, so these can be implemented as a single application.
Fig. 5.3.1: Configuration of account management function which uses Account Manager
Originally, the developer’s signature key of user application (requesting application) and Authenticator
application can be the different ones. However, only in Android 4.0.x devices, there’s an Android
Framework bug, and when the signature key of user application and Authenticator application are
different, exception occurs in user application, and in-house account cannot be used. The following
sample code does not implement any workarounds against this defect. Please refer to “5.3.3.2. Exception
8 Account Manager provides mechanism of synchronizing with online services, however, this section doesn’t deal with
it.
365
Secure Coding Guide Documentation Release 2019-12-01
Occurs When Signature Keys of User Application and Authenticator Application Are Different, in Android
4.0.x” for details.
“5.3.1.1. Creating In-house accounts” is prepared as a sample of Authenticator application, and “5.3.1.2.
Using In-house Accounts” is prepared as a sample of requesting application. In sample code set which is
distributed in JSSEC’s Web site, each of them is corresponded to AccountManager Authenticator and
AccountManager User.
Here is the sample code of Authenticator application which enables Account Manager to use the in-
house account. There is no Activity which can be launched from home screen in this application. Please
pay attention that it’s called indirectly via Account Manager from another sample code “5.3.1.2. Using
In-house Accounts”
Points:
1. The service that provides an authenticator must be private.
2. The login screen activity must be implemented in an authenticator application.
3. The login screen activity must be made as a public activity.
4. The explicit intent which the class name of the login screen activity is specified must be set to
KEY_INTENT.
5. Sensitive information (like account information or authentication token) must not be output to the
log.
6. Password should not be saved in Account Manager.
7. HTTPS should be used for communication between an authenticator and the online services.
Service which gives Account Manager IBinder of Authenticator is defined in AndroidManifest.xml. Spec-
ify resource XML file which Authenticator is written, by meta-data.
AccountManager Authenticator/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.accountmanager.authenticator"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
366
Secure Coding Guide Documentation Release 2019-12-01
<!-- Activity for for login screen which is displayed when adding an account -->
<!-- *** POINT 2 *** The login screen activity must be implemented in an authenticator␣
˓→application. -->
<!-- *** POINT 3 *** The login screen activity must be made as a public activity. -->
<activity
android:name=".LoginActivity"
android:exported="true"
android:label="@string/login_activity_title"
android:theme="@android:style/Theme.Dialog"
tools:ignore="ExportedActivity" />
</application>
</manifest>
Define Authenticator by XML file. Specify account type etc. of in-house account.
res/xml/authenticator.xml
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="org.jssec.android.accountmanager"
android:icon="@drawable/ic_launcher"
android:label="@string/label"
android:smallIcon="@drawable/ic_launcher"
android:customTokens="true" />
Service which gives Authenticator’s Instance to AccountManager. Easy implementation which returns
Instance of JssecAuthenticator class that is Authenticator implemented in this sample by onBind(), is
enough.
AuthenticationService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.accountmanager.authenticator;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
@Override
public void onCreate() {
(continues on next page)
367
Secure Coding Guide Documentation Release 2019-12-01
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
package org.jssec.android.accountmanager.authenticator;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
(continues on next page)
368
Secure Coding Guide Documentation Release 2019-12-01
AccountManager am = AccountManager.get(mContext);
Account[] accounts = am.getAccountsByType(JSSEC_ACCOUNT_TYPE);
Bundle bundle = new Bundle();
if (accounts.length > 0) {
// In this sample code, when an account already exists, consider it
// as an error.
bundle.putString(AccountManager.KEY_ERROR_CODE, String.valueOf(-1));
bundle.putString(AccountManager.KEY_ERROR_MESSAGE,
mContext.getString(R.string.error_account_exists));
} else {
// *** POINT 2 *** The login screen activity must be implemented in an
// authenticator application.
// *** POINT 4 *** The explicit intent which the class name of the
// login screen activity is specified must be set to KEY_INTENT.
Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
}
return bundle;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response,
Account account, String authTokenType,
Bundle options)
throws NetworkErrorException {
@Override
public String getAuthTokenLabel(String authTokenType) {
return JSSEC_AUTHTOKEN_LABEL;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response,
Account account, Bundle options)
throws NetworkErrorException {
return null;
}
@Override
(continues on next page)
369
Secure Coding Guide Documentation Release 2019-12-01
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response,
Account account,
String authTokenType, Bundle options)
throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response,
Account account, String[] features)
throws NetworkErrorException {
Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return result;
}
This is Login activity which sends an account name and password to online service, and perform login
authentication, and as a result, get an authentication token. It’s displayed when adding a new account
or when getting authentication token again. It’s supposed that the actual access to online service is
implemented in WebService class.
LoginActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.accountmanager.authenticator;
import org.jssec.android.accountmanager.webservice.WebService;
import android.accounts.Account;
(continues on next page)
370
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// *** POINT 3 *** The login screen activity must be made as a public
// activity, and suppose the attack access from other application.
// Regarding external input, only RE_AUTH_NAME which is String type of
// Intent#extras, are handled.
// This external input String is passed toextEdit#setText(),
// WebService#login(),new Account(), as a parameter,it's verified that
// there's no problem if any character string is passed.
mReAuthName = getIntent().getStringExtra(JssecAuthenticator.RE_AUTH_NAME);
if (mReAuthName != null) {
// Since LoginActivity is called with the specified user name,
// user name should not be editable.
mNameEdit.setText(mReAuthName);
mNameEdit.setInputType(InputType.TYPE_NULL);
mNameEdit.setFocusable(false);
mNameEdit.setEnabled(false);
}
}
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pass)) {
// Process when the inputed value is incorrect
setResult(RESULT_CANCELED);
finish();
}
371
Secure Coding Guide Documentation Release 2019-12-01
if (mReAuthName == null) {
// Register accounts which logged in successfully, to aAccountManager
// *** POINT 6 *** Password should not be saved in Account Manager.
AccountManager am = AccountManager.get(this);
Account account =
new Account(name, JssecAuthenticator.JSSEC_ACCOUNT_TYPE);
am.addAccountExplicitly(account, null, null);
am.setAuthToken(account, JssecAuthenticator.JSSEC_AUTHTOKEN_TYPE,
authToken);
Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, name);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
JssecAuthenticator.JSSEC_ACCOUNT_TYPE);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
} else {
// Return authentication token
Bundle bundle = new Bundle();
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, name);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE,
JssecAuthenticator.JSSEC_ACCOUNT_TYPE);
bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken);
setAccountAuthenticatorResult(bundle);
setResult(RESULT_OK);
}
finish();
}
}
Actually, WebService class is dummy implementation here, and this is the sample implementation which
supposes authentication is always successful, and fixed character string is returned as an authentication
token.
WebService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
(continues on next page)
372
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.accountmanager.webservice;
/**
* Suppose to access to account managemnet function of online service.
*
* @param username Account name character string
* @param password password character string
* @return Return authentication token
*/
public String login(String username, String password) {
// *** POINT 7 *** HTTPS should be used for communication between an
// authenticator and the online services.
// Actually, communication process with servers is implemented here,
// but Omit here, since this is a sample.
return getAuthToken(username, password);
}
Here is the sample code of an application which adds an in-house account and gets an authentication
token. When another sample application “5.3.1.1. Creating In-house accounts” is installed in a device,
in-house account can be added or authentication token can be got. “Access request” screen is displayed
only when the signature keys of both applications are different.
Point:
1. Execute the account process after verifying if the authenticator is regular one.
373
Secure Coding Guide Documentation Release 2019-12-01
AccountManager User/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.accountmanager.user" >
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".UserActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Activity of user application. When tapping the button on the screen, either addAccount() or getAuth-
Token() is to be executed. Authenticator which corresponds to the specific account type may be fake in
some cases, so pay attention that the account process is started after verifying that the Authenticator is
regular one.
UserActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.accountmanager.user;
import java.io.IOException;
import org.jssec.android.shared.PkgCert;
import org.jssec.android.shared.Utils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorDescription;
(continues on next page)
374
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity);
mLogView = (TextView)findViewById(R.id.logview);
}
// *** POINT 1 *** Execute the account process after verifying if the
// authenticator is regular one.
if (!checkAuthenticator()) return;
AccountManager am = AccountManager.get(this);
am.addAccount(JSSEC_ACCOUNT_TYPE, JSSEC_TOKEN_TYPE, null, null, this,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle result = future.getResult();
String type =
result.getString(AccountManager.KEY_ACCOUNT_TYPE);
String name =
result.getString(AccountManager.KEY_ACCOUNT_NAME);
if (type != null && name != null) {
logLine("Add the following accounts:");
logLine(" Account type: %s", type);
logLine(" Account name: %s", name);
} else {
String code =
result.getString(AccountManager.KEY_ERROR_CODE);
String msg =
result.getString(AccountManager.KEY_ERROR_MESSAGE);
logLine("The account cannot be added");
logLine(" Error code %s: %s", code, msg);
}
} catch (OperationCanceledException e) {
} catch (AuthenticatorException e) {
} catch (IOException e) {
}
}
},
(continues on next page)
375
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 1 *** After checking that the Authenticator is the regular
// one, execute account process.
if (!checkAuthenticator()) return;
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType(JSSEC_ACCOUNT_TYPE);
if (accounts.length > 0) {
Account account = accounts[0];
am.getAuthToken(account, JSSEC_TOKEN_TYPE, null, this,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle result = future.getResult();
String name =
result.getString(AccountManager.KEY_ACCOUNT_NAME);
String authtoken =
result.getString(AccountManager.KEY_AUTHTOKEN);
logLine("%s-san's token:", name);
if (authtoken != null) {
logLine(" %s", authtoken);
} else {
logLine(" Couldn't get");
}
} catch (OperationCanceledException e) {
logLine(" Exception: %s",e.getClass().getName());
} catch (AuthenticatorException e) {
logLine(" Exception: %s",e.getClass().getName());
} catch (IOException e) {
logLine(" Exception: %s",e.getClass().getName());
}
}
},
null);
} else {
logLine("Account is not registered.");
}
}
if (pkgname == null) {
logLine("Authenticator cannot be found.");
return false;
}
(continues on next page)
376
Secure Coding Guide Documentation Release 2019-12-01
PkgCert.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
377
Secure Coding Guide Documentation Release 2019-12-01
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
378
Secure Coding Guide Documentation Release 2019-12-01
3. The Login Screen Activity Must Be Made as a Public Activity and Suppose Attack Accesses by
Other Applications (Required)
4. Provide KEY_INTENT with Explicit Intent with the Specified Class Name of Login Screen Activity
(Required)
5. Sensitive Information (like Account Information and Authentication Token) Must Not Be Output
to the Log (Required)
6. Password Should Not Be Saved in Account Manager (Recommended)
7. HTTPS Should Be Used for Communication Between an Authenticator and the Online Service
(Required)
Follow the rules below when implementing user application.
8. Account Process Should Be Executed after verifying if the Authenticator is the regular one (Required)
It’s presupposed that the Service which provides with Authenticator is used by Account Manager, and
it should not be accessed by other applications. So, by making it Private Service, it can exclude accesses
by other applications. In addition, Account Manager runs with system privilege, so Account Manager
can access even if it’s private Service.
Login screen for adding a new account and getting the authentication token should be implemented
by Authenticator application. Own Login screen should not be prepared in user application side. As
mentioned at the beginning of this article, “The advantage of AccountManager is that the extremely
sensitive information/password is not necessarily to be handled by application.”, If login screen is prepared
in user application side, password is handled by user application, and its design becomes what is beyond
the policy of Account Manager.
By preparing login screen by Authenticator application, who can operate login screen is limited only the
device’s user. It means that there’s no way to attack the account for malicious applications by attempting
to login directly, or by creating an account.
5.3.2.3 The Login Screen Activity Must Be Made as a Public Activity and Suppose Attack Accesses
by Other Applications (Required)
Login screen Activity is the system launched by the user application’s p. In order that the login screen
Activity is displayed even when the signature keys of user application and Authenticator application are
different, login screen Activity should be implemented as Public Activity.
What login screen Activity is public Activity means, that there’s a chance that it may be launched by
malicious applications. Never trust on any input data. Hence, it’s necessary to take the counter-measures
mentioned in “3.2. Handling Input Data Carefully and Securely”
5.3.2.4 Provide KEY_INTENT with Explicit Intent with the Specified Class Name of Login Screen
Activity (Required)
When Authenticator needs to open login screen Activity, Intent which launches login screen Activity is
to be given in the Bundle that is returned to Account Manager, by KEY_INTENT. The Intent to be
given, should be the explicit Intent which specifies class name of login screen Activity. If an implicit
Intent is given, the framework may attempt to launch an Activity other than the Activity prepared by
the Authenticator app for the login window. On Android 4.4 (API Level 19) and later versions, this may
cause the app to crash; on earlier versions it may cause unintended Activities prepared by other apps to
be launched.
379
Secure Coding Guide Documentation Release 2019-12-01
On Android 4.4(API Level 19) and later versions, if the signature of an app launched by an intent
given by the framework via KEY_INTENT does not match the signature of the Authenticator app, a
SecurityException is generated; in this case, there is no risk that a false login screen will be launched;
however, there is a possibility that the ordinary screen will be able to launch and the user’s normal use
of the app will be obstructed. On versions prior to Android 4.4(API Level 19), there is a risk that a false
login screen prepared by a malicious app will be launched, and thus that the user may input passwords
and other authentication information to the malicious app.
5.3.2.5 Sensitive Information (like Account Information and Authentication Token) Must Not Be
Output to the Log (Required)
Applications which access to online service sometimes face a trouble like it cannot access to online service
successfully. The causes of unsuccessful access are various, like lack in network environment arrangement,
mistakes in implementing communication protocol, lack of Permission, authentication error, etc. A
common implementation is that a program outputs the detailed information to log, so that developer
can analyze the cause of a problem later.
Sensitive information like password or authentication token should not be output to log. Log information
can be read from other applications, so it may become the cause of information leakage. Also, account
names should not be output to log, if it could be lead the damage of leakage.
Two of authentication information, password and authentication token, can be saved in an account to
be register to AccountManager. This information is to be stored in accounts.db under the following
directories, in a plain text (i.e. without encryption).
• Android 4.1 or earlier /data/system/accounts.db
• Android 4.2 to Android 6.0 /data/system/0/accounts.db or /data/system/<UserId>/accounts.db
• Android 7.0 or later /data/system_ce/0/accounts_ce.db
Note: Because multiuser functionality is supported on Android 4.2 and later versions, this has been
changed to save the content to a user-specific directory. Also, because Android 7.0 and later versions
support Direct Boot, the database file is divided into two parts: one file that handles data while locked
(/data/system_de/0/accounts_de_db) and a separate file that handles data while unlocked (/data/sys-
tem_ce/0/accounts_ce.db) Under ordinary circumstances, authentication information is stored in the
latter database file.
Root privileges or system privileges are required to read the content of these database files, so they
cannot be read on commercial Android terminals. If Android OS contains any vulnerabilities that allow
attackers to acquire root privileges or system privileges, this would leave the authentication information
stored in accounts.db exposed to risk.
To read in the contents of accounts.db, either root privilege or system privilege is required, and it cannot
be read from the marketed Android devices. In the case there is any vulnerability in Android OS, which
root privilege or system privilege may be taken over by attackers, authentication information which is
saved in accounts.db will be on the edge of the risk.
The Authentication application, which is introduced in this article, is designed to save authentication
token in AccountManager without saving user password. When accessing to online service continuously
in a certain period, generally the expiration period of authentication token is extended, so the design
that password is not saved is enough in most cases.
In general, valid date of authentication token is shorter than password, and it’s characteristic that it can
be disabled anytime. In case, authentication token is leaked, it can be disabled, so authentication token
is comparatively safer, compared with password. In the case authentication token is disabled, user can
input the password again to get a new authentication token.
If disabling password when it’s leaked, user cannot use online service any more. In this case, it requires
call center support etc., and it will take huge cost. Hence, it’s better to avoid from the design to save
380
Secure Coding Guide Documentation Release 2019-12-01
password in AccountManager. In case, the design to save password cannot be avoided, high level of reverse
engineering counter-measures like encrypting password and obfuscating the key of that encryption, should
be taken.
5.3.2.7 HTTPS Should Be Used for Communication Between an Authenticator and the Online
Service (Required)
Password or authentication token is so called authentication information, and if it’s taken over by the
third party, the third party can masquerade as the valid user. Since Authenticator sends/receives these
types of authentication information with online service, reliable encrypted communication method like
an HTTPS should be used.
5.3.2.8 Account Process Should Be Executed after verifying if the Authenticator is the regular one
(Required)
In the case there are several Authenticators which the same account type is defined in a device, Authen-
ticator which was installed earlier becomes valid. So, when the own Authenticator was installed later,
it’s not to be used.
If the Authenticator which was installed earlier, is the malware’s masquerade, account information in-
putted by user may be taken over by malware. User application should verify the account type which
performs account operation, whether the regular Authenticator is allocated to it or not, before executing
account operation.
Whether the Authenticator which is allocated to one account type is regular one or not, can be verified by
checking whether the certificate hash value of the package of Authenticator matches with pre-confirmed
valid certificate hash value. If the certificate hash values are found to be not matched, a measure to
prompt user to uninstall the package which includes the unexpected Authenticator allocated to that
account type, is preferable.
To use each method of AccountManager class, it’s necessary to declare to use the appropriate Permission
respectively, in application’s AndroidManifest.xml. In Android 5.1 (API Level 22) and earlier versions,
privileges such as AUTHENTICATE_ACCOUNTS, GET_ACCOUNTS, or MANAGE_ACCOUNTS
are required; the privileges corresponding to various methods are shown in Table 5.3.1.
381
Secure Coding Guide Documentation Release 2019-12-01
382
Secure Coding Guide Documentation Release 2019-12-01
permissions and no signature. It’s necessary for especially developers who use the different signature
keys per applications, to be very careful when selecting which key to use for applications, considering
this restriction. In addition, since the data which is obtained by AccountManager includes the sensitive
information, so need to handle with care in order to decrease the risk like leakage or unauthorized use.
5.3.3.2 Exception Occurs When Signature Keys of User Application and Authenticator Application
Are Different, in Android 4.0.x
When authentication token acquisition function, is required by the user application which is signed
by the developer key which is different from the signature key of Authenticator application that in-
cludes Authenticator, AccountManager verifies users whether to grant the usage of authentication
token or not, by displaying the authentication token license screen (GrantCredentialsPermissionAc-
tivity.) However, there’s a bug in Android Framework of Android 4.0.x, as soon as this screen in
opened by AccountManager, exception occurs, and application is force closed. (Fig. 5.3.3). See
https://code.google.com/p/android/issues/detail?id=23421 for the details of the bug. This bug can-
not be found in Android 4.1.x. and later.
Fig. 5.3.3: When displaying Android standard authentication token license screen
5.3.3.3 Cases in which Authenticator accounts with non-matching signatures may be read in Android
8.0 (API Level 26) or later
In Android 8.0 (API Level 26) and later versions, account-information-fetching methods that required
GET_ACCOUNTS Permission in Android 7.1 (API Level 25) and earlier versions may now be called
without that permission. Instead, account information may now be obtained only in cases where the
signature matches or in which the setAccountVisibility method has been used on the Authenticator app
side to specify an app to which account information may be provided However, note carefully that there
are a number of exceptions to this rule, implemented by the framework. In what follows we discuss these
exceptions.
First, when the targetSdkVersion of the app using the account information is 25 (Android 7.1) or below,
the above rule does not apply; in this case apps with the GET_ACCOUNTS permission may obtain
account information within the terminal regardless of its signature. However, below we discuss how this
behavior may be changed depending on the Authenticator-side implementation.
383
Secure Coding Guide Documentation Release 2019-12-01
Next, account information for Authenticators that declare the use of WRITE_CONTACTS Permission
may be read by other apps with READ_CONTACTS Permission, regardless of signature. This is not
a bug, but is rather the way the framework is designed9 . Note again that this behavior may differ
depending on the Authenticator-side implementation.
Thus we see that there are some exceptional cases in which account information may be read even for
apps with non-matching signatures and for which the setAccountVisibility method has not been called
to specify a destination to which account information is to be provided. However, these behaviors may
be modified by calling the setAccountVisibility method on the Authenticator side, as in the following
snippet.
Do not provide account information to third-party apps
By proceeding this way, we can avoid the framework’s default behavior regarding account information
for Authenticators that have called the setAccountVisibility method; the above modification ensures that
account information is not provided even in cases where targetSdkVersion <= 25 or READ_CONTACTS
permission is present.
Most of smartphone applications communicate with Web servers on the Internet. As methods of com-
munications, here we focus on the 2 methods of HTTP and HTTPS. From the security point of view,
HTTPS communication is preferable. Lately, major Web services like Google or Facebook have been
coming to use HTTPS as default. However, among HTTPS connection methods, those that use SSL3.0 /
early Transport Layer Security (TLS) protocols are known to be susceptible to a vulnerability (commonly
known as POODLE and BEAST), and we strongly recommend against the use of such methods, please
refer to “5.4.3.8. (Column): Transitioning to TLS1.2/TLS1.3 for secure connections”.
Since 2012, many defects in implementation of HTTPS communication have been pointed out in Android
applications. These defects might have been implemented for accessing testing Web servers operated by
server certificates that are not issued by trusted third party certificate authorities, but issued privately
(hereinafter, called private certificates).
In this section, communication methods of HTTP and HTTPS are explained and the method to access
safely with HTTPS to a Web server operated by a private certificate is also described.
You can find out which type of HTTP/HTTPS communication you are supposed to implement through
the following chart (Fig. 5.4.1) shown below.
9 It is assumed that Authenticators that declare the use of WRITE_CONTACTS Permission will write account infor-
mation to ContactsProvider, and that apps with READ_CONTACTS Permission will be granted permission to obtain
account information.
384
Secure Coding Guide Documentation Release 2019-12-01
When sensitive information is sent or received, HTTPS communication is to be used because its com-
munication channel is encrypted with SSL/TLS. HTTPS communication is required for the following
sensitive information.
• Login ID/Password for Web services.
• Information for keeping authentication state (session ID, token, Cookie etc.)
• Important/confidential information depending on Web services (personal information, credit card
information etc.)
A smartphone application with network communication is a part of “system” as well as a Web server.
And you have to select HTTP or HTTPS for each communication based on secure design and coding
considering the whole “system”. Table 5.4.1 is for a comparison between HTTP and HTTPS. And Table
5.4.2 is for the differences in sample codes.
385
Secure Coding Guide Documentation Release 2019-12-01
It is based on two premises that all contents sent/received through HTTP communications may be sniffed
and tampered by attackers and your destination server may be replaced with fake servers prepared by
attackers. HTTP communication can be used only if no damage is caused or the damage is within the
permissible extent even under the premises. If an application cannot accept the premises, please refer to
“5.4.1.2. Communicating via HTTPS” and “5.4.1.3. Communicating via HTTPS with private certificate”.
The following sample code shows an application which gets the specific image on a Web server, and shows
it. The worker thread for communication process using AsyncTask is created to avoid the communications
performing on the UI thread. Contents sent/received in the communications with the server are not
considered as sensitive (e.g. the URL of the image, or the image data) here. So, the received data such
as the URL of the image and the image data may be provided by attackers10 . To show the sample code
simply, any countermeasures are not taken in the sample code by considering the received attacking data
as tolerable. Also, the handlings for possible exceptions during HTTP connections or showing image
data are omitted. It is necessary to handle the exceptions properly depending on the application specs.
Because the sample code is HTTP communication, the android:usesCleartextTraffic attribute value in
AndroidManifest.xml is set to “true”, which was the default up to Android 8.1 (API level 27). Because
“false” became the default setting (in other words, HTTPS communication became the default) starting
from Android 9 (API level 28), an error will occur in HTTP communication unless “true” is explicitly set.
In this way, when android:usesCleartextTraffic=“true” is set, this permits all HTTP communication11 ,
but instead, to limit the domains where HTTP communication is allowed, make the setting by referring
to “Prevent unencrypted (HTTP) communication” in “5.4.3.7. Network Security Configuration”. Start-
ing from Android 7.0 (API level 24), the Network Security Configuration setting has priority over the
android:usesCleartextTraffic attribute12 .
Points:
1. Sensitive information must not be contained in send data.
2. Suppose that received data may be sent from attackers.
10 In fact, a vulnerability that executes any selected code when a PNG image is loaded was found in February 2019.
(https://source.android.com/security/bulletin/2019-02-01.html)
11 “This flag is honored on a best effort basis because it’s impossible to prevent all cleartext traffic from Android
386
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.https.imagesearch"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@drawable/ic_launcher"
android:allowBackup="false"
android:label="@string/app_name"
android:usesCleartextTraffic="true">
<activity
android:name=".ImageSearchActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Light"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
HttpImageSearch.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.https.imagesearch;
import android.os.AsyncTask;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
@Override
(continues on next page)
387
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 2 *** Suppose that received data may be sent from
// attackers.
// This is sample, so omit the process in case of the searching result
// is the data from an attacker.
responseArray = getByteArray(con);
if (responseArray == null) {
return null;
}
} catch (IOException e) {
// Exception handling is omitted
}
return responseArray;
}
try {
inputStream = new BufferedInputStream(con.getInputStream());
responseArray = new ByteArrayOutputStream();
388
Secure Coding Guide Documentation Release 2019-12-01
ImageSearchActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.https.imagesearch;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
389
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mQueryBox = (EditText)findViewById(R.id.querybox);
mMsgBox = (TextView)findViewById(R.id.msgbox);
mImgBox = (ImageView)findViewById(R.id.imageview);
}
@Override
protected void onPause() {
// After this, Activity may be deleted, so cancel the asynchronization
// process in advance.
if (mAsyncTask != null) mAsyncTask.cancel(true);
super.onPause();
}
// Cancel, since the last asynchronous process might not have been
// finished yet.
if (mAsyncTask != null) mAsyncTask.cancel(true);
390
Secure Coding Guide Documentation Release 2019-12-01
In HTTPS communication, a server is checked whether it is trusted or not as well as data transferred
is encrypted. To authenticate the server, Android HTTPS library verifies “server certificate” which is
transmitted from the server in the handshake phase of HTTPS transaction with following points:
• The server certificate is signed by a trusted third party certificate authority
• The period and other properties of the server certificate are valid
• The server’s host name matches the CN (Common Name) or SAN (Subject Alternative Names) in
the Subject field of the server certificate
SSLException (server certificate verification exception) is raised if the above verification is failed. This
possibly means man-in-the-middle attack13 or just server certificate defects. Your application has to
handle the exception with an appropriate sequence based on the application specifications.
The next a sample code is for HTTPS communication which connects to a Web server with a server
certificate issued by a trusted third party certificate authority. For HTTPS communication with a server
certificate issued privately, please refer to “5.4.1.3. Communicating via HTTPS with private certificate”.
The following sample code shows an application which performs an image search on a Web server, gets
the result image and shows it. HTTPS communication with the server is performed twice a search. The
first communication is for searching image data and the second is for getting it. The worker thread for
communication process using AsyncTask is created to avoid the communications performing on the UI
thread. All contents sent/received in the communications with the server are considered as sensitive
(e.g. the character string for searching, the URL of the image, or the image data) here. To show the
sample code simply, no special handling for SSLException is performed. It is necessary to handle the
exceptions properly depending on the application specifications. For the HTTPS communication by
javax.net.ssl.HttpsUrlConnection that is used in the sample code, in devices running Android 7.1.1(API
Level 25) or lower, if the server has not disabled connections by SSL 3.0, vulnerable SSL 3.0 communica-
tion could be established. As an example of a corrective measure at the app side, in the sample code, a
custom class (NoSSLv3SocketFactory class) was created that inherited the javax.net.ssl.SSLSocketFac-
13 Concerning “man-in-the-middle attack”, please refer to https://www.ipa.go.jp/about/press/20140919_1.html .
391
Secure Coding Guide Documentation Release 2019-12-01
tory class, and SSL 3.0 was set as an exception from protocol transferred to setEnabledProtocols()14 .
As a corrective measure not on the app side, we recommend configuring settings15 on remote servers to
disable SSL 3.0. Besides SSL 3.0, this also applies in the same way to vulnerable initial versions of TLS,
such as TLS 1.0.
Based on the information in RFC281816 , the use of CN, which is an existing customary practice in
verification of server certificates, is not recommended, and the use of SAN is strongly recommended for
comparing domain names and certificates. For this reason, Android 9.0 (API level 28) was changed so
that SAN only is used for verifications, and the server must present a certificate including SAN, and if
the certificate does not include one, it is no longer trusted.
Points:
1. URI starts with https://.
2. Sensitive information may be contained in send data.
3. Handle the received data carefully and securely, even though the data was sent from the server
connected by HTTPS.
4. SSLException should be handled with an appropriate sequence in an application.
HttpsImageSearch.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.https.imagesearch;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.AsyncTask;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.regex.Matcher;
(continues on next page)
14 Connections via SSL3.0 will not arise, as these are prohibited at the platform level in Android 8.0 (API Level 26) and
later versions; In this case, no corrective measures by inheriting SSLSocketFactory in the sample code are required.
15 For example, in the Apache 2.4 series, set “SSLProtocol all -SSLv3” in ssl.conf.
16 “HTTP Over TLS”(https://tools.ietf.org/html/rfc2818)
392
Secure Coding Guide Documentation Release 2019-12-01
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocketFactory;
@Override
protected Object doInBackground(String... params) {
HttpsURLConnection con1, con2;
ArrayList<String> imageUrlList = new ArrayList<>();
byte[] responseArray = null;
try{
// --------------------------------------------------------
// Communication 1st time : Execute image search
// --------------------------------------------------------
StringBuilder s = new StringBuilder();
for (String param : params) {
s.append(param);
s.append('+');
}
s.deleteCharAt(s.length() - 1);
// *** POINT 1 *** URI starts with https://.
// *** POINT 2 *** Sensitive information may be contained in send data.
// Code for sending image search string is omitted.
String search_url = "https://www.google.com/search?tbm=isch&q=" +
s.toString();
// *** POINT 3 *** Handle the received data carefully and securely,
// even though the data was sent from the server connected by HTTPS.
// Omitted, since this is a sample. Please refer to
// "3.2 Handling Input Data Carefully and Securely."
con1 = connectUrl(search_url);
BufferedReader in = new BufferedReader(
new InputStreamReader(con1.getInputStream()));
String inputLine;
StringBuffer sb = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
}
in.close();
final String regex = "<img.+?src=\"(.+?)\".+?>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(sb.toString());
while (matcher.find()) {
if (matcher.group(1).startsWith("https://"))
imageUrlList.add(matcher.group(1));
}
if (imageUrlList == null || imageUrlList.isEmpty()) {
return null;
}
// --------------------------------------------------------
// Communication 2nd time : Get image
// --------------------------------------------------------
393
Secure Coding Guide Documentation Release 2019-12-01
try {
inputStream = new BufferedInputStream(con.getInputStream());
responseArray = new ByteArrayOutputStream();
394
Secure Coding Guide Documentation Release 2019-12-01
NoSSLv3SocketFactory.java
package org.jssec.android.https.imagesearch;
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
(continues on next page)
395
Secure Coding Guide Documentation Release 2019-12-01
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws␣
˓→IOException {
@Override
public Socket createSocket(String host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)␣
˓→throws IOException {
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int␣
˓→localPort) throws IOException {
396
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
@Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
@Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
(continues on next page)
397
Secure Coding Guide Documentation Release 2019-12-01
@Override
public SSLSession getSession() {
return delegate.getSession();
}
@Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
@Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
@Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
@Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
@Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
@Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
@Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
@Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
@Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
@Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
@Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
398
Secure Coding Guide Documentation Release 2019-12-01
@Override
public synchronized void close() throws IOException {
delegate.close();
}
@Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
@Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
@Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
@Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
@Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
@Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
@Override
(continues on next page)
399
Secure Coding Guide Documentation Release 2019-12-01
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
@Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
@Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
@Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
@Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
@Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
@Override
public boolean isBound() {
return delegate.isBound();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isConnected() {
(continues on next page)
400
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
@Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
@Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
@Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
@Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
@Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
@Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
@Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
(continues on next page)
401
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
Other sample code files (AndroidManifest.xml, ImageSearchActivity.java) are the same as “5.4.1.1. Com-
municating via HTTP”, so please refer to “5.4.1.1. Communicating via HTTP”
This section shows a sample code of HTTPS communication with a server certificate issued privately
(private certificate), but not with that issued by a trusted third party authority. Please refer to “5.4.3.1.
How to Create Private Certificate and Configure Server Settings” for creating a root certificate of a
private certificate authority and private certificates and setting HTTPS settings in a Web server. The
sample program has a cacert.crt file in assets. It is a root certificate file of private certificate authority.
The following sample code shows an application which gets an image on a Web server and shows it.
HTTPS is used for the communication with the server. The worker thread for communication process
using AsyncTask is created to avoid the communications performing on the UI thread. All contents
(the URL of the image and the image data) sent/received in the communications with the server are
considered as sensitive here. To show the sample code simply, no special handling for SSLException is
performed. It is necessary to handle the exceptions properly depending on the application specifications.
Points:
1. Verify a server certificate with the root certificate of a private certificate authority.
2. URI starts with https://.
3. Sensitive information may be contained in send data.
4. Received data can be trusted as same as the server.
5. SSLException should be handled with an appropriate sequence in an application.
PrivateCertificateHttpsGet.java
/*
(continues on next page)
402
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.https.privatecertificate;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;
import android.content.Context;
import android.os.AsyncTask;
@Override
protected Object doInBackground(String... params) {
TrustManagerFactory trustManager;
BufferedInputStream inputStream = null;
ByteArrayOutputStream responseArray = null;
byte[] buff = new byte[1024];
int length;
try {
URL url = new URL(params[0]);
// *** POINT 1 *** Verify a server certificate with the root
// certificate of a private certificate authority.
// Set keystore which includes only private certificate that is stored
// in assets, to client.
KeyStore ks = KeyStoreUtil.getEmptyKeyStore();
KeyStoreUtil.loadX509Certificate(ks,
mContext.getResources().getAssets().open("cacert.crt"));
(continues on next page)
403
Secure Coding Guide Documentation Release 2019-12-01
trustManager.init(ks);
SSLContext sslCon = SSLContext.getInstance("TLS");
sslCon.init(null, trustManager.getTrustManagers(), new SecureRandom());
response.setSSLSocketFactory(sslCon.getSocketFactory());
checkResponse(response);
// *** POINT 4 *** Received data can be trusted as same as the server.
inputStream = new BufferedInputStream(response.getInputStream());
responseArray = new ByteArrayOutputStream();
while ((length = inputStream.read(buff)) != -1) {
if (length > 0) {
responseArray.write(buff, 0, length);
}
}
return responseArray.toByteArray();
} catch(SSLException e) {
// *** POINT 5 *** SSLException should be handled with an appropriate
// sequence in an application.
// Exception process is omitted here since it's sample.
return e;
} catch(Exception e) {
return e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
// This is sample, so omit the exception process
}
}
if (responseArray != null) {
try {
responseArray.close();
} catch (Exception e) {
// This is sample, so omit the exception process
}
}
}
}
404
Secure Coding Guide Documentation Release 2019-12-01
KeyStoreUtil.java
package org.jssec.android.https.privatecertificate;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
405
Secure Coding Guide Documentation Release 2019-12-01
PrivateCertificateHttpsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.https.privatecertificate;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUrlBox = (EditText)findViewById(R.id.urlbox);
mMsgBox = (TextView)findViewById(R.id.msgbox);
mImgBox = (ImageView)findViewById(R.id.imageview);
}
@Override
protected void onPause() {
// After this, Activity may be discarded, so cancel asynchronous process
// in advance.
if (mAsyncTask != null) mAsyncTask.cancel(true);
super.onPause();
}
406
Secure Coding Guide Documentation Release 2019-12-01
In HTTP transaction, sent and received information might be sniffed or tampered and the connected
server might be masqueraded. Sensitive information must be sent/ received by HTTPS communication.
5.4.2.2 Received Data over HTTP Must be Handled Carefully and Securely (Required)
Received data in HTTP communications might be generated by attackers for exploiting vulnerability of
an application. So you have to suppose that the application receives any values and formats of data and
then carefully implement data handlings for processing received data so as not to put any vulnerabilities
in. Furthermore you should not blindly trust the data from HTTPS server too. Because the HTTPS
server may be made by the attacker or the received data may be made in other place from the HTTPS
server. Please refer to “3.2. Handling Input Data Carefully and Securely”.
407
Secure Coding Guide Documentation Release 2019-12-01
the other hand, no special notice to the user might be required in some case. Like this, because how
to handle SSLException depends on the application specs and characteristics you need to determine it
after first considering thoroughly.
As mentioned above, the application may be attacked by man-in-the-middle attack when SSLException
occurs, so it must not be implemented like trying to send/receive sensitive information again via non
secure protocol such as HTTP.
Just Changing KeyStore which is used for verifying server certificates is enough to communicate via
HTTPS with a private certificate like self-signed certificate. However, as explained in “5.4.3.3. Risky
Code that Disables Certificate Verification”, there are so many dangerous TrustManager implementations
as sample codes for such purpose on the Internet. An Application implemented by referring to these
sample codes may have the vulnerability.
When you need to communicate via HTTPS with a private certificate, refer to the secure sample code
in “5.4.1.3. Communicating via HTTPS with private certificate”.
Of course, custom TrustManager can be implemented securely, but enough knowledge for encryption
processing and encryption communication is required so as not to implement vulnerable codes. So this
rule dare be (Required).
Just Changing KeyStore which is used for verifying server certificates is enough to communicate via
HTTPS with a private certificate like self-signed certificate. However, as explained in “5.4.3.3. Risky Code
that Disables Certificate Verification”, there are so many dangerous HostnameVerifier implementations
as sample codes for such purpose on the Internet. An Application implemented by referring to these
sample codes may have the vulnerability.
When you need to communicate via HTTPS with a private certificate, refer to the secure sample code
in “5.4.1.3. Communicating via HTTPS with private certificate”.
Of course, custom HostnameVerifier can be implemented securely, but enough knowledge for encryption
processing and encryption communication is required so as not to implement vulnerable codes. So this
rule dare be (Required).
In this section, how to create a private certificate and configure server settings in Linux such as Ubuntu
and CentOS is described. Private certificate means a server certificate which is issued privately and
is told from server certificates issued by trusted third party certificate authorities like Cybertrust and
VeriSign.
First of all, you need to create a private certificate authority to issue a private certificate. Private
certificate authority means a certificate authority which is created privately as well as private certificate.
You can issue plural private certificates by using the single private certificate authority. PC in which the
private certificate authority is stored should be limited strictly to be accessed just by trusted persons.
To create a private certificate authority, you have to create two files such as the following shell script
newca.sh and the setting file openssl.cnf and then execute them. In the shell script, CASTART and
CAEND stand for the valid period of certificate authority and CASUBJ stands for the name of certificate
authority. So these values need to be changed according to a certificate authority you create. While
408
Secure Coding Guide Documentation Release 2019-12-01
executing the shell script, the password for accessing the certificate authority is asked for 3 times in
total, so you need to input it every time.
umask 0077
CONFIG=openssl.cnf
CATOP=./CA
CAKEY=cakey.pem
CAREQ=careq.pem
CACERT=cacert.pem
CAX509=cacert.crt
CASTART=130101000000Z # 2013/01/01 00:00:00 GMT
CAEND=230101000000Z # 2023/01/01 00:00:00 GMT
CASUBJ="/CN=JSSEC Private CA/O=JSSEC/ST=Tokyo/C=JP"
mkdir -p ${CATOP}
mkdir -p ${CATOP}/certs
mkdir -p ${CATOP}/crl
mkdir -p ${CATOP}/newcerts
mkdir -p ${CATOP}/private
touch ${CATOP}/index.txt
openssl.cnf - Setting file of openssl command which 2 shell scripts refers in common
[ ca ]
default_ca = CA_default # The default ca section
[ CA_default ]
dir = ./CA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of several ctificates␣
˓→with same subject.
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = supplied
(continues on next page)
409
Secure Coding Guide Documentation Release 2019-12-01
[ usr_cert ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName = @alt_names
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
[ alt_names ]
DNS.1 = ${ENV::HOSTNAME}
DNS.2 = *.${ENV::HOSTNAME}
After executing the above shall script, a directory named CA is created just under the work directory.
This CA directory is just a private certificate authority. CA/cacert.crt file is the root certificate of the
private certificate authority. And it’s stored in assets directory of an application as described in “5.4.1.3.
Communicating via HTTPS with private certificate”, or it’s installed in Android device as described in
“5.4.3.2. Install Root Certificate of Private Certificate Authority to Android OS’s Certification Store”.
To create a private certificate, you have to create a shell script like the following newca.sh and execute it.
In the shell script, SVSTART and SVEND stand for the valid period of private certificate, and SVSUBJ
stands for the name of Web server, so these values need to be changed according to the target Web server.
Especially, you need to make sure not to set a wrong host name to /CN of SVSUBJ with which the host
name of Web server is to be specified. While executing the shell script, the password for accessing the
certificate authority is asked, so you need to input the password which you have set when creating the
private certificate authority. After that, y/n is asked 2 times in total and you need to input y every time.
newsv.sh - Shell script which issues private certificate
#!/bin/bash
umask 0077
CONFIG=openssl.cnf
CATOP=./CA
CAKEY=cakey.pem
CACERT=cacert.pem
SVKEY=svkey.pem
SVREQ=svreq.pem
SVCERT=svcert.pem
SVX509=svcert.crt
SVSTART=130101000000Z # 2013/01/01 00:00:00 GMT
SVEND=230101000000Z # 2023/01/01 00:00:00 GMT
HOSTNAME=selfsigned.jssec.org
SVSUBJ="/CN="${HOSTNAME}"/O=JSSEC Secure Coding Group/ST=Tokyo/C=JP"
410
Secure Coding Guide Documentation Release 2019-12-01
After executing the above shall script, a private key file for Web server “svkey.pem” and private certificate
file “svcert.pem” are created just under the work directory.
If the Web server is Apache, you will specify prikey.pem and cert.pem in the configuration file as follows
SSLCertificateFile "/path/to/svcert.pem"
SSLCertificateKeyFile "/path/to/svkey.pem"
5.4.3.2 Install Root Certificate of Private Certificate Authority to Android OS’s Certification Store
In the sample code of “5.4.1.3. Communicating via HTTPS with private certificate”, the method to
establish HTTPS sessions to a Web server from one application using a private certificate by installing
the root certificate into the application is introduced. In this section, the method to establish HTTPS
sessions to Web servers from all applications using private certificates by installing the root certificate
into Android OS is to be introduced. Note that all you install should be certificates issued by trusted
certificate authorities including your own certificate authorities.
However, the method described here can be used in versions prior to Android 6.0 (API level 23) only.
Starting from Android 7.0 (API level 24), even if the root certificate of the private certificate authority is
installed, the system ignores it. Starting from API level 24, to use a private certificate, refer to the section
“Communicating via HTTPS with private certificates” in “5.4.3.7. Network Security Configuration”.
First of all, you need to copy the root certificate file “cacert.crt” to the internal storage of an Android de-
vice. You can also get the root certificate file used in the sample code from https://www.jssec.org/dl/an-
droid_securecoding_sample_cacert.crt.
And then, you will open Security page from Android Settings and you can install the root certificate in
an Android device by doing as follows.
411
Secure Coding Guide Documentation Release 2019-12-01
Android Once the root certificate is installed in Android OS, all applications can correctly verify every
private certificate issued by the certificate authority. The following figure shows an example when
displaying https://selfsigned.jssec.org/droid_knight.png in Chrome browser.
Fig. 5.4.4: Once root certificate installed, private certificates can be verified correctly
By installing the root certificate this way, even applications using the sample code “5.4.1.2. Communi-
cating via HTTPS” can correctly connect via HTTPS to a Web server which is operated with a private
certificate.
412
Secure Coding Guide Documentation Release 2019-12-01
A lot of incorrect samples (code snippets), which allow applications to continue to communicate via
HTTPS with Web servers even after certificate verification errors occur, are found on the Internet. Since
they are introduced as the way to communicate via HTTPS with a Web server using a private certificate,
there have been so many applications created by developers who have used those sample codes by copy
and paste. Unfortunately, most of them are vulnerable to man-in-the-middle attack. As mentioned in
the top of this article, “In 2012, many defects in implementation of HTTPS communication were pointed
out in Android applications”, many Android applications which would have implemented such vulnerable
codes have been reported.
Several code snippets to cause vulnerable HTTPS communication are shown below. When you find this
type of code snippets, it’s highly recommended to replace the sample code of “5.4.1.3. Communicating
via HTTPS with private certificate”.
Risk:Case which creates empty TrustManager
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// Do nothing -> accept any certificates
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// Do nothing -> accept any certificates
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
SSLSocketFactory sf;
[...]
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
If you wish to specify your own individual HTTP request header for HTTP or HTTPS communication,
use the setRequestProperty() or addRequestProperty() methods in the URLConnection class. If you will
be using input data received from external sources as parameters for these methods, you must implement
HTTP header-injection protections. The first step in attacks based on HTTP header injection is to
include carriage-return codes—which are used as separators in HTTP headers—in input data. For this
reason, all carriage-return codes must be eliminated from input data.
413
Secure Coding Guide Documentation Release 2019-12-01
try {
URL url = new URL(strUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
[...]
When an app uses HTTPS communication, one step in the handshake procedure carried out at the start
of the communication is to check whether or not the certificate sent from the remote server is signed by a
third-party certificate authority. However, attackers may acquire improper certificates from third-party
authentication agents, or may acquire signed keys from a certificate authority to construct improper
certificates. In such cases, apps will be unable to detect the attack during the handshake process—even
in the event of a lure to an improper server established by the attacker, or of an man-in-the-middle
attack —and, as a result, there is a possibility that damage may be done.
“The technique of pinning” is an effective strategy for preventing man-in-the-middle attacks using these
types of certificates from improper third-party certificate authorities. In this method, certificates and
public keys for remote servers are stored in advance within an app, and this information is used for
handshake processing and re-testing after handshake processing has completed.
Pinning may be used to restore the security of communications in cases where the credibility of a
third-party certificate authority—the foundation of public-key infrastructure—has been tarnished. App
developers should assess the asset level handled by their own apps and decide whether or not to implement
these tests.
Use certificates and public keys stored within an app during the handshake procedure
To use information contained in remote-server certificates or public keys stored within an app during
the handshake procedure, an app must create its own KeyStore containing this information and use it
when communicating. This will allow the app to detect improprieties during the handshake procedure
even in the event of a man-in-the-middle attack using a certificate from an improper third-party cer-
tificate authority, as described above. Consult the sample code presented in the section titled “5.4.1.3.
Communicating via HTTPS with private certificate” for detailed methods of establishing your app’s own
KeyStore to conduct HTTPS communication.
414
Secure Coding Guide Documentation Release 2019-12-01
Use certificates and public-key information stored within an app for re-testing after the handshake
procedure is complete
To re-test the remote server after the handshake procedure has completed, an app first obtains the
certificate chain that was tested and trusted by the system during the handshake, then compares this
certificate chain against the information stored in advance within the app. If the result of this comparison
indicates agreement with the information stored within the app, the communication may be permitted
to proceed; otherwise, the communication procedure should be aborted.
However, if an app uses the methods listed below in an attempt to obtain the certificate chain that the
system trusted during the handshake, the app may not obtain the expected certificate chain, posing a
risk that the pinning may not function properly17 .
• javax.net.ssl.SSLSession.getPeerCertificates()
• javax.net.ssl.SSLSession.getPeerCertificateChain()
What these methods return is not the certificate chain that was trusted by the system during the
handshake, but rather the very certificate chain that the app received from the communication partner
itself. For this reason, even if an man-in-the-middle attack has resulted in a certificate from an improper
certificate authority being appended to the certificate chain, the above methods will not return the
certificate that was trusted by the system during the handshake; instead, the certificate of the server
to which the app was originally attempting to connect will also be returned at the same time. This
certificate—“the certificate of the server to which the app was originally attempting to connect”—will,
because of pinning, be equivalent to the certificate pre-stored within the app; thus re-testing it will not
detect any improprieties. For this and other similar reasons, it is best to avoid using the above methods
when implementing re-testing after the handshake.
On Android versions 4.2 (API Level 17) and later, using the checkServerTrusted() method within
net.http.X509TrustManagerExtensions will allow the app to obtain only the certificate chain that was
trusted by the system during the handshake.
An example illustrating pinning using X509TrustManagerExtensions
// Store the SHA-256 hash value of the public key included in the correct
// certificate for the remote server (pinning)
private static final Set<String> PINS = new HashSet<>(Arrays.asList(
new String[] {
"d9b1a68fceaa460ac492fb8452ce13bd8c78c6013f989b76f186b1cbba1315c1",
"cd13bb83c426551c67fabcff38d4496e094d50a20c7c15e886c151deb8531cdc"
}
));
[...]
415
Secure Coding Guide Documentation Release 2019-12-01
[...]
}
5.4.3.6 Strategies for addressing OpenSSL vulnerabilities using Google Play Services
Google Play Services (version 5.0 and later) provides a framework known as Provider Installer. This
may be used to address vulnerabilities in Security Provider, an implementation of OpenSSL and other
encryption-related technologies. For details, see Section “5.6.3.5. Addressing Vulnerabilities with Security
Provider from Google Play Services”.
Android 7.0 (API Level 24) introduced a framework known as Network Security Configuration that
allows individual apps to configure their own security settings for network communication. Using this
framework makes it easy for apps to incorporate a variety of techniques for improving app security,
including not only HTTPS communication with private certificates and public key pinning but also
prevention of unencrypted (HTTP) communication and the use of private certificates enabled only during
debugging18 .
The various types of functionality offered by Network Security Configuration may be accessed simply by
configuring settings in xml files, which may be applied to the entirety of an app’s HTTP and HTTPS
communications. This eliminates the need for modifying an app’s code or carrying out any additional
processing, simplifying implementation and providing an effective protection against Incorporating bugs
or vulnerabilities.
Section “5.4.1.3. Communicating via HTTPS with private certificate” presents sample code that per-
forms HTTPS communication with private certificates (e.g. self-signed certificates or intra-company
certificates). However, by using Network Security Configuration, developers may use private certificates
without implementation presented in the sample code of Section “5.4.1.2. Communicating via HTTPS”.
18 For more information on Network Security Configuration, see https://developer.android.com/training/articles/
security-config.html
416
Secure Coding Guide Documentation Release 2019-12-01
In the example above, the private certificates (private_ca) used for communication may be stored as
resources within the app, with the conditions for their use and their range of applicability described
in .xml files. By using the <domain-config> tag, it is possible to apply private certificates to specific
domains only. To use private certificates for all HTTPS communications performed by the app, use the
<base-config> tag, as shown below.
Use private certificates for all HTTPS communications performed by the app
Pinning
We mentioned public key pinning in Section “5.4.3.5. Notes and sample implementations for pinning”
By using Network Security Configuration to configure settings as in the example below, you eliminate
the need to implement the authentication process in your code; instead, the specifications in the xml file
suffice to ensure proper authentication.
Use public key pinning for HTTPS communication
The quantity described by the <pin> tag above is the base64-encoded hash value of the public key used
for pinning. The only supported hash function is SHA-256.
Using Network Security Configuration allows you to prevent HTTP communication (unencrypted com-
munication) from apps.
The methods of restricting unencrypted communications are as follows.
417
Secure Coding Guide Documentation Release 2019-12-01
1. Basically, the <base-config> tag is used to restrict unencrypted communications (HTTP commu-
nication) in communication with all domains19
2. Only for domains that require unencrypted communications for unavoidable reasons, the <domain-
config> tag can be used to individually set exceptions that allow unencrypted communications.
For details on determining whether unencrypted communications should be permitted, refer to
“5.4.1.1. Communicating via HTTP”.
Unencrypted communications are restricted by setting the cleartextTrafficPermitted attribute to false.
An example of this is shown below.
This setting is also applied in the WebView from Android 8.0 (API level 26), but be aware that it is not
applied to WebView for Android 7.1 (API level 25) and earlier.
Prior to Android 9.0 (API level 28), the default value of the attribute cleartextTrafficPermitted was
true, but from Android 9.0, it was changed to false. For this reason, if targeting API level 28 and
higher, declaration using <base-config> in the above example is not needed. However, to clearly define
the intention and to avoid the effect of different behavior depending on the target API level, explicitly
including as shown in the example above is recommended.
For purposes of debugging during app development, developers may wish to use private certificates to
communicate with certain HTTPS servers that exist for app-development purposes. In this case, de-
velopers must be careful to ensure that no dangerous implementations—including code that disables
certificate authentication—are incorporated into the app; this is discussed in Section “5.4.3.3. Risky
Code that Disables Certificate Verification”. In Network Security Configuration, settings may be con-
figured as in the example below to specify a set of certificates to be used only when debugging (only
if android:debuggable is set to “true” in the file AndroidManifest.xml). This eliminates the risk that
dangerous code may inadvertently be retained in the release version of an app, thus constituting a useful
means of preventing vulnerabilities.
Use private certificates only when debugging
19 See the following API reference about how the Network Security Configuration works for non-HTTP connections.
https://developer.android.com/reference/android/security/NetworkSecurityPolicy.html#isCleartextTrafficPermitted
418
Secure Coding Guide Documentation Release 2019-12-01
The 1994 release of SSL 2, which was its first public release, had a major vulnerability in security
protocol, and so SSL 3.0 (RFC 6101) was completely redesigned from the ground up and was released
in the second half of 1995. However, due to a vulnerability known as POODLE20 announced by the
Google Security Team in 2014, it was found that the padding for SSL 3.0 was not safe. In TLS 1.0 (RFC
2246), which was released in 1999, a defect in the padding design was corrected, but an attack method
known as BEAST that extracts encrypted data was announced in 2011 by Duong and Rizzo21 . TLS 1.1
(RFC 4346) was released in 2006 with security fixes (enhanced safety from TLS 1.0), and TLS 1.2 (RFC
5246), which was released in 2008, enables the use of even stronger encryption algorithms, including the
use of SHA-2 hash functions (SHA-256 and SHA-384) and supports cipher suites where authenticated
encryption with associated data (AEAD) usage modes (GCM, CCM) can be used.
With this as a background, in its guidelines22 on TLS issued on October 15, 2018, the (U.S.) National
Institute of Standards and Technology (NIST) either deprecated or prohibited the use of TLS 1.1 and
lower, and it requires not only government agencies, but also the servers that support non-government
apps to migrate to TLS 1.2. In line with this move, given the rash of security incidents in recent years
and the availability of new TLS versions, an increasing number of sites and services are discontinuing
support for “old versions of SSL or TLS”, and the transition to TLS 1.2 is well underway23 .
For example, one manifestation of this transition is a new security standard known as “the Payment
Card Industry Data Security Standard (PCI DSS)”, established by the Payment Card Industry Security
Standards Council (PCI SSC). The latest version is v3.2.1 released in May 201824 . Smartphones and
tablets are also widely used for E-commerce today, with credit cards typically used for payment. Indeed,
we expect that many users of this document (Android Application Secure Design / Secure Coding Guide)
will offer services that send credit-card information and other data to the server side; when using credit
cards in networked environments, it is essential to ensure the security of the data pathway, and PCI
DSS is a standard that governs the handling of member data in services of this type, designed with the
objective of preventing improper card use, information leaks, and other harmful consequences. Among
these security standards, although the exact version numbers are not specified, support for all SSL
versions and early TLS versions susceptible to known exploits (attack programs) was discontinued on
June 30, 2018, and websites were required to upgrade to a safer version (TLS 1.2 or higher).
In communication between smartphones and servers, the need to ensure the security of data pathways
is not restricted to handling of credit-card information, but is also an extremely important aspect of
operations involving the handling of private data or other sensitive information. Thus, the need to
transition to secure connections using TLS 1.2 on the service-provision (server) side may now be said to
be an urgent requirement.
On the other hand, in Android—which runs on the client side—WebView functionality supporting TLS
1.1 and later versions has been available since Android 4.4 (Kitkat), and for direct HTTP communication
since Android 4.1 (early Jelly Bean), although some additional implementation is needed in this case.
Among service developers, the adoption of TLS 1.2 means cutting off access to users of Android 4.3 and
earlier versions, so it might seem that such a step would have significant repercussions. However, as
shown in the figure below, the most recent data25 (current as of May 2019) show that Android versions
4.4 and later account for the overwhelming majority—96.2%—of all Android systems currently in use.
In view of this fact, and considering the importance of guaranteeing the security of assets handled by
apps, we recommend that serious consideration be paid to transitioning to TLS 1.2.
20 “This POODLE bites: exploiting the SSL 3.0 fallback”(Google Security Team, October 14, 2014) (https://
googleonlinesecurity.blogspot.co.uk/2014/10/this-poodle-bites-exploiting-ssl-30.html)
21 “Here come the Ninjas”(Thai Duong, Juliano Rizzo, May 13, 2011) (http://www.hit.bme.hu/%7Ebuttyan/courses/
EIT-SEC/abib/04-TLS/BEAST.pdf)
22 “Guidelines for the Selection, Configuration, and Use of Transport Layer Security (TLS) Implementations” (Rvision
document_library)
25 Distribution dashboard - Platform versions (https://developer.android.com/about/dashboards/index.html)
419
Secure Coding Guide Documentation Release 2019-12-01
Fig. 5.4.5: Distribution of OS versions among Android systems in current use(Source: Android Devel-
opers site)
TLS 1.3 (RFC 8446), which was released in August 2018, was a complete redesign of protocols and
encryption algorithms for the purpose of providing fixes for new vulnerabilities and exploits discovered
since the issuing of TLS 1.2 and for providing performance enhancements26 . Starting from Android
10, platform TLS implementation supports TLS 1.3, and TLS 1.3 is enabled for all TLS connections
by default27 . Also, the following cipher suites with low safety were removed starting from Android 10
(mode: CBC, MAC: SHA2)28 .
• TLS_RSA_WITH_AES_128_CBC_SHA256
• TLS_RSA_WITH_AES_256_CBC_SHA256
• TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
• TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
• TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
• TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
In recent years, “Privacy-by-Design” concept has been proposed as a global trend to protect the privacy
data. And based on the concept, governments are promoting legislation for privacy protection.
Applications that make use of user data in smartphones must take steps to ensure that users may use the
application safely and securely without fears regarding privacy and personal data. These steps include
handling user data appropriately and asking users to choose whether or not an application may use certain
data. To this end, each application must prepare and display an application privacy policy indicating
which information the application will use and how it will use that information; moreover, when fetching
and using certain information, the application must first ask the user’s permission. Note that application
26 The Transport Layer Security (TLS) Protocol Version 1.3 (https://datatracker.ietf.org/doc/rfc8446/)
27 Android Q features and APIs - TLS 1.3 support (https://developer.android.com/preview/features#tls-1.3)
28 SHA-2 CBC cipher suites removed (https://developer.android.com/preview/behavior-changes-all#
sha2-cbc-cipher-suites)
420
Secure Coding Guide Documentation Release 2019-12-01
privacy policies differ from other documents that may have been present in the past—such as “Personal
Data Protection Policies” or “Terms of Use”—and must be created separately from any such documents.
For details on the creation and execution of privacy policies, see the document “Smartphone Privacy
Initiative” and “Smartphone Privacy Initiative II” (JMIC’s SPI) released by Japan’s Ministry of Internal
Affairs and Communications (MIC).
The terminology used in this section is defined in the text and in Section “5.5.3.2. Glossary of Terms”.
When preparing application privacy policy, you may use the “Tools to Assist in Creating Application
Privacy Policies”29 . These tools output two files—a summary version and a detailed version of the
application privacy policy —both in HTML format and XML format. The HTML and XML content of
these files comports with the recommendations of MIC’s SPI including features such as search tags. In
the sample code below, we will demonstrate the use of this tool to present application privacy policy
using the HTML files prepared by this tool.
More specifically, you may use the following flowchart to determine which sample code to use.
29 http://www.kddi-research.jp/newsrelease/2013/090401.html
421
Secure Coding Guide Documentation Release 2019-12-01
Fig. 5.5.2: Flow Figure to select sample code of handling privacy data
Here the phrase “broad consent” refers to a broad permission, granted by the user to the application
upon the first launch of the application through display and review of the application privacy policy, for
the application to transmit user data to servers.
In contrast, the phrase “specific consent” refers to pre consent obtained immediately prior to the trans-
mission of specific user data.
5.5.1.1 Both broad consent and specific consent are granted: Applications that incorporate appli-
cation privacy policy
Points: (Both broad consent and specific consent are granted: Applications that incorporate application
privacy policy)
1. On first launch (or application update), obtain broad consent to transmit user data that will be
handled by the application.
2. If the user does not grant broad consent, do not transmit user data.
3. Obtain specific consent before transmitting user data that requires particularly delicate handling.
4. If the user does not grant specific consent, do not transmit the corresponding data.
5. Provide methods by which the user can review the application privacy policy.
6. Provide methods by which transmitted data can be deleted by user operations.
7. Provide methods by which transmitting data can be stopped by user operations.
8. Use UUIDs or cookies to keep track of user data.
9. Place a summary version of the application privacy policy in the assets folder.
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
(continues on next page)
422
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.privacypolicy;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import org.jssec.android.privacypolicy.ConfirmFragment.DialogListener;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnSuccessListener;
import android.Manifest;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.core.content.ContextCompat;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
423
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void beforeTextChanged(CharSequence s,
int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s,
int start, int before, int count) {
boolean buttonEnable = (s.length() > 0);
MainActivity.this
.findViewById(R.id.buttonStart).setEnabled(buttonEnable);
}
@Override
public void afterTextChanged(Editable s) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFusedLocationClient =
LocationServices.getFusedLocationProviderClient(this);
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[],
int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission is granted
// Fetch user ID from server
new GetDataAsyncTask().execute();
findViewById(R.id.buttonStart).setEnabled(false);
(continues on next page)
424
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onStart() {
super.onStart();
SharedPreferences pref =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);
int privacyPolicyAgreed =
pref.getInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, -1);
if (privacyPolicyAgreed <= VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW) {
// *** POINT 1 *** On first launch (or application update),
// obtain broad consent to transmit user data that will be handled
// by the application.
// When the application is updated, it is only necessary to renew
// the user's grant of broad consent
// if the updated application will handle new types of user data.
ConfirmFragment dialog =
ConfirmFragment.newInstance(R.string.privacyPolicy,
R.string.agreePrivacyPolicy,
DIALOG_TYPE_COMPREHENSIVE_AGREEMENT);
dialog.setDialogListener(this);
FragmentManager fragmentManager = getSupportFragmentManager();
dialog.show(fragmentManager, "dialog");
}
}
425
Secure Coding Guide Documentation Release 2019-12-01
pref.putInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, getVersionCode());
pref.apply();
} else if (type == DIALOG_TYPE_PRE_CONFIRMATION) {
// *** POINT 3 *** Obtain specific consent before transmitting user
// data that requires particularly delicate handling.
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
String nickname =
((TextView) findViewById(R.id.editTextNickname))
.getText().toString();
if (location != null) {
String locationData =
"Latitude:" + location.getLatitude() +
", Longitude:" + location.getLongitude();
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
"\n - nickname : " + nickname +
"\n - location : " + locationData,
Toast.LENGTH_SHORT).show();
new SendDataAsyncTack().execute(SEND_DATA_URI,
UserId, locationData, nickname);
} else {
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
"\n - nickname : " + nickname +
"\n - location : unavailable",
Toast.LENGTH_SHORT).show();
}
}
});
// Store the status of user consent.
// Actually, it is necessary to obtain consent for each user data type.
SharedPreferences.Editor pref =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE)
.edit();
pref.putInt(PRIVACY_POLICY_DISCRETE_TYPE1_AGREED_KEY,
getVersionCode());
pref.apply();
}
}
426
Secure Coding Guide Documentation Release 2019-12-01
return versionCode;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_pp:
// *** POINT 5 *** Provide methods by which the user can review the
// application privacy policy.
Intent intent = new Intent();
intent.setClass(this, WebViewAssetsActivity.class);
startActivity(intent);
return true;
case R.id.action_del_id:
// *** POINT 6 *** Provide methods by which transmitted data can be
// deleted by user operations.
new SendDataAsyncTack().execute(DEL_ID_URI, UserId);
return true;
case R.id.action_donot_send_id:
// *** POINT 7 *** Provide methods by which transmitting data can be
// stopped by user operations.
// If the user stop sending data, user consent is deemed to have been
// revoked.
SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME,␣
˓→MODE_PRIVATE).edit();
pref.putInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, 0);
pref.apply();
427
Secure Coding Guide Documentation Release 2019-12-01
return true;
}
return false;
}
@Override
protected String doInBackground(String... params) {
// *** POINT 8 *** Use UUIDs or cookies to keep track of user data
// In this sample we use an ID generated on the server side
SharedPreferences sp =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);
UserId = sp.getString(ID_KEY, null);
if (UserId == null) {
// There is not token in SharedPreferences, obtain ID from server
try {
UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");
} catch (IOException e) {
// Handle exception such as certificate error
extMessage = e.toString();
}
// Save obtained ID in SharedPreferences
sp.edit().putString(ID_KEY, UserId).commit();
}
return UserId;
}
@Override
protected void onPostExecute(final String data) {
String status = (data != null) ? "success" : "error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
@Override
protected Boolean doInBackground(String... params) {
String url = params[0];
String id = params[1];
String location = params.length > 2 ? params[2] : null;
String nickname = params.length > 3 ? params[3] : null;
if (nickname != null)
jsonData.put(NICK_NAME_KEY, nickname);
(continues on next page)
428
Secure Coding Guide Documentation Release 2019-12-01
result = true;
} catch (IOException e) {
// Catch exceptions such as certification errors
extMessage = e.toString();
} catch (JSONException e) {
extMessage = e.toString();
}
return result;
}
@Override
protected void onPostExecute(Boolean result) {
String status = result ? "Success" : "Error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
}
ConfirmFragment.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicy;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
429
Secure Coding Guide Documentation Release 2019-12-01
@Override
public Dialog onCreateDialog(Bundle args) {
// *** POINT 1 *** On first launch (or application update), obtain broad
// consent to transmit user data that will be handled by the application.
// *** POINT 3 *** Obtain specific consent before transmitting user data
// that requires particularly delicate handling.
final int title = getArguments().getInt("title");
final int sentence = getArguments().getInt("sentence");
final int type = getArguments().getInt("type");
builder.setPositiveButton(R.string.buttonConsent,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onPositiveButtonClick(type);
}
}
});
builder.setNegativeButton(R.string.buttonDonotConsent,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onNegativeButtonClick(type);
}
}
(continues on next page)
430
Secure Coding Guide Documentation Release 2019-12-01
return dialog;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof DialogListener)) {
throw new ClassCastException(activity.toString() +
" must implement DialogListener.");
}
mListener = (DialogListener) activity;
}
WebViewAssetsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicy;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
431
Secure Coding Guide Documentation Release 2019-12-01
webView.loadUrl(ABST_PP_URL);
}
}
5.5.1.2 Broad consent is granted: Applications that incorporate application privacy policy
Points: (Broad consent is granted: Applications that incorporate application privacy policy)
1. On first launch (or application update), obtain broad consent to transmit user data that will be
handled by the application.
2. If the user does not grant broad consent, do not transmit user data.
3. Provide methods by which the user can review the application privacy policy.
4. Provide methods by which transmitted data can be deleted by user operations.
5. Provide methods by which transmitting data can be stopped by user operations.
6. Use UUIDs or cookies to keep track of user data.
7. Place a summary version of the application privacy policy in the assets folder.
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynopreconfirm;
import java.io.IOException;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import org.jssec.android.privacypolicynopreconfirm.ConfirmFragment.DialogListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import android.text.Editable;
(continues on next page)
432
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void beforeTextChanged(CharSequence s,
int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s,
int start, int before, int count) {
boolean buttonEnable = (s.length() > 0);
MainActivity.this.findViewById(R.id.buttonStart)
.setEnabled(buttonEnable);
}
@Override
public void afterTextChanged(Editable s) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
433
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onStart() {
super.onStart();
SharedPreferences pref =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);
int privacyPolicyAgreed =
pref.getInt(PRIVACY_POLICY_AGREED_KEY, -1);
434
Secure Coding Guide Documentation Release 2019-12-01
return versionCode;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_pp:
// *** POINT 3 *** Provide methods by which the user can review the
// application privacy policy.
Intent intent = new Intent();
intent.setClass(this, WebViewAssetsActivity.class);
startActivity(intent);
return true;
case R.id.action_del_id:
// *** POINT 4 *** Provide methods by which transmitted data can be
// deleted by user operations.
new SendDataAsyncTack().execute(DEL_ID_URI, UserId);
return true;
case R.id.action_donot_send_id:
// *** POINT 5 *** Provide methods by which transmitting data can be
// stopped by user operations.
// If the user stop sending data, user consent is deemed to have been
// revoked.
SharedPreferences.Editor pref =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE)
.edit();
pref.putInt(PRIVACY_POLICY_AGREED_KEY, 0);
pref.apply();
return true; }
return false;
(continues on next page)
435
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected String doInBackground(String... params) {
// *** POINT 6 *** Use UUIDs or cookies to keep track of user data
// In this sample we use an ID generated on the server side
SharedPreferences sp = getSharedPreferences(PRIVACY_POLICY_PREF_NAME,
MODE_PRIVATE);
UserId = sp.getString(ID_KEY, null);
if (UserId == null) {
// No token in SharedPreferences; fetch ID from server
try {
UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");
} catch (IOException e) {
// Catch exceptions such as certification errors
extMessage = e.toString();
}
@Override
protected void onPostExecute(final String data) {
String status = (data != null) ? "success" : "error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
@Override
protected Boolean doInBackground(String... params) {
String url = params[0];
String id = params[1];
String nickname = params.length > 2 ? params[2] : null;
String lineNum = params.length > 3 ? params[3] : null;
if (nickname != null)
jsonData.put(NICK_NAME_KEY, nickname);
if (lineNum != null)
jsonData.put(LN_KEY, lineNum);
result = true;
(continues on next page)
436
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onPostExecute(Boolean result) {
String status = result ? "Success" : "Error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
}
ConfirmFragment.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynopreconfirm;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
437
Secure Coding Guide Documentation Release 2019-12-01
@Override
public Dialog onCreateDialog(Bundle args) {
// *** POINT 1 *** On first launch (or application update), obtain broad
// consent to transmit user data that will be handled by the application.
final int title = getArguments().getInt("title");
final int sentence = getArguments().getInt("sentence");
final int type = getArguments().getInt("type");
builder.setPositiveButton(R.string.buttonConsent,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onPositiveButtonClick(type);
}
}
});
builder.setNegativeButton(R.string.buttonDonotConsent,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (mListener != null) {
mListener.onNegativeButtonClick(type);
}
}
});
return dialog;
(continues on next page)
438
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof DialogListener)) {
throw new ClassCastException(activity.toString()
+ " must implement DialogListener.");
}
mListener = (DialogListener) activity;
}
WebViewAssetsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynopreconfirm;
import org.jssec.android.privacypolicynopreconfirm.R;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webSettings.setAllowFileAccess(false);
webView.loadUrl(ABST_PP_URL);
}
(continues on next page)
439
Secure Coding Guide Documentation Release 2019-12-01
5.5.1.3 Broad consent is not needed: Applications that incorporate application privacy policy
Points: (Broad consent is not needed: Applications that incorporate application privacy policy)
1. Provide methods by which the user can review the application privacy policy.
2. Provide methods by which transmitted data can be deleted by user operations.
3. Provide methods by which transmitting data can be stopped by user operations
4. Use UUIDs or cookies to keep track of user data.
5. Place a summary version of the application privacy policy in the assets folder.
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynocomprehensive;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Intent;
import android.content.SharedPreferences;
import androidx.fragment.app.FragmentActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
440
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void beforeTextChanged(CharSequence s,
int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s,
int start, int before, int count) {
boolean buttonEnable = (s.length() > 0);
MainActivity.this.findViewById(R.id.buttonStart)
.setEnabled(buttonEnable);
}
@Override
public void afterTextChanged(Editable s) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.buttonStart).setEnabled(false);
((TextView) findViewById(R.id.editTextNickname))
.addTextChangedListener(watchHandler);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_pp:
// *** POINT 1 *** Provide methods by which the user can review the
(continues on next page)
441
Secure Coding Guide Documentation Release 2019-12-01
return true;
}
return false;
}
@Override
protected String doInBackground(String... params) {
// *** POINT 4 *** Use UUIDs or cookies to keep track of user data
// In this sample we use an ID generated on the server side
SharedPreferences sp =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);
UserId = sp.getString(ID_KEY, null);
if (UserId == null) {
// No token in SharedPreferences; fetch ID from server
try {
UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");
} catch (IOException e) {
// Catch exceptions such as certification errors
extMessage = e.toString();
}
@Override
protected void onPostExecute(final String data) {
String status = (data != null) ? "success" : "error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
(continues on next page)
442
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected Boolean doInBackground(String... params) {
String url = params[0];
String id = params[1];
String nickname = params.length > 2 ? params[2] : null;
if (nickname != null)
jsonData.put(NICK_NAME_KEY, nickname);
result = true;
} catch (IOException e) {
// Catch exceptions such as certification errors
extMessage = e.toString();
} catch (JSONException e) {
extMessage = e.toString();
}
return result;
}
@Override
protected void onPostExecute(Boolean result) {
String status = result ? "Success" : "Error";
Toast.makeText(MainActivity.this,
this.getClass().getSimpleName() +
" - " + status + " : " + extMessage,
Toast.LENGTH_SHORT).show();
}
}
}
WebViewAssetsActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynocomprehensive;
import org.jssec.android.privacypolicynocomprehensive.R;
(continues on next page)
443
Secure Coding Guide Documentation Release 2019-12-01
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webSettings.setAllowFileAccess(false);
webView.loadUrl(ABST_PP_URL);
}
}
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.privacypolicynoinfosent;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnSuccessListener;
(continues on next page)
444
Secure Coding Guide Documentation Release 2019-12-01
import android.Manifest;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.core.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
import android.view.View;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFusedLocationClient =
LocationServices.getFusedLocationProviderClient(this);
if (Build.VERSION.SDK_INT >= 23) {
// API level 23 and greater requires permission to get location info.
Boolean permissionCheck = (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED);
if (!permissionCheck) {
// We have no permission, request to user
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_ACCESS_LOCATION);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[],
int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_ACCESS_LOCATION: {
if (grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
// permission is granted
int resultCode =
GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
// We cannot use Googleplay service, sample app will
// terminate.
finish();
}
} else {
// permission is not granted, we sample app will terminate.
(continues on next page)
445
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
446
Secure Coding Guide Documentation Release 2019-12-01
When transmitting usage data to external servers or other destinations, restrict transmissions to the bare
minimum necessary to provide service. In particular, you should design that applications have access to
only user data of which purpose of use the user can imagine on the basis of the application description.
For example, an application that the user can imagine it is an alarm application, must not have access
location data. On the other hand, if an alarm application can sound the alarm depending on the location
of user and its feature is written on the description of the application, the application may have access
to location data.
In cases where information need only be accessed within an application, avoid transmitting it externally
and take other steps to minimize the possibility of inadvertent leakage of user data.
5.5.2.2 On first launch (or application update), obtain broad consent to transmit user data that
requires particularly delicate handling or that may be difficult for users to change (Required)
If an application will transmit to external servers any user data that may be difficult for users to change,
or any user data that requires particularly delicate handling, the application must obtain advance consent
(opt-in) from the user—before the user begins using the application—informing the user of what types
of information will be sent, for what purposes, to servers, and whether or not any third-party providers
will be involved. More specifically, on first launch the application should display its application privacy
policy and confirm that the user has reviewed it and consented. Also, whenever an application is updated
in such a way that it now transmits new types of user data to external servers, it must again confirm
that the user has reviewed and consented to these changes. If the user does not consent, the application
should terminate or otherwise take steps to ensure that all functions requiring the transmission of data
are disabled.
These steps serve to guarantee that users understand how their data will be handled when they use an
application, providing users with a sense of security and enhancing their trust in the application.
MainActivity.java
protected void onStart() {
super.onStart();
447
Secure Coding Guide Documentation Release 2019-12-01
5.5.2.3 Obtain specific consent before transmitting user data that requires particularly delicate
handling (Required)
When transmitting to external servers any user data that requires particularly delicate handling, an
application must obtain advance consent (opt-in) from users for each such type of user data (or for each
feature that involves the transmission of user data); this is in addition to the need to obtain general
consent. If the user does not grant consent, the application must not send the corresponding data to the
external server.
This ensures that users can obtain a more thorough understanding of the relationship between an ap-
plication’s features (and the services it provides) and the transmission of user data for which the user
granted general consent; at the same time, application providers can expect to obtain user consent on
the basis of more precise decision-making.
448
Secure Coding Guide Documentation Release 2019-12-01
MainActivity.java
public void onSendToServer(View view) {
// *** POINT *** Obtain specific consent before transmitting user data
// that requires particularly delicate handling.
ConfirmFragment dialog =
ConfirmFragment.newInstance(R.string.sendLocation,
R.string.cofirmSendLocation,
DIALOG_TYPE_PRE_CONFIRMATION);
dialog.setDialogListener(this);
FragmentManager fragmentManager = getSupportFragmentManager();
dialog.show(fragmentManager, "dialog");
}
5.5.2.4 Provide methods by which the user can review the application privacy policy (Required)
In general, the Android application marketplace will provide links to application privacy policies for users
to review before choosing to install the corresponding application. In addition to supporting this feature,
it is important for applications to provide methods by which users can review application privacy policies
after installing applications on their devices. It is particularly important to provide methods by which
users can easily review application privacy policies in cases involving consent to transmit user data to
external servers to assist users in making appropriate decisions.
MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_pp:
// *** POINT *** Provide methods by which the user can review the
(continues on next page)
449
Secure Coding Guide Documentation Release 2019-12-01
5.5.2.5 Use UUIDs or cookies for identifiers linked with user data (Do not use device-specific iden-
tifiers) (Required)
IMEIs and other device-specific IDs should not be transmitted in ways that are tied to user data.
Indeed, if a device -specific ID and a piece of user data are bundled together and released or leaked to
public—even just once—it will be impossible subsequently to change that device -specific ID, whereupon
it will be impossible (or at least difficult) to sever ties between the ID and the user data. Also, in
Android 10, the obtaining of IMEI and other device-specific identifiers is no longer possible regardless of
the targetSdkVersion for the app. For this reason, UUIDs or cookies—that is, variable identifiers that
are regenerated each time based on random numbers without using device-specific identifiers—must be
transmitted together with user data. This allows an implementation of the notion, discussed below, of
the “right to be forgotten.”
MainActivity.java
@Override
protected String doInBackground(String... params) {
// *** POINT *** Use UUIDs or cookies to keep track of user data
// In this sample we use an ID generated on the server side
SharedPreferences sp =
getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);
UserId = sp.getString(ID_KEY, null);
(continues on next page)
450
Secure Coding Guide Documentation Release 2019-12-01
return UserId;
}
5.5.2.6 Place a summary version of the application privacy policy in the assets folder (Recom-
mended)
It is a good idea to place a summary version of the application privacy policy in the assets folder to
ensure that users may review it as necessary. Ensuring that the application privacy policy is present in
the assets folder not only allows users to access it easily at any time, but also avoids the risk that users
may see a counterfeit or corrupted version of the application privacy policy prepared by a malicious third
party.
5.5.2.7 Provide methods by which transmitted data can be deleted and transmitting data can be
stopped by user operations (Recommended)
It is a good idea to provide methods by which user data that has been transmitted to external servers
can be deleted at the user’s request. Similarly, in cases in which the application itself has stored user
data (or a copy thereof) within the device, it is a good idea to provide users with methods for deleting
this data. And, it is a good idea to provide methods by which transmitting user data can be stopped at
the user’s request.
This rule (recommendation) is codified by the “right to be forgotten” promoted in the EU; more generally,
in the future it seems clear that various proposals will call for further strengthening the rights of users to
have their data protected, and for this reason in these guidelines we recommend the provision of methods
for the deletion of user data unless there is some specific reason to do otherwise. And, regarding stop
transmitting data, it is the one that is defined by the point of view “Do Not Track (deny track)” of the
correspondence by the browser is progressing mainly.
MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// (some portions omitted)
case R.id.action_del_id:
// *** POINT *** Provide methods by which transmitted data can be
// deleted by user operations.
new SendDataAsyncTack().execute(DEL_ID_URI, UserId);
return true;
}
5.5.2.8 If you will only be using user data within the device, notify the user that data will not be
transmitted externally. (Recommended)
Even in cases in which user data will only be accessed temporarily within the user’s device, it is a good
idea to communicate this fact to the user to ensure that the user’s understanding of the application’s
451
Secure Coding Guide Documentation Release 2019-12-01
behavior remains full and transparent. More specifically, users should be informed that the user data
accessed by an application will only be used within the device for a certain specific purpose and will
not be stored or sent. Possible methods for communicating this content to users include specifying it
within the description of the application on the application marketplace. Information that is only used
temporarily within a device need not be discussed in the application privacy policy.
For cases in which a smartphone application will obtain user data and transmit this data externally, it
is necessary to prepare and display an application privacy policy to inform users of details such as the
types of data will be collected and the ways in which the data will be handled. The content that should
be included in an application privacy policy is detailed in the Smartphone Privacy Initiative advocated
by JMIC’s SPI. The primary objective of the application privacy policy should be to state clearly all
types of user data that will be accessed by an application, the purposes for which the data will be used,
where the data will be stored, and to what destinations the data will be transmitted.
A second document, separate from and required in addition to the application privacy policy, is the
Enterprise Privacy Policy, which details how all user data gathered by a corporation from its various
applications will be stored, managed, and disposed of. This Enterprise Privacy Policy corresponds to the
privacy policy that would traditionally have been prepared to comply with Japan’s Personal Information
Protection Law.
A detailed description of proper methods for preparing and displaying privacy policies, together with a
discussion of the roles played by the various different types of privacy policies, may be found in the doc-
ument “A Discussion of the Creation and Presentation of Privacy Policies for JSSEC Smartphone Appli-
cations”, available at this URL:https://www.jssec.org/event/20140206/03-1_app_policy.pdf (Japanese
only).
452
Secure Coding Guide Documentation Release 2019-12-01
In the table below we define a number of terms that are used in these guidelines; these definitions are taken
from the document “A Discussion of the Creation and Presentation of Privacy Policies for JSSEC Smart-
phone Applications” (https://www.jssec.org/event/20140206/03-1_app_policy.pdf) (Japanese only).
453
Secure Coding Guide Documentation Release 2019-12-01
The occasions on which Android ID values are generated or modified remain essentially unchanged, but
there are a few points to note, as discussed below.
• On package uninstallation / reinstallation:
As long as the signature of the app remains unchanged, its Android ID will be unchanged after
uninstalling and reinstalling. On the other hand, note that, if the key used as the signature
is modified, the Android ID will be different after re-installation, even if the package name is
unchanged.
• On updates to terminals running Android 8.0 (API Level 26) or later:
If an app was already installed on a terminal running Android 7.1 (API Level 25) or earlier, the
Android ID value that may be obtained by the app remains unchanged after the terminal is updated
to Android 8.0 (API Level 26) or later. However, this excludes cases in which apps are uninstalled
and reinstalled after the update.
Note that all Android IDs are classified as User information that is difficult for users to exchange
(as described in Section “5.5.3.2. Glossary of Terms”), and thus—as noted at the beginning of this
discussion—we recommend that similar levels of caution be employed when using Android IDs.
To protect privacy, in Android 10, more restrictions have been placed on the obtaining of non-resettable
device identifiers. To obtain device identifiers, the READ_PRIVILEGED_PHONE_STATE permission
is required, but this permission is normally not granted to an app. This change affects all apps running
in Android 10 regardless of the setting for targetSdkVersion. For this reason, even in apps that ran
using the granting of normal permissions, unexpected behavior can still occur due to occurrence of a
security exception or returning of null. The types of information that are affected by this and the APIs for
obtaining them are as follows. (It is assumed that required permissions such as READ_PHONE_STATE
were already granted.)
• Build Class
• TelephonyManager Class
In the security world, the terms “confidentiality”, “integrity”, and “availability” are used in analyzing
responses to threats. These three terms refer, respectively, to measures to prevent the third parties from
viewing private data, protections to ensure that the data referenced by users has not been modified (or
techniques for detecting when it has been falsified) and the ability of users to access services and data at
454
Secure Coding Guide Documentation Release 2019-12-01
all times. All three of these elements are important to consider when designing security protections. In
particular, encryption techniques are frequently used to ensure confidentiality and integrity, and Android
is equipped with a variety of cryptographic features to allow applications to realize confidentiality and
integrity.
In this section we will use sample code to illustrate methods by which Android applications can se-
curely implement encryption and decryption (to ensure confidentiality) and message authentication codes
(MAC) or digital signatures (to ensure integrity).
A variety of cryptographic methods have been developed for specific purposes and conditions, including
use cases such as “encrypting and decrypting data (to ensure confidentiality)” and “detecting falsifica-
tion of data (to ensure integrity)”. Here is sample code that is categorized into three broad groups of
cryptography techniques on the basis of the purpose of each technology. The features of the crypto-
graphic technology in each case should make it possible to choose an appropriate encryption method and
key type. For cases in which more detailed considerations are necessary, see Section “5.6.3.1. Choosing
encryption methods”.
Before designing an implementation that uses encryption technology, be sure to read Section “5.6.3.3.
Measures to Protect against Vulnerabilities in Random-Number Generators”.
• Protecting data from third-party eavesdropping
Fig. 5.6.1: Selection flowchart for sample code to protect data from eavesdropping
455
Secure Coding Guide Documentation Release 2019-12-01
You may use password-based key encryption for the purpose of protecting a user’s confidential data
assets.
Points:
1. Explicitly specify the encryption mode and the padding.
2. Use strong encryption technologies (specifically, technologies that meet the relevant criteria), in-
cluding algorithms, block cipher modes, and padding modes.
3. When generating a key from password, use Salt.
4. When generating a key from password, specify an appropriate hash iteration count.
5. Use a key of length sufficient to guarantee the strength of encryption.
AesCryptoPBEKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.cryptsymmetricpasswordbasedkey;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
456
Secure Coding Guide Documentation Release 2019-12-01
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption technologies (specifically,
// technologies that meet the relevant criteria),
// including algorithms, block cipher modes, and padding modes.
// Parameters passed to the getInstance method of the Cipher class:
// Encryption algorithm, block encryption mode, padding rule
// In this sample, we choose the following parameter values:
// encryption algorithm=AES, block encryption mode=CBC, padding
// rule=PKCS7Padding
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
// A string used to fetch an instance of the class that generates the key
private static final String KEY_GENERATOR_MODE =
"PBEWITHSHA256AND128BITAES-CBC-BC";
// *** POINT 3 *** When generating a key from a password, use Salt.
// Salt length in bytes
public static final int SALT_LENGTH_BYTES = 20;
AesCryptoPBEKey() {
mIV = null;
initSalt();
}
457
Secure Coding Guide Documentation Release 2019-12-01
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption technologies (specifically,
// technologies that meet the relevant criteria),
// including algorithms, modes, and padding.
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// *** POINT 3 *** When generating keys from passwords, use Salt.
SecretKey secretKey = generateKey(password, mSalt);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
mIV = cipher.getIV();
encrypted = cipher.doFinal(plain);
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} finally {
}
return encrypted;
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption technologies (specifically,
// technologies that meet the relevant criteria),
// including algorithms, block cipher modes, and padding modes.
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
// *** POINT 3 *** When generating a key from a password, use Salt.
SecretKey secretKey = generateKey(password, mSalt);
IvParameterSpec ivParameterSpec = new IvParameterSpec(mIV);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
plain = cipher.doFinal(encrypted);
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} finally {
}
return plain;
(continues on next page)
458
Secure Coding Guide Documentation Release 2019-12-01
try {
// *** POINT 2 *** Use strong encryption technologies (specifically,
// technologies that meet the relevant criteria),
// including algorithms, block cipher modes, and padding modes.
// Fetch an instance of the class that generates the key
// In this example, we use a KeyFactory that uses SHA256
// to generate AES-CBC 128-bit keys.
SecretKeyFactory secretKeyFactory =
SecretKeyFactory.getInstance(KEY_GENERATOR_MODE);
// *** POINT 3 *** When generating a key from a password, use Salt.
// *** POINT 4 *** When generating a key from a password,
// specify an appropriate hash iteration count.
// *** POINT 5 *** Use a key of length sufficient to guarantee
// the strength of encryption.
keySpec = new PBEKeySpec(password,
salt,
KEY_GEN_ITERATION_COUNT,
KEY_LENGTH_BITS);
// Clear password
Arrays.fill(password, '?');
// Generate the key
secretKey = secretKeyFactory.generateSecret(keySpec);
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
keySpec.clearPassword();
}
return secretKey;
}
}
In some cases, only data encryption will be performed -using a stored public key- on the application side,
while decryption is performed in a separate safe location (such as a server) under a private key. In cases
such as this, it is possible to use public-key encryption.
Points:
1. Explicitly specify the encryption mode and the padding
2. Use strong encryption methods (specifically, technologies that meet the relevant criteria), including
algorithms, block cipher modes, and padding modes.
3. Use a key of length sufficient to guarantee the strength of encryption.
RsaCryptoAsymmetricKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
(continues on next page)
459
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.cryptasymmetrickey;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption methods (specifically, technologies
// that meet the relevant criteria), including algorithms, block cipher
// modes, and padding modes..
// Parameters passed to getInstance method of the Cipher class: Encryption
// algorithm, block encryption mode, padding rule.
// In this sample, we choose the following parameter values: encryption
// algorithm=RSA, block encryption mode=NONE, padding rule=OAEPPADDING.
private static final String TRANSFORMATION = "RSA/NONE/OAEPPADDING";
// encryption algorithm
private static final String KEY_ALGORITHM = "RSA";
// *** POINT 3 *** Use a key of length sufficient to guarantee the strength of
// encryption.
// Check the length of the key
private static final int MIN_KEY_LENGTH = 2000;
RsaCryptoAsymmetricKey() {
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes..
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
(continues on next page)
460
Secure Coding Guide Documentation Release 2019-12-01
return encrypted;
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes..
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
plain = cipher.doFinal(encrypted);
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} finally {
}
return plain;
}
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(keyData));
} catch (IllegalArgumentException e) {
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
(continues on next page)
461
Secure Coding Guide Documentation Release 2019-12-01
return publicKey;
}
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
privateKey =
keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyData));
} catch (IllegalArgumentException e) {
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
}
return privateKey;
}
}
Pre shared keys may be used when working with large data sets or to protect the confidentiality of an
application’s or a user’s assets.
Points:
1. Explicitly specify the encryption mode and the padding
2. Use strong encryption methods (specifically, technologies that meet the relevant criteria), including
algorithms, block cipher modes, and padding modes.
3. Use a key of length sufficient to guarantee the strength of encryption.
AesCryptoPreSharedKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
(continues on next page)
462
Secure Coding Guide Documentation Release 2019-12-01
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption methods (specifically, technologies
// that meet the relevant criteria), including algorithms, block cipher
// modes, and padding modes.
// Parameters passed to getInstance method of the Cipher class: Encryption
// algorithm, block encryption mode, padding rule.
// In this sample, we choose the following parameter values: encryption
// algorithm=AES, block encryption mode=CBC, padding rule=PKCS7Padding
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
// Encryption algorithm
private static final String KEY_ALGORITHM = "AES";
// Length of IV in bytes
public static final int IV_LENGTH_BYTES = 16;
// *** POINT 3 *** Use a key of length sufficient to guarantee the strength of
// encryption
// Check the length of the key
private static final int MIN_KEY_LENGTH_BYTES = 16;
AesCryptoPreSharedKey() {
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
(continues on next page)
463
Secure Coding Guide Documentation Release 2019-12-01
encrypted = cipher.doFinal(plain);
}
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} finally {
}
return encrypted;
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
plain = cipher.doFinal(encrypted);
}
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} finally {
}
return plain;
}
try {
// *** POINT 3 *** Use a key of length sufficient to guarantee the
// strength of encryption
if (keyData.length >= MIN_KEY_LENGTH_BYTES) {
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
secretKey = new SecretKeySpec(keyData, KEY_ALGORITHM);
(continues on next page)
464
Secure Coding Guide Documentation Release 2019-12-01
return secretKey;
}
}
You may use password-based (shared-key) encryption to verify the integrity of a user’s data.
Points:
1. Explicitly specify the encryption mode and the padding.
2. Use strong encryption methods (specifically, technologies that meet the relevant criteria), including
algorithms, block cipher modes, and padding modes.
3. When generating a key from a password, use Salt.
4. When generating a key from a password, specify an appropriate hash iteration count.
5. Use a key of length sufficient to guarantee the MAC strength.
HmacPBEKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.signsymmetricpasswordbasedkey;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption methods (specifically, technologies
// that meet the relevant criteria),
// including algorithms, block cipher modes, and padding modes.
(continues on next page)
465
Secure Coding Guide Documentation Release 2019-12-01
// A string used to fetch an instance of the class that generates the key
private static final String KEY_GENERATOR_MODE = "PBEWITHHMACSHA1";
// *** POINT 3 *** When generating a key from a password, use Salt.
// Salt length in bytes
public static final int SALT_LENGTH_BYTES = 20;
// *** POINT 5 *** Use a key of length sufficient to guarantee the MAC
// strength.
// Key length in bits
private static final int KEY_LENGTH_BITS = 160;
HmacPBEKey() {
initSalt();
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
Mac mac = Mac.getInstance(TRANSFORMATION);
// *** POINT 3 *** When generating a key from a password, use Salt.
SecretKey secretKey = generateKey(password, mSalt);
mac.init(secretKey);
hmac = mac.doFinal(plain);
(continues on next page)
466
Secure Coding Guide Documentation Release 2019-12-01
return hmac;
}
if (Arrays.equals(hmac, hmacForPlain)) {
return true;
}
return false;
}
try {
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
// Fetch an instance of the class that generates the key
// In this example, we use a KeyFactory that uses SHA1 to
// generate AES-CBC 128-bit keys.
SecretKeyFactory secretKeyFactory =
SecretKeyFactory.getInstance(KEY_GENERATOR_MODE);
// *** POINT 3 *** When generating a key from a password, use Salt.
// *** POINT 4 *** When generating a key from a password, specify an
// appropriate hash iteration count.
// *** POINT 5 *** Use a key of length sufficient to guarantee the MAC
// strength.
keySpec = new PBEKeySpec(password, salt,
KEY_GEN_ITERATION_COUNT, KEY_LENGTH_BITS);
// Clear password
Arrays.fill(password, '?');
// Generate the key
secretKey = secretKeyFactory.generateSecret(keySpec);
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
keySpec.clearPassword();
}
return secretKey;
}
When working with data whose signature is determined using private keys stored in distinct, secure
locations (such as servers), you may utilize public-key encryption for applications involving the storage
467
Secure Coding Guide Documentation Release 2019-12-01
of public keys on the application side solely for the purpose of authenticating data signatures.
Points:
1. Explicitly specify the encryption mode and the padding.
2. Use strong encryption methods (specifically, technologies that meet the relevant criteria), including
algorithms, block cipher modes, and padding modes.
3. Use a key of length sufficient to guarantee the signature strength.
RsaSignAsymmetricKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.signasymmetrickey;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption methods (specifically, technologies
// that meet the relevant criteria), including algorithms, block cipher
// modes, and padding modes.
// Parameters passed to the getInstance method of the Cipher class:
// Encryption algorithm, block encryption mode, padding rule.
// In this sample, we choose the following parameter values: encryption
// algorithm=RSA, block encryption mode=NONE, padding rule=OAEPPADDING.
private static final String TRANSFORMATION = "SHA256withRSA";
// encryption algorithm
private static final String KEY_ALGORITHM = "RSA";
// *** POINT 3 *** Use a key of length sufficient to guarantee the signature
// strength.
// Check the length of the key
private static final int MIN_KEY_LENGTH = 2000;
RsaSignAsymmetricKey() {
}
(continues on next page)
468
Secure Coding Guide Documentation Release 2019-12-01
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
Signature signature = Signature.getInstance(TRANSFORMATION);
sign = signature.sign();
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeyException e) {
} catch (SignatureException e) {
} finally {
}
return sign;
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
Signature signature = Signature.getInstance(TRANSFORMATION);
ret = signature.verify(sign);
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeyException e) {
} catch (SignatureException e) {
} finally {
}
return ret;
}
469
Secure Coding Guide Documentation Release 2019-12-01
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(keyData));
} catch (IllegalArgumentException e) {
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
}
return publicKey;
}
try {
keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
privateKey = keyFactory
.generatePrivate(new PKCS8EncodedKeySpec(keyData));
} catch (IllegalArgumentException e) {
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeySpecException e) {
} finally {
}
return privateKey;
}
}
You may use pre-shared keys to verify the integrity of application assets or user assets.
Points:
1. Explicitly specify the encryption mode and the padding.
2. Use strong encryption methods (specifically, technologies that meet the relevant criteria), including
algorithms, block cipher modes, and padding modes.
3. Use a key of length sufficient to guarantee the MAC strength.
HmacPreSharedKey.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
(continues on next page)
470
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.signsymmetricpresharedkey;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
// *** POINT 1 *** Explicitly specify the encryption mode and the padding.
// *** POINT 2 *** Use strong encryption methods (specifically, technologies
// that meet the relevant criteria), including algorithms, block cipher
// modes, and padding modes.
// Parameters passed to the getInstance method of the Mac class:
// Authentication mode
private static final String TRANSFORMATION = "HmacSHA256";
// Encryption algorithm
private static final String KEY_ALGORITHM = "HmacSHA256";
// *** POINT 3 *** Use a key of length sufficient to guarantee the MAC
// strength.
// Check the length of the key
private static final int MIN_KEY_LENGTH_BYTES = 16;
HmacPreSharedKey() {
}
try {
// *** POINT 1 *** Explicitly specify the encryption mode and the
// padding.
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
Mac mac = Mac.getInstance(TRANSFORMATION);
471
Secure Coding Guide Documentation Release 2019-12-01
hmac = mac.doFinal(plain);
}
} catch (NoSuchAlgorithmException e) {
} catch (InvalidKeyException e) {
} finally {
}
return hmac;
}
return false;
}
try {
// *** POINT 3 *** Use a key of length sufficient to guarantee the MAC
// strength.
if (keyData.length >= MIN_KEY_LENGTH_BYTES) {
// *** POINT 2 *** Use strong encryption methods (specifically,
// technologies that meet the relevant criteria), including
// algorithms, block cipher modes, and padding modes.
secretKey = new SecretKeySpec(keyData, KEY_ALGORITHM);
}
} catch (IllegalArgumentException e) {
} finally {
}
return secretKey;
}
}
472
Secure Coding Guide Documentation Release 2019-12-01
5.6.2.1 When Specifying an Encryption Algorithm, Explicitly Specify the Encryption Mode and the
Padding (Required)
When using cryptographic technologies such as encryption and data verification, it is important that the
encryption mode and the padding be explicitly specified. When using encryption in Android application
development, you will primarily use the Cipher class within java.crypto. To use the Cipher class, you
will first create an instance of Cipher class object by specifying the type of encryption to use. This
specification is called a Transformation, and there are two formats in which Transformations may be
specified:
• “algorithm/mode/padding”
• “algorithm”
In the latter case, the encryption mode and the padding will be implicitly set to the appropriate default
values for the encryption service provider that Android may access. These default values are chosen to
prioritize convenience and compatibility and in some cases may not be particularly secure choices. For
this reason, to ensure proper security protections it is mandatory to use the former of the two formats,
in which the encryption mode and padding are explicitly specified.
5.6.2.2 Use Strong Algorithms (Specifically, Algorithms that Meet the Relevant Criteria) (Required)
When using cryptographic technologies it is important to choose strong algorithms which meet certain
criteria. In addition, in cases where an algorithm allows multiple key lengths, it is important to consider
the application’s full product lifetime and to choose keys of length sufficient to guarantee security.
Moreover, for some encryption modes and padding modes there exist known strategies of attack; it is
important to make choices that are robust against such threats.
Indeed, choosing weak encryption methods can have disastrous consequences; for example, files which
were supposedly encrypted to prevent eavesdropping by a third party may in fact be only ineffectually
protected and may allow third-party eavesdropping. Because the continual progress of IT leads to
continual improvements in encryption-analysis technologies, it is crucial to consider and select algorithms
that can guarantee security throughout the entire period during which you expect an application to
remain in operation.
Algorithm Security Lifetime, that are expected to be secure for the entire security life of the protected
data and Standards for actual encryption technologies differ from country to country, as detailed in the
tables below.
473
Secure Coding Guide Documentation Release 2019-12-01
Unit bit
Unit bit
30 NIST Special Publication 800-57 Part1 Revision4(1/28/2016) “Recommendation for Key Management Part1:Gen-
eral” “5.6 Guidance for Cryptographic Algorithm and Key Size Selection” (https://nvlpubs.nist.gov/nistpubs/
SpecialPublications/NIST.SP.800-57pt1r4.pdf)
31 NIST Special Publication 800-131A Revision2(3/21/2019) “Transitioning the Use of Cryptographic Algorithms
and Key Lengths” “1.1 Background and Purpose” “1.2.1 Security Strengths” (https://nvlpubs.nist.gov/nistpubs/
SpecialPublications/NIST.SP.800-131Ar2.pdf)
32 Implementation Guidance for FIPS 140-2 and the Cryptographic Module Validation Program(Last Mod-
474
Secure Coding Guide Documentation Release 2019-12-01
5.6.2.3 When Using Password-based Encryption, Do Not Store Passwords on Device (Required)
In password-based encryption, when generating an encryption key based on a password input by a user,
always use Salt. In addition, if you are providing features to different users within the same device, use
a different Salt for each user. The reason for this is that, if you generate encryption keys using only a
simple hash function without using Salt, the passwords may be easily recovered using a technique known
as a “rainbow table.” When Salt is applied, keys generated from the same password will be distinct
(different hash values), preventing the use of a rainbow table to search for keys.
(Sample) When generating keys from passwords, use salt
public final byte[] encrypt(final byte[] plain, final char[] password) {
byte[] encrypted = null;
try {
// *** POINT *** Explicitly specify the encryption mode
// and the padding.
// *** POINT *** When generating keys from passwords, use Salt.
SecretKey secretKey = generateKey(password, mSalt);
5.6.2.5 When Generating Key from Password, Specify Appropriate Hash Iteration Count (Required)
In password-based encryption, when generating an encryption key based on a password input by a user,
you will choose a number of times for the hashing procedure to be repeated during the process of key
generation (“stretching”); it is important to specify this number large enough to ensure security. In
general, the iteration count equal to 1,000 or greater is considered sufficient. If you are using the key to
protect even more valuable assets, specify a count equal to 1,000,000 or greater. Because the processing
time required for a single computation of the hash function is minuscule, it may be easy for attackers
to launch brute-force attacks. Thus, by using the stretching method - in which hash processing is
repeated many times - we can purposely ensure that the process consumes significant time and thus that
brute-force attacks are more costly. Note that the number of stretching repetitions will also affect your
application’s processing speed, so take care in choosing an appropriate value.
(Sample) When generating key from password, Set hash iteration counts
private static final SecretKey generateKey(final char[] password,
final byte[] salt) {
SecretKey secretKey = null;
PBEKeySpec keySpec = null;
(Omit)
// *** POINT *** When generating a key from password, use Salt.
// *** POINT *** When generating a key from password, specify
// an appropriate hash iteration count.
(continues on next page)
475
Secure Coding Guide Documentation Release 2019-12-01
In password-based encryption, when generating an encryption key based on a password input by a user,
the strength of the generated key is strongly affected by the strength of the user’s password, and thus
it is desirable to take steps to strengthen the passwords received from users. For example, you might
require that passwords be at least 8 characters long and contain multiple types of characters—perhaps
at least one letter, one numeral, and one symbol.
In the above “sample codes”, we showed implementation examples involving three types of cryptographic
methods each for encryption and decryption and for detecting data falsification. You may use “Fig. 5.6.1
Selection flowchart for sample code to protect data from eavesdropping”, “Fig. 5.6.2 Selection flowchart
for sample code to detect falsifications” to make a coarse-grained choice of which cryptographic method
to use based on your application. On the other hand, more fine-tuned choices of cryptographic methods
require more detailed comparisons of the features of various methods. In what follows we consider some
of these comparisons.
• Comparison of cryptographic methods for encryption and decryption
Public-key cryptography has high processing cost and thus is not well suited for large-scale data pro-
cessing. However, because the keys used for encryption and for decryption are different, it is relatively
easy to manage keys in cases where you handle only the public key on the application side (i.e. you
only perform encryption) and perform decryption in a separate (secure) location. Shared-key cryptog-
raphy is an all-purpose encryption scheme with few limitations, but in this case the same key is used
for encryption and decryption, and thus it is necessary to store the key securely within the application,
making key management difficult. Password-based cryptography (shared-key cryptography based on a
password) generates keys from user-specified passwords, obviating the need to store key-related secrets
within devices. This method is used for applications protecting only user assets but not application
assets. Because the strength of the encryption depends on the strength of the password, it is necessary
to choose passwords whose complexity grows in proportion to the value of assets to be protected. Please
refer to “5.6.2.6. Take Steps to Increase the Strengths of Passwords (Recommended)”.
476
Secure Coding Guide Documentation Release 2019-12-01
When using cryptographic technologies, it is extremely important to choose strong encryption algorithms
and encryption modes and sufficiently long keys in order to ensure the security of the data handled by
applications and services. However, even if all of these choices are made appropriately, the strength of the
security guaranteed by the algorithms in use plummets immediately to zero when the keys that form the
linchpin of the security protocol are leaked or guessed. Even for the initial vector (IV) used for shared-key
encryption under AES and similar protocols, or the Salt used for password-based encryption, large biases
can make it easy for third parties to launch attacks, heightening the risk of exposure to data leakage or
477
Secure Coding Guide Documentation Release 2019-12-01
corruption. To prevent such situations, it is necessary to generate keys and IVs in such a way as to make
it difficult for third parties to guess their values, and random numbers play an immensely important
role in ensuring the realization of this imperative. A device that generates random numbers is called a
random-number generator. Whereas hardware random-number generators (RNGs) may use sensors or
other devices to produce random numbers by measuring natural phenomena that cannot be predicted or
reproduced, it is more common to encounter software-implemented random-number generators, known
as pseudorandom-number generators (PRNGS).
In Android applications, random numbers of sufficient security for use in encryption may be generated
via the SecureRandom class. The SecureRandom class can internally have multiple implementations,
which are called providers and provide the function, and if no provider is explicitly specified, then the
default provider will be selected. Crypto Provider, which provides the SHA1PRNG algorithm that is
cryptographically unsafe35 , was deprecated in Android 7.0 (API level 24), and it was removed in Android
9.0 (API level 28)363738 . If Crypto Provider is specified and SecureRandom is used, NoSuchProviderEx-
ception will always occur in devices running Android 9.0 and higher, and NoSuchProviderException will
occur even in devices running Android 7.0 and higher if targetSdkVersion>=24. For this reason, gener-
ally, the use of SecureRandom without specifying the provider is recommended. In what follows we offer
examples to demonstrate the use of SecureRandom.
Note that SecureRandom may exhibit a number of weaknesses depending on the Android version, re-
quiring preventative measures to be put in place in implementations. Please refer to “5.6.3.3. Measures
to Protect against Vulnerabilities in Random-Number Generators”.
Using SecureRandom (using the default implementation)
import java.security.SecureRandom;
[...]
SecureRandom random = new SecureRandom();
byte[] randomBuf = new byte [128];
random.nextBytes(randomBuf);
[...]
The pseudorandom-number generators found in programs like SecureRandom typically operate on the
basis of a process like that illustrated in “Fig. 5.6.3 Inner process of pseudorandom number generator”.
A random number seed is entered to initialize the internal state; thereafter, the internal state is updated
each time a random number is generated, allowing the generation of a sequence of random numbers.
The seed plays an extremely important role in a pseudorandom number generator (PRNG).
35 On statistical distance based testing of pseudo random sequences and experiments with PHP and Debian OpenSSL -
8.1 Java SHA1PRNG API based sequences (https://webpages.uncc.edu/yonwang/papers/lilesorics.pdf)
36 Security “Crypto” provider deprecated in Android N (https://android-developers.googleblog.com/2016/06/
security-crypto-provider-deprecated-in.html)
37 Cryptography Changes in Android P (https://android-developers.googleblog.com/2018/03/
cryptography-changes-in-android-p.html)
38 SecureRandom (https://developer.android.com/reference/java/security/SecureRandom)
478
Secure Coding Guide Documentation Release 2019-12-01
As noted above, PRNGs must be initialized by specifying a seed. Thereafter, the process used to
generate random numbers is a deterministic algorithm, so if you specify the same seed you will get the
same sequence of random numbers. This means that if a third party gains access to (that is, eavesdrops
upon) or guesses the seed of a PRNG, he can produce the same sequence of random numbers, thus
destroying the properties of confidentiality and integrity that the random numbers provide.
For this reason, the seed of a random number generator is itself a highly confidential piece of informa-
tion—and one which must be chosen in such a way as to be impossible to predict or guess. For example,
time information or device-specific data (such as a MAC address, IMEI, or Android ID) should not be
used to construct RNG seeds. On many Android devices, /dev/urandom or /dev/random is available,
and the default implementation of SecureRandom provided by Android uses these device files to deter-
mine seeds for random number generators. As far as confidentiality is concerned, as long as the RNG
seed exists only in memory, there is little risk of discovery by third parties with the exception of malware
tools that acquire root privileges. If you need to implement security measures that remain effective even
on rooted devices, consult an expert in secure design and implementation.
The internal state of a pseudorandom number generator is initialized by the seed, then updated each
time a random number is generated. Just as for the case of PRNGs initialized by the same seed, two
PRNGs with the same internal state will subsequently produce precisely the same sequence of random
numbers. Consequently, it is also important to protect the internal state against eavesdropping by third
parties. However, because the internal state exists in memory, there is little risk of discovery by third
parties except in cases involving malware tools that acquire root access. If you need to implement
security measures that remain effective even on rooted devices, consult an expert in secure design and
implementation.
The “Crypto” Provider implementation of SecureRandom, found in Android versions 4.3.x and ear-
lier, suffered from the defect of insufficient entropy (randomness) of the internal state. In particular,
in Android versions 4.1.x and earlier, the “Crypto” Provider was the only available implementation of
SecureRandom, and thus most applications that use SecureRandom either directly or indirectly were
affected by this vulnerability. Similarly, the “AndroidOpenSSL” Provider offered as the default imple-
mentation of SecureRandom in Android versions 4.2 and later exhibited the defect that the majority of
the data items used by OpenSSL as “random-number seeds” were shared between applications (Android
versions 4.2.x—4.3.x), creating a vulnerability in which any one application can easily predict the ran-
dom numbers generated by other applications. The table below details the impact of the vulnerabilities
present in various versions of Android OS.
479
Secure Coding Guide Documentation Release 2019-12-01
Android 4.2 -
4.3.x • Use a clearly identified Crypto • Default implementation of Se-
Provider cureRandom
• Explicit use of AndroidOpenSSL
Provider
• Direct use of random-number
generation functionality provided
by OpenSSL
• Encryption functionality pro-
vided by the Cipher class
• HTTPS communication func-
tionality, etc.
Since August 2013, patches that remove these Android OS vulnerabilities have been distributed by
Google to its partners (device makers, etc.)
However, these vulnerabilities associated with SecureRandom affected a wide range of applications—in-
cluding encryption functionality and HTTPS communication functionality—and presumably many de-
vices remain unpatched. For this reason, when designing applications targeted at Android 4.3.x and
earlier, we recommend that you incorporate the countermeasures (implementations) discussed in the
following site.
https://android-developers.blogspot.jp/2013/08/some-securerandom-thoughts.html
When using encryption techniques to ensure the security (confidentiality and integrity) of sensitive data,
even the most robust encryption algorithm and key lengths will not protect data from third-party attacks
if the data content of the keys themselves are readily available. For this reason, the proper handling of
keys is among the most important items to consider when using encryption. Of course, depending on
the level of the assets you are attempting to protect, the proper handling of keys may require extremely
sophisticated design and implementation techniques which exceed the scope of these guidelines. Here we
can only offer some basic ideas regarding the secure handling of keys for various applications and key
storage locations; our discussion does not extend to specific implementation methods, and as necessary
we recommend that you consult an expert in secure design and implementation for Android.
To begin, “Fig. 5.6.4 Places of encrypt keys and strategies for protecting them” illustrates the various
480
Secure Coding Guide Documentation Release 2019-12-01
places in which keys used for encryption and related purposes in Android smartphones and tablets may
exist, and outlines strategies for protecting them.
Fig. 5.6.4: Places of encrypt keys and strategies for protecting them
The table below summarizes the asset classes of the assets protected by keys, as well as the protection
policies appropriate for various asset owners. For more information on asset classes, please refer to “3.1.3.
Asset Classification and Protective Countermeasures”.
If keys are stored in public strage such as an APK file or an SD card, it is as follows.
In what follows, we will augment the discussion of protective measures appropriate for the various places
in which keys may be stored.
481
Secure Coding Guide Documentation Release 2019-12-01
Here we are considering password-based encryption. When keys are generated from passwords, the
key storage location is the user’s memory, so there is no danger of leakage due to malware. However,
depending on the strength of the password, it may be easy to reproduce keys. For this reason, it is
necessary to take steps—similar to those taken when asking users to specify service login passwords—to
ensure the strength of passwords; for example, passwords may be restricted by the UI, or warning
messages may be used. Please refer to “5.5.2.6. Place a summary version of the application privacy
policy in the assets folder (Recommended)”. Of course, when passwords are stored in a user’s memory
one must keep in mind the possibility that the password will be forgotten. To ensure that data may be
recovered in the event of a forgotten password, it is necessary to store backup data in a secure location
other than the device (for example, on a server).
When keys are stored in Private mode in application directories, the key data cannot be read by other
applications. In addition, if the application has disabled backup functionality, users will also be unable
to access the data. Thus, when storing keys used to protect application assets in application directories,
you should disable backups.
However, if you also need to protect keys from applications or users with root privileges, you must encrypt
or obfuscate the keys. For keys used to protect user assets, you may use password-based encryption. For
keys used to encrypt application assets that you wish to keep private from users as well, you must store
the key used for key encryption in an APK file, and the key data must be obfuscated.
Because data in APK files may be accessed, in general this is not an appropriate place to store confidential
data such as keys. When storing keys in APK files, you must obfuscate the key data and take steps to
ensure that the data may not be easily read from the APK file.
Because public storage can be accessed by all applications, in general it is not an appropriate place
to store confidential data such as passwords. When storing keys in public locations, it is necessary
to encrypt or obfuscate the key data to ensure that the data cannot be easily accessed. See also the
protections suggested above under “Keys stored in application directories” for cases in which keys must
also be protected from applications or users with root privileges.
When using the cryptographic technologies available in Android, key data that have been encrypted or
obfuscated somewhere other than the application process shown in the figure above must be decrypted
(or, for password-based keys, generated) in advance of the encryption procedure; in this case, key data
will reside in process memory in unencrypted form. On the other hand, the memory of an application
process may not generally be read by other applications, so if the asset class falls within the range
covered by these guidelines there is no particular need to take specific steps to ensure security. In cases
where—due to the specific objective in question or to the level of the assets handled by an application—it
is unacceptable for key data to appear in unencrypted form (even though they are present that way in
process memory), it may be necessary to resort to obfuscation or other techniques for key data and
encryption logic. However, these methods are difficult to realize at the Java level; instead, you will use
obfuscation tools at the JNI level. Such measures fall outside the scope of these guidelines; consult an
expert in secure design and implementation.
482
Secure Coding Guide Documentation Release 2019-12-01
5.6.3.5 Addressing Vulnerabilities with Security Provider from Google Play Services
Google Play Services (Version 5.0 and later) provides a framework known as Provider Installer that may
be used to address vulnerabilities in Security Provider.
First, Security Provider provides implementations of various encryption-related algorithms based on Java
Cryptography Architecture (JCA). These Security Provider algorithms may be used via classes such as
Cipher, Signature, and Mac to make use of encryption technology in Android apps. In general, rapid
response is required whenever vulnerabilities are discovered in encryption-technology-related implemen-
tations. Indeed, the exploitation of such vulnerabilities for malicious purposes could result in major
damage. Because encryption technologies are also relevant for Security Provider, it is desirable that
revisions designed to address vulnerabilities be reflected as quickly as possible.
The most common method of reflecting Security Provider revisions is to use device updates. The process
of reflecting revisions via device updates begins with the device manufacturer preparing an update, after
which users apply this update to their devices. Thus, the question of whether or not an app has access
to an up-to-date version of Security Provider—including the most recent revisions—depends in practice
on compliance from both manufacturers and users. In contrast, using Provider Installer from Google
Play Services ensures that apps have access to automatically-updated versions of Security Provider.
With Provider Installer from Google Play Services, calling Provider Installer from an app allows access to
Security Provider as provided by Google Play Services. Google Play Services is automatically updated via
the Google Play Store, and thus the Security Provider provided by Provider Installer will be automatically
updated to the latest version, with no dependence on compliance from manufacturers or users.
Sample code that calls Provider Installer is shown below.
Call Provider Installer
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.security.ProviderInstaller;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ProviderInstaller.installIfNeededAsync(this, this);
setContentView(R.layout.activity_main);
}
@Override
public void onProviderInstalled() {
// Called when Security Provider is the latest version,
// or when installation completes.
}
@Override
public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
GoogleApiAvailability.getInstance().showErrorNotification(this, errorCode);
}
}
A variety of methods for biological authentication are currently under research and development, with
methods using facial information and vocal signatures particularly prominent. Among these methods,
methods for using fingerprint authentication to identify individuals have been used since ancient times,
and are used today for purposes such as signatures (by thumbprint) and crime investigation. Applications
483
Secure Coding Guide Documentation Release 2019-12-01
of fingerprinting have also advanced in several areas of the computer world, and in recent years these
methods have begun to enjoy wide recognition as highly convenient techniques (offering advantages such
as ease of input) for use in areas such as identifying the owner of a smartphone (primarily for unlocking
screens).
Capitalizing on these trends, Android 6.0(API Level 23) incorporates a framework for fingerprint authen-
tication on terminals, which allows apps to make use of fingerprint authentication features(Fingerprint-
Manager) to identify individuals. Also, in Android 9.0 (API level 28), a BiometricPrompt API was added
for providing comprehensive support for face recognition, iris recognition, and other biometric recognition
functions beyond just simply fingerprint authentication. Also, the authentication UI that previously had
to be provided separately by the app is no longer needed, and a standard authentication dialog box is
automatically used instead. Together with this change, the previous fingerprint authentication function
(FingerprintManager) was deprecated. In what follows we discuss some security precautions to keep in
mind when using BiometricPrompt authentication.
In biometric authentication functions, there are two major use cases: when a key linked to the user’s
authentication information is used and when simply performing user authentication only. Based on the
application of this biometric authentication, select the sample code based on Fig. 5.7.1.
Fig. 5.7.1: Selection flowchart for sample code using biometric authentication
At the time when Android 9.0 (API level 29) was released, no BiometricPrompt support library was avail-
able, and so this meant that usage was limited to devices running Android 9.0 only. However, currently,
the support library androidx.biometric is available, and this enables the use of BiometricPrompt in a
wide range of models from Android 6.0 and higher. The sample code shown below uses BiometricPrompt,
which is provided as a support library.
We present sample code below that allows an application to use Android’s biometric authentication
feature.
Points:
1. Declare the use of the USE_FINGERPRINT(Android 6.0 - Android 8.1) or USE_BIOMET-
RIC(Android 9.0 -) permission39 .
39 In Android 6.0 (API level 23) to Android 8.1 (API level 27) devices, for the BiometricPrompt of the support library
androidx.biometric that is used in the sample code, use of the supported USE_FINGERPRINT permission must be declared
in order to use the FingerPrintManager function and perform biometric (fingerprint) authentication. By contrast, in devices
484
Secure Coding Guide Documentation Release 2019-12-01
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.biometricprompt.cipher;
import androidx.appcompat.app.AlertDialog;
import androidx.biometric.BiometricPrompt;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.icu.text.SimpleDateFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Date;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
@Override
protected void onCreate(Bundle savedInstanceState) {
(continues on next page)
running Android 9.0 (API level 28) or higher, the BiometricPrompt function of android.hardware.biometrics is used, and
use of the USE_BIOMETRIC permission must be declared (In actuality, the use of these permissions has already been
declared in the manifest file of the support library package androidx.biometric, and so the manifest file at the app side that
uses it can run without any problems even if use was not declared).
485
Secure Coding Guide Documentation Release 2019-12-01
if (!isBiometricEnabled(this)) {
// *** POINT 3 *** Notify users that biometric information
// registration will be required to create a key
new AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage("No biometric information has been registered. \n" +
"Click \"Security\" on the Settings menu to register fingerprints. \n" +
"Registering biometric information allows easy authentication.")
.setPositiveButton("OK", null)
.show();
return;
}
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
Cipher cipher = result.getCryptoObject().getCipher();
try {
// *** POINT 7 *** Limit encrypted data to items that can be
// restored (replaced) by methods other than fingerprint
// authentication
byte[] encrypted = cipher.doFinal(SENSITIVE_DATA.getBytes());
showEncryptedData(encrypted);
} catch (IllegalBlockSizeException | BadPaddingException e) {
}
showMessage(getString(R.string.biometric_auth_succeeded),
R.color.colorAuthenticated);
reset();
}
@Override
public void onAuthenticationFailed() {
showMessage(getString(R.string.biometric_auth_failed),
R.color.colorError);
}
};
mBiometricAuthentication =
new BiometricAuthentication(this, callback);
486
Secure Coding Guide Documentation Release 2019-12-01
return simpleDateFormat.format(date);
}
BiometricAuthentication.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.biometricprompt.cipher;
(continues on next page)
487
Secure Coding Guide Documentation Release 2019-12-01
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.os.CancellationSignal;
import android.os.Handler;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyInfo;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import androidx.biometric.BiometricPrompt;
import androidx.fragment.app.FragmentActivity;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.util.concurrent.Executor;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
@Override
(continues on next page)
488
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onAuthenticationFailed() {
android.util.Log.e(TAG, "onAuthenticationFailed");
if (callback != null) {
callback.onAuthenticationFailed();
}
}
};
mBiometricPrompt =
new androidx.biometric.BiometricPrompt(context, mExecutor, hook);
final androidx.biometric.BiometricPrompt.PromptInfo.Builder builder =
new androidx.biometric.BiometricPrompt.PromptInfo.Builder()
.setTitle("Please Authenticate")
.setNegativeButtonText("Cancel");
mPromptInfo = builder.build();
reset();
}
if (!initializeCipherObject())
return false;
androidx.biometric.BiometricPrompt.CryptoObject cryptoObject =
new BiometricPrompt.CryptoObject(mCipher);
489
Secure Coding Guide Documentation Release 2019-12-01
mCipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
// *** POINT 6 *** Design your application on the assumption that
// the status of fingerprint registration will change between
// when keys are created and when keys are used
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeySpecException
| NoSuchProviderException | InvalidKeyException e) {
android.util.Log.e(TAG, "failed to init Cipher: " + e.getMessage());
throw new RuntimeException("failed to init Cipher", e);
(continues on next page)
490
Secure Coding Guide Documentation Release 2019-12-01
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.biometricprompt.cipher">
<!-- *** POINT 1 *** Declare the use of the USE_BIOMETRIC permission -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light">
<activity android:name="org.jssec.android.biometricprompt.cipher.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</manifest>
The sample code for using biometric authentication when user authentication only is performed is shown
below. In this case, you do not need to pay attention to any particular security points, but the sample
code is provided below for reference.
MainActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.biometricprompt.nocipher;
491
Secure Coding Guide Documentation Release 2019-12-01
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BiometricPrompt.AuthenticationCallback callback =
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode,
CharSequence errString) {
showMessage(errString, R.color.colorError);
}
@Override
public void onAuthenticationHelp(int helpCode,
CharSequence helpString) {
showMessage(helpString, R.color.colorHelp);
}
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
showMessage(getString(R.string.biometric_auth_succeeded),
R.color.colorAuthenticated);
}
@Override
public void onAuthenticationFailed() {
showMessage(getString(R.string.biometric_auth_failed),
R.color.colorError);
}
492
Secure Coding Guide Documentation Release 2019-12-01
return simpleDateFormat.format(date);
}
BiometricAuthentication.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.biometricprompt.nocipher;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricPrompt;
import android.os.CancellationSignal;
493
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onAuthenticationHelp(int helpCode,
CharSequence helpString) {
android.util.Log.d(TAG, "onAuthenticationHelp");
if (callback != null) {
callback.onAuthenticationHelp(helpCode, helpString);
}
}
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
android.util.Log.d(TAG, "onAuthenticationSuccess");
if (callback != null) {
callback.onAuthenticationSucceeded(result);
}
reset();
}
@Override
public void onAuthenticationFailed() {
android.util.Log.d(TAG, "onAuthenticationFailed");
if (callback != null) {
(continues on next page)
494
Secure Coding Guide Documentation Release 2019-12-01
return true;
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.biometric.authentication.nocipher">
<!-- *** POINT 1 *** Declare the use of the USE_BIOMETRIC permission -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="org.jssec.android.biometricprompt.nocipher.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</manifest>
Observe the following rules when using biometric authentication. There are no particular rules when
using the fingerprint authentication function for other applications.
1. When creating (registering) keys, use an encryption algorithm that is not vulnerable (meets stan-
dards). (Required)
495
Secure Coding Guide Documentation Release 2019-12-01
2. Restrict encrypted data to items that can be restored (replaced) by methods other than biometric
authentication. (Required)
3. Notify users that biometric information registration will be required to create a key. (Recommended)
5.7.2.1 When creating (registering) keys, use an encryption algorithm that is not vulnerable (meets
standards). (Required)
Like the password keys and public keys discussed in Section “5.6. Using Cryptography”, when using
biometric authentication features to create keys it is necessary to use encryption algorithms that are not
vulnerable—that is, algorithms that meet certain standards adequate to prevent eavesdropping by third
parties. Indeed, safe and non-vulnerable choices must be made not only for encryption algorithms but
also for encryption modes and padding.
For more information on selecting algorithms, see Section “5.6.2.2. Use Strong Algorithms (Specifically,
Algorithms that Meet the Relevant Criteria) (Required)”.
5.7.2.2 Restrict encrypted data to items that can be restored (replaced) by methods other than
biometric authentication. (Required)
When an app uses biometric authentication features for the encryption of data within the app, the app
must be designed in such a way as to allow the data to be recovered (replaced) by methods other than
biometric authentication.
In general, the use of biological information entails various problems—including secrecy, the difficulty
of making modifications, and erroneous identifications—and it is thus best to avoid relying solely on
biological information for authentication.
For example, suppose that data internal to an app is encrypted with a key generated using biometric
authentication features, but that the iometric data stored within the terminal is subsequently deleted by
the user. Then the key used to encrypt the data is not available for use, nor is it possible to copy the
data. If the data cannot be recovered by some means other than biometric-authentication functionality,
there is substantial risk that the data will be made useless.
Moreover, the deletion of biometric information is not the only scenario in which keys created using bio-
metric authentication functions can become unusable. In Nexus5X, if biometric authentication features
are used to create a key and this key is then newly registered as an addition to the biometric information,
keys created earlier have been observed to become unusable.
5.7.2.3 Notify users that biometric information registration will be required to create a key. (Rec-
ommended)
In order to create a key using biometric authentication, it is necessary that a user’s biometrics be
registered on the terminal. When designing apps to guide users to the Settings menu to encourage
biometric registration, developers must keep in mind that biometrics represent important personal data,
and it is desirable to explain to users why it is necessary or convenient for the app to use biometric
information.
Notify users the fingerprint registration will be required.
if (!mFingerprintAuthentication.isFingerprintAuthAvailable()) {
// *** Point *** Notify users that biometric registration will be
// required to create a key.
new AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage("No biometric information has been registered.\n" +
"Click \"Security\" on the Settings menu to register biometrics.\n" +
"Registering biometrics allows easy authentication.")
.setPositiveButton("OK", null)
(continues on next page)
496
Secure Coding Guide Documentation Release 2019-12-01
5.7.3.1 Preconditions for the use of biometric authentication features by Android apps
The following two conditions must be satisfied in order for an app to use biometric authentication.
• User biometrics must be registered within the terminal.
• An (application-specific) key must be associated with registered biometrics.
User biometric information can only be registered via the “Security” option in the Settings menu; ordinary
applications may not perform the biometric registration procedure. For this reason, if no biometrics have
been registered when an app attempts to use biometric authentication features, the app must guide the
user to the Settings menu and encourage the user to register biometrics. At this time, it is desirable
for the app to offer the user some explanation of why it is necessary and convenient to use biometric
information.
In addition, as a necessary precondition for biometric registration to be possible, the terminal must be
configured with an alternative screen-locking mechanism. If the screen lock is disabled in a state in which
biometric have been registered in the terminal, the registered biometric information will be deleted.
To associate a key with biometrics registered in a terminal, use a KeyStore instance provided by an
“AndroidKeyStore” Provider to create and register a new key or to register an existing key.
To create a key associated with biometric information, configure the parameter settings when creating a
KeyGenerator to enable requests for user authentication.
Creating and registering a key associated with biometric information.
try {
// Obtain an instance from the "AndroidKeyStore" Provider.
KeyGenerator keyGenerator =
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
keyGenerator.init(
new KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
// Enable requests for user (biometric) authentication.
.setUserAuthenticationRequired(true)
.build());
keyGenerator.generateKey();
} catch (IllegalStateException e) {
// no biometrics have been registered in this terminal.
throw new RuntimeException("No biometric registered", e);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| CertificateException | KeyStoreException | IOException e) {
// failed to generate a key.
(continues on next page)
497
Secure Coding Guide Documentation Release 2019-12-01
To associate biometric information with an existing key, register the key with a KeyStore entry to which
has been added a setting to enable user authentication requests.
Associating biometric information with an existing key.
498
Secure Coding Guide Documentation Release 2019-12-01
Difficult Problems
6
In Android, there are some problems that it is difficult to assure a security by application implementation
due to a specification of Android OS or a function which Android OS provides. By being abused by the
malicious third party or used by users carelessly, these functions are always holding risks that may lead
to security problems like information leakage. In this chapter, by indicating risk mitigation plans that
developers can take against these functions, some topics that needs calling attentions, are picked up as
articles.
Copy & paste are the functions which users often use in a casual manner. For example, not a few users
use these functions to store curious information or important information to remember in a mail or a
web page into a notepad, or to copy and to paste a password from a notepad in which passwords are
stored in order not to forget in advance. These are very casual actions at a glance, but actually there’s
a hidden risk that user handling information may be stolen.
The risk is related to mechanism of copy & paste in Android system. The information which was
copied by user or application, is once stored in the buffer called Clipboard. The information stored in
Clipboard is distributed to other applications when it is pasted by a user or an application. So there is a
risk which leads to information leakage in this Clipboard function. It is because the entity of Clipboard
is single in a system and any application can obtain the information stored in Clipboard at any time by
using ClipboardManager. It means that all the information which user copied/cut, is leaked out to the
malicious application.
Hence, application developers need to take measures to minimize the possibility of information leakage,
considering the Android OS specifications.
Roughly speaking, there are two outlooks of counter-measures to mitigate the risk of information leakage
form Clipboard.
1. Counter-measure when copying from other applications to your application.
2. Counter-measure when copying from your application to other applications.
Firstly, let us discuss the countermeasure 1 above. Supposing that a user copies character strings from
other applications like note pad, Web browser or mailer application, and then paste it to EditText in
your application. As it turns out, there’s no basic counter-measure to prevent from sensitive information
leakage due to copy & paste, in this scenario. Since there’s no function in Android to control copy
operations by the third party application.
499
Secure Coding Guide Documentation Release 2019-12-01
So, regarding the countermeasure 1, there’s no method other than explaining users the risk of copying &
pasting sensitive information, and just continuing to enlighten users to decrease the actions themselves
continuously.
Next discussion is the countermeasure 2 above, supposing that the scenario that a user copies sensitive
information displayed in your application. In this case, the sound counter-measure for leakage is to
prohibit copying/cutting operations from View (TextView, EditText etc.). If there are no copy/cut
functions in View where the sensitive information (like personal information) is input/output, information
leakage will never happen from your application via Clipboard.
There are several methods to prohibit copying/cutting. This section herein describes the easy and
effective methods: One method is to disable long press View and another method is to delete copy/cut
items from menu when selecting character string.
Necessary of counter-measure can be determined as per the flow of Fig. 6.1.1. Fig. 6.1.1, “Input type
is fixed to Password attribute” means, the input type is necessarily either of the followings three when
application is running. In this case, no counter-measures are required since copy/cut are prohibited as
default.
• InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
• InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASS-
WORD
• InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASS-
WORD
6.1.1.1 Delete copy/cut from the menu when character string selection
500
Secure Coding Guide Documentation Release 2019-12-01
package org.jssec.android.clipboard.leakage;
import android.app.Activity;
import android.os.Bundle;
import androidx.core.app.NavUtils;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.uncopyable);
501
Secure Coding Guide Documentation Release 2019-12-01
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.uncopyable, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
Prohibiting copying/cutting can also be realized by disabling Long Click View. Disabling Long Click
View can be specified in layout xml file.
Point:
1. Set false to android:longClickable in View to prohibit copy/cut.
unlongclickable.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/unlongclickable_description" />
502
Secure Coding Guide Documentation Release 2019-12-01
Follow the rule below when copying sensitive information from your application to other applications.
1. Disabling Copy/Cut Character Strings that Are Displayed in View (Required)
6.1.2.1 Disabling Copy/Cut Character Strings that Are Displayed in View (Required)
If there’s a View which displays sensitive information in an application and besides the information
is allowed to be copied/cut like EditText in the View, the information may be leaked via Clipboard.
Therefore, copy/cut must be disabled in View where sensitive information is displayed.
There are two methods to disable copy/cut. One method is to delete items of copy/cut from menu of
character string selection, and another method is to disable Long Click View.
Please refer to “6.1.3.1. Precautions When Applying Rules”.
503
Secure Coding Guide Documentation Release 2019-12-01
The root cause of the information leakage risk is that the specifications of Clipboard and ClipboardMan-
ager in Android OS leave the security risk out of consideration. Application developers need to create
higher quality applications in terms of user integrity, usability, functions, and so forth.
As mentioned in “6.1. Risk of Information Leakage from Clipboard”, an application can manipulate infor-
mation stored in Clipboard by using ClipboardManager. In addition, there is no need to set particular
Permission for using ClipboardManager and thus the application can use ClipboardManager without
being recognized by user.
Information, called ClipData, stored in Clipboard can be obtained with ClipboardManager.getPrima-
ryClip() method. If a listener is registered to ClipboardManager by ClipboardManager.addPrima-
ryClipChangedListener() method implementing OnPrimaryClipChangedListener, the listener is called
every time copy/cut operations occurred by user. Therefore ClipData can be got without overlooking
the timing. Listener call is executed when copy/cut operations occur in any application regardless.
The following shows the source code of Service, which gets ClipData whenever copy/cut is executed in
a device and displays it through Toast. You can realize that information stored in Clipboard is leaked
out doe to simple codes as follows. It’s necessary to pay attention that the sensitive information is not
taken at least by the following source code.
ClipboardListeningService.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.clipboard;
import android.app.Service;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ClipboardManager.OnPrimaryClipChangedListener;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
(continues on next page)
504
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onDestroy() {
super.onDestroy();
if (mClipboardManager != null) {
mClipboardManager.removePrimaryClipChangedListener(clipListener);
}
}
Next, below shows an example code of Activity which uses ClipboardListeningService touched in the
above.
ClipboardListeningActivity.java
/*
* Copyright (C) 2012-2019 Japan Smartphone Security Association
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jssec.android.clipboard;
import android.app.Activity;
(continues on next page)
505
Secure Coding Guide Documentation Release 2019-12-01
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clipboard_listening);
}
Thus far we have introduced methods for obtaining data stored on the Clipboard. It is also possible to
use the ClipboardManager.setPrimaryClip() method to store new data on the Clipboard.
Note that setPrimaryClip() method will overwrite the information stored in Clipboard, therefore the
information stored by user’s copy/cut may be lost. When providing custom copy/cut functions with
these methods, it’s necessary to design/implement in order not that the contents stored in Clipboard
are changed to unexpected contents, by displaying a dialogue to notify the contents are to be changed,
according the necessity.
506
Secure Coding Guide Documentation Release 2019-12-01
Revision history
507
Secure Coding Guide Documentation Release 2019-12-01
508
Secure Coding Guide Documentation Release 2019-12-01
Published by
Japan Smartphone Security Association Secure Coding Working Group, Smartphone Technology Com-
mittee
509
Secure Coding Guide Documentation Release 2019-12-01
Leader
Akira Ando
Sony Digital Network Applications, Inc.
Member
510
Secure Coding Guide Documentation Release 2019-12-01
Leader
Akira Ando
Sony Digital Network Applications, Inc.
Member
511
Secure Coding Guide Documentation Release 2019-12-01
Leader
Ken Okuyama
Sony Digital Network Applications, Inc.
Member
512
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
513
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
514
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
515
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
516
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
517
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
518
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
519
Secure Coding Guide Documentation Release 2019-12-01
Leader
Masaru Matsunami
Sony Digital Network Applications, Inc.
Member
520