In this comprehensive guide, we delve into the world of Android security from an offensive perspective, shedding light on the various techniques and methodologies used by attackers to compromise Android devices and infiltrate their sensitive data. From exploiting common coding flaws to leveraging sophisticated social engineering tactics, we explore the full spectrum of attack surfaces present in Android environments.
在这本综合指南中,我们从进攻的角度深入探讨了 Android 安全的世界,阐明了攻击者用来破坏 Android 设备并渗透其敏感数据的各种技术和方法。从利用常见的编码缺陷到利用复杂的社会工程策略,我们探索了 Android 环境中存在的各种攻击面。
ContentProvider Management in Android Applications
Android 应用程序中的 ContentProvider 管理
The ContentProvider
class in Android facilitates data sharing among applications. Proper access control is crucial to prevent unauthorized access to sensitive data. There are three primary ways to control access:
Android 中的 ContentProvider
类有助于应用程序之间的数据共享。适当的访问控制对于防止未经授权访问敏感数据至关重要。控制访问有三种主要方法:
-
Public Access: 公共访问:
-
Making a
ContentProvider
public allows other applications to access its data.
公开允许ContentProvider
其他应用程序访问其数据。 -
Use the
android:exported
attribute in theAndroidManifest.xml
file to specify whether aContentProvider
is public.
使用文件中的android:exported
AndroidManifest.xml
属性指定 aContentProvider
是否为公共。 -
Before API Level 16, a
ContentProvider
is public by default unlessandroid:exported="false"
is explicitly set.
在 API 级别 16 之前,除非显式设置,否则android:exported="false"
aContentProvider
默认为公共。 -
Example code in
AndroidManifest.xml
:
示例代码:AndroidManifest.xml
-
<provider
android:exported="true"
android:name="MyContentProvider"
android:authorities="com.example.mycontentprovider" />
Private Access: 私人访问:
-
A
ContentProvider
can be made private to restrict access from other applications.
可以将 AContentProvider
设为私有,以限制来自其他应用程序的访问。 -
From API Level 17 onwards, a
ContentProvider
is private by default ifandroid:exported
is not specified.
从 API 级别 17 开始,如果android:exported
未指定,则默认情况下 aContentProvider
为私有。 -
Example code in
AndroidManifest.xml
:
示例代码:AndroidManifest.xml
<provider
android:exported="false"
android:name="MyContentProvider"
android:authorities="com.example.mycontentprovider" />
-
Restricted Access: 限制访问:
- More details are needed for implementing restricted access. This section requires further elaboration.
实现受限访问需要更多详细信息。这一部分需要进一步阐述。
- More details are needed for implementing restricted access. This section requires further elaboration.
Noncompliant Code Example:
不合规代码示例:
The example of noncompliant code demonstrates a Twitter client application inadvertently exposing sensitive information through a public ContentProvider
.
不合规代码的示例演示了 Twitter 客户端应用程序无意中通过公共 ContentProvider
.
Proof of Concept: 概念验证:
The provided code snippet illustrates how the vulnerability in the ContentProvider
can be exploited to extract sensitive data from the Twitter client application.
提供的代码片段说明了如何利用 中的 ContentProvider
漏洞从 Twitter 客户端应用程序中提取敏感数据。
Compliant Solution: 合规解决方案:
The compliant solution involves making the ContentProvider
private in the AndroidManifest.xml
file to prevent unauthorized access to sensitive data.
合规的解决方案涉及在 AndroidManifest.xml
文件中设 ContentProvider
为私有,以防止未经授权访问敏感数据。
<provider
android:name=".content.AccountProvider"
android:exported="false"
android:authorities="jp.co.vulnerable.accountprovider" />
Risk Assessment: 风险评估:
Declaring a ContentProvider
as public without proper access control can lead to leakage of sensitive information to malicious applications.
在没有适当访问控制的情况下将 声明 ContentProvider
为公共信息可能会导致敏感信息泄露给恶意应用程序。
Protecting Exported Services with Strong Permissions in Android Applications
在 Android 应用程序中使用强权限保护导出的服务
Background: 背景:
The guideline is derived from the work of Chin et al. [Chin 2011], highlighting the risk associated with exported services that lack proper protection. Unprotected services can be exploited by any application, potentially leading to data leaks or unauthorized activities.
该指南源自 Chin 等人的工作 [Chin 2011],强调了与缺乏适当保护的出口服务相关的风险。任何应用程序都可能利用未受保护的服务,从而可能导致数据泄露或未经授权的活动。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code example demonstrates an exported service without adequate protection, allowing arbitrary applications to access sensitive information:
不合规的代码示例演示了没有足够保护的导出服务,允许任意应用程序访问敏感信息:
<activity android:exported="false" ... >
<intent-filter > ... </intent-filter>
...
</activity>
Compliant Solutions: 合规解决方案:
-
Removing
<intent-filter>
: 删除<intent-filter>
:- By removing the
<intent-filter>
, access to the service is restricted to components within the same application or applications with the same user ID.
通过删除<intent-filter>
,对服务的访问仅限于同一应用程序中的组件或具有相同用户标识的应用程序。
- By removing the
-
Using Custom Permissions:
使用自定义权限:- If the intention is to allow access from other applications, custom permissions should be used instead of relying on default permissions like “normal.” This prevents unauthorized access to confidential data.
如果目的是允许来自其他应用程序的访问,则应使用自定义权限,而不是依赖默认权限(如“正常”)。这样可以防止未经授权访问机密数据。
- If the intention is to allow access from other applications, custom permissions should be used instead of relying on default permissions like “normal.” This prevents unauthorized access to confidential data.
Compliant Solution Code: 合规解决方案代码:
<permission android:name="customPermission" android:protectionLevel="dangerous" ...></permission>
<activity
android:permission="customPermission"
... >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter >
<action android:name="package_name.MyAction" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Usage in Other Applications:
在其他应用中的用途:
<uses-permission
android:name="customPermission"
android:maxSdkVersion=.. />
Intent in = new Intent();
in.setAction("package_name.MyAction");
in.addCategory("android.intent.category.DEFAULT");
startActivity(in);
Risk Assessment: 风险评估:
Failure to protect exported services with strong permissions poses a high risk, potentially leading to sensitive data exposure or denial of service attacks.
如果无法使用强权限保护导出的服务,则会带来高风险,可能导致敏感数据泄露或拒绝服务攻击。
Protecting Against Directory Traversal Vulnerabilities in Android ContentProviders
防范 Android ContentProvider 中的目录遍历漏洞
Background: 背景:
The guideline warns about potential directory traversal vulnerabilities that can arise when using the ContentProvider.openFile()
method in Android applications. Directory traversal vulnerabilities can allow an attacker to access files outside the intended directory, leading to unauthorized access or data corruption.
该指南警告在 Android 应用程序中使用该 ContentProvider.openFile()
方法时可能出现的潜在目录遍历漏洞。目录遍历漏洞可能允许攻击者访问预期目录之外的文件,从而导致未经授权的访问或数据损坏。
Noncompliant Code Example 1:
不符合的代码示例 1:
The first noncompliant code example attempts to access a file using Uri.getLastPathSegment()
, which may be vulnerable to directory traversal attacks when the path is URL encoded:
第一个不合规的代码示例尝试使用 Uri.getLastPathSegment()
访问文件,当路径进行 URL 编码时,该文件可能容易受到目录遍历攻击:
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath();
public ParcelFileDescriptor openFile(Uri paramUri, String paramString)
throws FileNotFoundException {
File file = new File(IMAGE_DIRECTORY, paramUri.getLastPathSegment());
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
Noncompliant Code Example 2:
不符合的代码示例 2:
The second noncompliant code example tries to fix the vulnerability by decoding the URI string, but it still remains vulnerable to double encoding attacks:
第二个不合规的代码示例尝试通过解码 URI 字符串来修复漏洞,但它仍然容易受到双重编码攻击:
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath();
public ParcelFileDescriptor openFile(Uri paramUri, String paramString)
throws FileNotFoundException {
File file = new File(IMAGE_DIRECTORY, Uri.parse(paramUri.getLastPathSegment()).getLastPathSegment());
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
Proof of Concept: 概念验证:
Malicious code can exploit these vulnerabilities by supplying specially crafted URI strings to the content provider, leading to unauthorized file access or traversal.
恶意代码可以通过向内容提供商提供特制的 URI 字符串来利用这些漏洞,从而导致未经授权的文件访问或遍历。
Compliant Solution: 合规解决方案:
The compliant solution ensures protection against directory traversal by decoding the URI string and canonicalizing the file path:
合规的解决方案通过解码 URI 字符串并规范化文件路径来确保防止目录遍历:
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath();
public ParcelFileDescriptor openFile(Uri paramUri, String paramString)
throws FileNotFoundException {
String decodedUriString = Uri.decode(paramUri.toString());
File file = new File(IMAGE_DIRECTORY, Uri.parse(decodedUriString).getLastPathSegment());
if (!file.getCanonicalPath().startsWith(localFile.getCanonicalPath())) {
throw new IllegalArgumentException("Path traversal attempt detected!");
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
Applicability: 适用性:
This guideline is applicable to any Android application that exchanges files through a ContentProvider, ensuring protection against directory traversal attacks.
此准则适用于通过 ContentProvider 交换文件的任何 Android 应用程序,确保防止目录遍历攻击。
Risk Assessment: 风险评估:
Failure to properly decode and canonicalize file paths received by a ContentProvider may result in directory traversal vulnerabilities, leading to unauthorized access or corruption of sensitive data.
未能正确解码和规范化 ContentProvider 接收的文件路径可能会导致目录遍历漏洞,从而导致未经授权的访问或损坏敏感数据。
Preventing Unauthorized Access to Sensitive Activities in Android Applications
防止未经授权访问 Android 应用程序中的敏感活动
Background: 背景:
Declaring an intent filter for an activity in the AndroidManifest.xml file exposes the activity to other applications, potentially allowing unauthorized access. Malicious apps could exploit this vulnerability to misuse sensitive functionalities of the exposed activity.
在 AndroidManifest.xml 文件中为某个活动声明 intent 过滤器会将该活动暴露给其他应用程序,从而可能允许未经授权的访问。恶意应用可利用此漏洞滥用公开活动的敏感功能。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code example shows an AndroidManifest.xml file exporting an activity without restricting access:
不符合的代码示例显示了一个 AndroidManifest.xml 文件,该文件在不限制访问的情况下导出活动:
<activity
android:configChanges="keyboard|keyboardHidden|orientation"
android:name=".media.yfrog.YfrogUploadDialog"
android:theme="@style/Vulnerable.Dialog"
android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter android:icon="@drawable/yfrog_icon" android:label="@string/YFROG">
<action android:name="jp.co.vulnerable.ACTION_UPLOAD" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
</activity>
Compliant Solution (Do not export activity):
合规解决方案(不导出活动):
In the compliant solution, the activity is not exported, restricting it to only accept intents from within the same app or apps with the same user ID:
在合规解决方案中,不会导出活动,从而限制其仅接受来自同一应用或具有相同用户 ID 的应用中的意向:
<activity
android:configChanges="keyboard|keyboardHidden|orientation"
android:name=".media.yfrog.YfrogUploadDialog"
android:theme="@style/ VulnerableTheme.Dialog"
android:windowSoftInputMode="stateAlwaysHidden"
android:exported="false">
</activity>
Compliant Solution (Twicca):
合规解决方案 (Twicca):
In Twicca’s case, instead of declaring the activity as not exported, they implemented caller validation within the activity itself. This ensures that the activity only proceeds if called from the same package:
在 Twicca 的案例中,他们没有将活动声明为未导出,而是在活动本身中实现了调用方验证。这可确保仅当从同一包调用时,活动才会继续:
public void onCreate(Bundle arg5) {
super.onCreate(arg5);
...
ComponentName v0 = this.getCallingActivity();
if(v0 == null) {
this.finish();
} else if(!jp.r246.twicca.equals(v0.getPackageName())) {
this.finish();
} else {
this.a = this.getIntent().getData();
if(this.a == null) {
this.finish();
}
...
}
}
Risk Assessment: 风险评估:
Failure to validate the caller’s identity before acting on received intents may lead to sensitive data exposure or denial of service attacks.
在根据收到的意图采取行动之前未能验证调用方的身份可能会导致敏感数据泄露或拒绝服务攻击。
Avoid Storing Sensitive Information on External Storage (SD Card) Without Encryption
避免将敏感信息存储在未加密的外部存储(SD卡)上
Background: 背景:
Android provides external storage options like SD cards for storing application data. However, files stored on external storage are not guaranteed to be secure and can be accessed by other apps or users. Storing sensitive information without encryption on external storage poses significant security risks.
Android 提供外部存储选项,例如用于存储应用数据的 SD 卡。但是,存储在外部存储上的文件不能保证是安全的,并且可以由其他应用程序或用户访问。在不加密的情况下将敏感信息存储在外部存储上会带来重大的安全风险。
Noncompliant Code Example:
不合规代码示例:
The following code snippet demonstrates storing sensitive information directly to external storage without encryption:
以下代码片段演示了如何在不加密的情况下将敏感信息直接存储到外部存储:
private String filename = "myfile";
private String string = "sensitive data such as credit card number";
FileOutputStream fos = null;
try {
File file = new File(getExternalFilesDir(TARGET_TYPE), filename);
fos = new FileOutputStream(file, false);
fos.write(string.getBytes());
} catch (FileNotFoundException e) {
// handle FileNotFoundException
} catch (IOException e) {
// handle IOException
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// handle error
}
}
}
Compliant Solution #1 (Save a File on Internal Storage):
The compliant solution stores sensitive information in the internal storage directory with permission set to MODE_PRIVATE
, ensuring that other apps cannot access the file:
private String filename = "myfile";
private String string = "sensitive data such as credit card number";
FileOutputStream fos = null;
try {
fos = openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(string.getBytes());
} catch (FileNotFoundException e) {
// handle FileNotFoundException
} catch (IOException e) {
// handle IOException
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// handle error
}
}
}
Risk Assessment: 风险评估:
Storing sensitive information on external storage without encryption can lead to data leakage to malicious apps, compromising the confidentiality of the data. Implementing proper encryption or storing data in internal storage mitigates this risk.
在未加密的情况下将敏感信息存储在外部存储上可能会导致数据泄露给恶意应用程序,从而损害数据的机密性。实施适当的加密或将数据存储在内部存储中可以降低这种风险。
Logging Sensitive Information in Android
在 Android 中记录敏感信息
Background: 背景:
Android provides built-in logging mechanisms through the android.util.Log
class. Developers use various logging levels (Log.d
, Log.e
, Log.i
, Log.v
, Log.w
) to output debugging information, errors, and other messages to the system log. However, logging sensitive information, such as access tokens or user location data, can pose security risks.
Android 通过 android.util.Log
该类提供内置的日志记录机制。开发人员使用各种日志记录级别( Log.d
、 、 Log.e
Log.i
、 Log.v
、 ) Log.w
将调试信息、错误和其他消息输出到系统日志。但是,记录敏感信息(如访问令牌或用户位置数据)可能会带来安全风险。
Logging Sensitive Information:
记录敏感信息:
Developers should avoid logging sensitive information using the android.util.Log
class. Instead, they should be cautious and ensure that only non-sensitive data is logged for debugging purposes.
开发人员应避免使用 android.util.Log
该类记录敏感信息。相反,他们应该谨慎行事,并确保仅记录非敏感数据以进行调试。
Noncompliant Code Example:
不合规代码示例:
// Logging sensitive information (access token) using Log.d
Log.d("Facebook-authorize", "Login Success! access_token="
+ getAccessToken() + " expires="
+ getAccessExpires());
Proof of Concept (Obtaining Log Output):
概念验证(获取日志输出):
// Example code to obtain log output from a vulnerable application
final StringBuilder slog = new StringBuilder();
try {
Process mLogcatProc;
mLogcatProc = Runtime.getRuntime().exec(new String[]
{"logcat", "-d", "LoginAsyncTask:I APIClient:I method:V *:S" });
BufferedReader reader = new BufferedReader(new InputStreamReader(
mLogcatProc.getInputStream()));
String line;
String separator = System.getProperty("line.separator");
while ((line = reader.readLine()) != null) {
slog.append(line);
slog.append(separator);
}
Toast.makeText(this, "Obtained log information", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// handle error
}
TextView tView = (TextView) findViewById(R.id.logView);
tView.setText(slog);
Compliant Solution:
Developers should ensure that sensitive information is not logged. They can achieve this by:
开发人员应确保不记录敏感信息。他们可以通过以下方式实现这一目标:
-
Reviewing code to identify and remove any logging of sensitive information.
查看代码以识别和删除任何敏感信息的日志记录。 -
Using custom logging mechanisms that automatically turn off logging in release builds.
使用自定义日志记录机制,在发布版本中自动关闭日志记录。 -
Employing obfuscation tools like ProGuard to remove specific logging calls.
使用 ProGuard 等混淆工具删除特定的日志记录调用。
Risk Assessment: 风险评估:
Logging sensitive information can lead to data leakage, compromising user privacy and potentially exposing sensitive data to malicious apps.
记录敏感信息可能会导致数据泄露,损害用户隐私,并可能将敏感数据暴露给恶意应用。
Securing Sensitive Data in Android
保护 Android 中的敏感数据
Background: 背景:
Android apps often handle sensitive data, such as user credentials or personal information. It’s crucial to protect this data from unauthorized access by other apps or entities. Data security measures include using appropriate file modes when creating files or databases and employing encryption for added protection.
Android 应用通常会处理敏感数据,例如用户凭据或个人信息。保护此数据免遭其他应用或实体未经授权的访问至关重要。数据安全措施包括在创建文件或数据库时使用适当的文件模式,以及采用加密以增加保护。
Noncompliant Code Example:
不合规代码示例:
// Creating a file that is world-readable (noncompliant)
openFileOutput("someFile", MODE_WORLD_READABLE);
In this example, the file is created with the MODE_WORLD_READABLE
flag, allowing any application to read its contents, which poses a security risk.
在此示例中,文件是使用 MODE_WORLD_READABLE
标志创建的,允许任何应用程序读取其内容,这会带来安全风险。
Compliant Solution: 合规解决方案:
// Creating a file with MODE_PRIVATE (compliant)
openFileOutput("someFile", MODE_PRIVATE);
By using MODE_PRIVATE
, the file can only be accessed by the app that created it, ensuring data security.
通过使用 MODE_PRIVATE
,该文件只能由创建它的应用程序访问,从而确保数据安全。
Risk Assessment: 风险评估:
Failure to secure sensitive data can lead to data leakage, compromising user privacy and potentially exposing confidential information to unauthorized apps or entities.
未能保护敏感数据可能会导致数据泄露,损害用户隐私,并可能将机密信息暴露给未经授权的应用或实体。
Cache
Caching data can pose security risks as it may become accessible to other applications or unauthorized users if the device is lost or stolen. Here’s a breakdown of the key points mentioned in the guideline:
-
Caching web application data: Storing data such as URL histories, HTTP headers, HTML form inputs, and cookies in the cache can expose sensitive information to other applications.
-
Keyboard cache: Words entered by the user via the keyboard are stored in the Android user dictionary for auto-correction. This data is accessible to any app without requiring permission, potentially leading to the leakage of sensitive information.
-
Cached camera images: Apps may cache images captured by the device’s camera, which can remain accessible even after the app has finished. Storing such images without proper security measures can compromise user privacy.
-
GUI objects caching: Application screens retained in memory can enable access to transaction histories by anyone with device access. Caching sensitive information in memory increases the risk of data exposure.
To address these concerns, developers should:
-
Avoid caching sensitive information whenever possible.
-
Use methods like
clearCache()
to delete cached data, especially when accessing sensitive data with aWebView
.
使用删除缓存数据等clearCache()
方法,尤其是在使用WebView
. -
Utilize server-side headers like
no-cache
to prevent caching of particular content by the application.
利用服务器端标头no-cache
等来防止应用程序缓存特定内容。
Noncompliant Code Example:
不合规代码示例:
// Caching web application data (noncompliant)
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
In this noncompliant code example, web application data is cached using the default cache mode, potentially revealing sensitive information such as URL histories, HTTP headers, and cookies.
在此不合规的代码示例中,Web 应用程序数据使用默认缓存模式进行缓存,可能会泄露敏感信息,例如 URL 历史记录、HTTP 标头和 Cookie。
Compliant Solution: 合规解决方案:
// Avoid caching web application data (compliant)
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
By setting the cache mode to LOAD_NO_CACHE
, caching of web application data is disabled, reducing the risk of sensitive information leakage.
通过将缓存模式设置为 LOAD_NO_CACHE
,将禁用 Web 应用程序数据的缓存,从而降低敏感信息泄露的风险。
Risk Assessment: 风险评估:
Failing to prevent caching of sensitive data can lead to unauthorized access to user information, compromising privacy and security. It’s important to implement measures to avoid caching where sensitive data is involved.
如果不能防止敏感数据的缓存,可能会导致未经授权访问用户信息,从而危及隐私和安全。请务必采取措施避免在涉及敏感数据的地方进行缓存。
Do not use world readable or writeable to share files between apps
不要使用全局可读或可写在应用之间共享文件
This guideline warns against the use of constants MODE_WORLD_READABLE and MODE_WORLD_WRITABLE in Android applications, as they can create security vulnerabilities. Instead, developers are encouraged to utilize more formal mechanisms for interactions and communication between applications, such as ContentProvider, BroadcastReceiver, and Service. Here’s an explanation of each mechanism:
本指南警告不要在 Android 应用程序中使用常量MODE_WORLD_READABLE和MODE_WORLD_WRITABLE,因为它们可能会造成安全漏洞。相反,鼓励开发人员使用更正式的机制在应用程序之间进行交互和通信,例如 ContentProvider、BroadcastReceiver 和 Service。以下是对每种机制的说明:
-
ContentProvider: This component provides content to applications and is used to share data among multiple applications. It offers a structured and secure way to expose data to other applications while maintaining control over access permissions.
ContentProvider:此组件为应用程序提供内容,用于在多个应用程序之间共享数据。它提供了一种结构化且安全的方式,可以向其他应用程序公开数据,同时保持对访问权限的控制。 -
BroadcastReceiver: Broadcasts are sent when an event of interest occurs, and applications can register to receive certain broadcasts. BroadcastReceiver serves as a messaging system among applications, allowing them to communicate and respond to system-wide events.
-
Service: Services allow applications to perform background tasks and expose some of their functionality to other applications. They enable long-running operations to be executed independently of the application’s UI.
The noncompliant code examples demonstrate the use of MODE_WORLD_READABLE and MODE_WORLD_WRITABLE constants to share files between applications, which is discouraged due to its security implications. These constants allow other applications to read or write the file using the shared preference API, potentially leading to security holes.
Noncompliant Code Example 1:
String FILENAME = "example_file";
String string = "hello world!";
FileOutputStream outputStream = openFileOutput(FILENAME, Context.MODE_WORLD_READABLE);
outputStream.write(string.getBytes());
outputStream.close();
Noncompliant Code Example 2:
String FILENAME = "example_file";
String string = "hello world!";
FileOutputStream outputStream = openFileOutput(FILENAME, Context.MODE_WORLD_WRITABLE);
outputStream.write(string.getBytes());
outputStream.close();
By avoiding the use of these constants and opting for more secure mechanisms like ContentProvider, BroadcastReceiver, and Service, developers can minimize the risk of security vulnerabilities in their Android applications.
For OAuth, use an explicit intent method to deliver access tokens
This guideline emphasizes the importance of using explicit intents over implicit intents in Android applications to protect user information and prevent potential security risks. Explicit intents specify the target component explicitly, while implicit intents declare general actions that all applications can use, potentially exposing sensitive user actions.
Noncompliant Code Example:
protected void OnTokenAcquired(Bundle savedInstanceState) {
//[Code to construct an OAuth client request goes here]
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(request.getLocationUri() + "&response_type=code"));
startActivity(intent);
}
In the noncompliant code example, an implicit intent is used to send access tokens by invoking a specific action without specifying the target component explicitly. This approach can lead to security vulnerabilities and expose user information to unintended recipients.
Compliant Solution:
protected void OnTokenAcquired(Bundle savedInstanceState) {
//[Code to construct an OAuth client request goes here]
Intent intent = new Intent(this, YourOAuthActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(request.getLocationUri() + "&response_type=code"));
startActivity(intent);
}
In the compliant solution, an explicit intent is used to send access tokens by specifying the target component (YourOAuthActivity.class) explicitly. This approach ensures that the intent is directed only to the intended recipient, enhancing security and protecting user information.
Do not broadcast sensitive information using an implicit intent
不要使用隐式意图广播敏感信息
This guideline focuses on securing broadcast intents in Android applications to prevent sensitive information leakage or denial of service attacks. It emphasizes the importance of using explicit intents or other secure mechanisms to restrict the receivers of broadcast intents.
本指南侧重于保护 Android 应用中的广播意图,以防止敏感信息泄露或拒绝服务攻击。它强调了使用显式意图或其他安全机制来限制广播意图的接收者的重要性。
Noncompliant Code Example:
不合规代码示例:
public class ServerService extends Service {
// ...
private void d() {
// ...
Intent v1 = new Intent();
v1.setAction("com.sample.action.server_running");
v1.putExtra("local_ip", v0.h);
v1.putExtra("port", v0.i);
v1.putExtra("code", v0.g);
v1.putExtra("connected", v0.s);
v1.putExtra("pwd_predefined", v0.r);
if (!TextUtils.isEmpty(v0.t)) {
v1.putExtra("connected_usr", v0.t);
}
this.sendBroadcast(v1);
}
}
In this noncompliant code example, an implicit intent is used to broadcast sensitive information such as the device’s IP address, port number, and password. This approach allows any application, including malicious ones, to receive the broadcast message, potentially leading to data leakage or denial of service attacks.
在此不合规的代码示例中,隐式 intent 用于广播敏感信息,例如设备的 IP 地址、端口号和密码。这种方法允许任何应用程序(包括恶意应用程序)接收广播消息,从而可能导致数据泄露或拒绝服务攻击。
Proof of Concept: 概念验证:
public class BcReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.sample.action.server_running")) {
String pwd = intent.getStringExtra("connected");
String message = "Received sensitive data: [" + pwd + "]";
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
}
The proof of concept demonstrates how a malicious broadcast receiver can intercept the implicit intent and access sensitive data sent by the vulnerable application.
Compliant Solution:
Intent intent = new Intent("my-sensitive-event");
intent.putExtra("event", "this is a test event");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
In the compliant solution, LocalBroadcastManager is used to send the broadcast intent. This ensures that the intent is only broadcast and received within the same application, preventing other apps from accessing sensitive information.
Risk Assessment: Using implicit intents for broadcasting sensitive information can expose the data to malicious apps or lead to denial of service attacks. Employing explicit intents, LocalBroadcastManager, or other secure mechanisms can mitigate these risks and enhance the security of the application.
Do not allow WebView to access sensitive local resource through file scheme
This guideline focuses on security concerns and best practices when using the WebView class in Android applications. It provides insights into several methods within WebView that can introduce security vulnerabilities if not used correctly.
WebView Security Concerns:
-
setJavaScriptEnabled():
-
Enables JavaScript execution within the WebView.
-
Default: false.
-
Risk: Allows JavaScript code execution, potentially leading to security breaches.
-
-
setPluginState():
-
Enables or disables plugins in the WebView.
-
Options: ON (always load), ON_DEMAND (load if plugin exists), OFF (disable all).
-
Default: OFF.
-
Risk: Malicious content may exploit enabled plugins, compromising security.
-
-
setAllowFileAccess():
-
Enables or disables file access within the WebView.
-
Default: true.
-
Risk: Allows or restricts access to local files, impacting security based on application requirements.
-
-
setAllowContentAccess():
-
Enables or disables content URL access within the WebView.
-
Default: true.
-
Risk: Controls access to content providers, affecting security based on application needs.
-
-
setAllowFileAccessFromFileURLs():
-
Controls JavaScript access to content from file scheme URLs.
-
Default: true or false based on API level.
-
Risk: Determines if JavaScript running in file scheme URLs can access other file scheme URLs, affecting security based on API level.
-
-
setAllowUniversalAccessFromFileURLs():
-
Controls JavaScript access to content from any origin from file scheme URLs.
-
Default: true or false based on API level.
-
Risk: Affects JavaScript access to content from file scheme URLs, impacting security based on API level.
-
Noncompliant Code Example:
The provided code demonstrates an activity with a WebView component that enables JavaScript and processes any URI passed through an Intent without validation. This setup can lead to potential security vulnerabilities, especially if malicious content is loaded.
// Noncompliant Code Example
public class MyBrowser extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView webView = (WebView) findViewById(R.id.webview);
// turn on javascript
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
String url = getIntent().getStringExtra("URL");
webView.loadUrl(url);
}
}
// Proof of Concept
// Malicious application prepares some crafted HTML file,
// places it on a local storage, makes accessible from
// other applications. The following code sends an
// intent to a target application (jp.vulnerable.android.app)
// to make it access and process the malicious HTML file.
String pkg = "jp.vulnerable.android.app";
String cls = pkg + ".DummyLauncherActivity";
String uri = "file:///[crafted HTML file]";
Intent intent = new Intent();
intent.setClassName(pkg, cls);
intent.putExtra("url", uri);
this.startActivity(intent);
// Compliant Solution
public class MyBrowser extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView webView = (WebView) findViewById(R.id.webview);
String url = getIntent().getStringExtra("url");
if (!url.startsWith("http")) { /* Note: "https".startsWith("http") == true */
url = "about:blank";
}
webView.loadUrl(url);
}
}
The noncompliant code initializes a WebView object, enables JavaScript, and loads a URL received via Intent without proper validation. This can lead to security vulnerabilities if malicious content is loaded.
The proof of concept demonstrates how a malicious application can exploit this vulnerability by sending an intent to a target application with a crafted HTML file URI.
The compliant solution validates the received URL to ensure it starts with “http” or “https” before loading it into the WebView. This helps mitigate the risk of loading potentially malicious local content.
Compliant Solution:
A compliant solution validates URIs received via Intent before rendering them with WebView. It checks that the URI starts with “http” to ensure that only trusted URLs are loaded. This approach helps mitigate the risk of loading malicious local content.
Risk Assessment:
Failure to implement proper security measures in WebView usage can result in information leaks and other security breaches. It’s essential to validate input and configure WebView settings carefully to minimize these risks.
Do not provide addJavascriptInterface method access in a WebView which could contain untrusted content. (API level JELLY_BEAN or below)
coding guideline regarding the usage of the addJavascriptInterface
method in Android applications, specifically targeting API levels JELLY_BEAN and below. This method, when used with untrusted content in a WebView, can expose the application to scripting attacks, potentially compromising sensitive data and app control.
Noncompliant Code Example:
The noncompliant code demonstrates an insecure usage of the addJavascriptInterface
method:
WebView webView = new WebView(this);
setContentView(webView);
class JsObject {
private String sensitiveInformation;
public String toString() {
return sensitiveInformation;
}
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("http://www.example.com");
By allowing JavaScript to control the host, this code opens up avenues for potential scripting attacks, utilizing Java reflection to access public methods of injected objects and thereby gaining access to sensitive information.
Compliant Solutions:
- Refrain from using
addJavascriptInterface
: In this approach, the code refrains from using theaddJavascriptInterface
method altogether.
WebView webView = new WebView(this);
setContentView(webView);
Specify minimum SDK version in manifest: Another compliant solution involves specifying in the app’s manifest that it’s only intended for API levels JELLY_BEAN_MR1 and above, where only public methods annotated with JavascriptInterface
can be accessed from JavaScript.
<manifest>
<uses-sdk android:minSdkVersion="17" />
...
</manifest>
Applicability:
- Android Version Applicability: Applies to Android API versions 16 (JELLY_BEAN) and below.
Risk Assessment:
-
Risk Level: High
-
Probability: Probable
-
Impact: Medium
-
Priority: P12
-
Likelihood: L1
Enable serialization compatibility during class evolution
coding guideline regarding the serialization of classes in Java, emphasizing the importance of maintaining compatibility across different versions of a class.
关于 Java 中类序列化的编码指南,强调在类的不同版本之间保持兼容性的重要性。
Noncompliant Code Example:
不合规代码示例:
The provided noncompliant code demonstrates a class GameWeapon
that implements Serializable
without specifying a serialVersionUID
, thus relying on the default serialized form. Any changes to the internal structure of the class may break compatibility with previously serialized objects.
提供的不合规代码演示了一个 Serializable
类,该类 GameWeapon
在不指定 serialVersionUID
的情况下实现,因此依赖于默认的序列化形式。对类的内部结构所做的任何更改都可能破坏与以前序列化对象的兼容性。
class GameWeapon implements Serializable {
int numOfWeapons = 10;
public String toString() {
return String.valueOf(numOfWeapons);
}
}
Compliant Solutions: 合规解决方案:
- Using
serialVersionUID
: In this solution, the class declares aprivate static final long serialVersionUID
, providing a unique identifier for the class version. This allows the JVM to deserialize objects even if the class definition has changed, as long as the version ID remains the same.
使用serialVersionUID
:在此解决方案中,类声明一个private static final long serialVersionUID
,为类版本提供唯一标识符。这允许 JVM 反序列化对象,即使类定义已更改,只要版本 ID 保持不变。
class GameWeapon implements Serializable {
private static final long serialVersionUID = 24L;
int numOfWeapons = 10;
public String toString() {
return String.valueOf(numOfWeapons);
}
}
Using serialPersistentFields
: Another compliant approach involves using custom serialization with serialPersistentFields
. This allows more flexibility in the serialization process by specifying which fields should be serialized. It also separates the serialized fields from the class implementation, making it easier to evolve the class without breaking compatibility.
使用 serialPersistentFields
:另一种兼容方法涉及使用 serialPersistentFields
自定义序列化。这样,通过指定应序列化的字段,可以在序列化过程中具有更大的灵活性。它还将序列化字段与类实现分开,从而在不破坏兼容性的情况下更轻松地发展类。
class WeaponStore implements Serializable {
int numOfWeapons = 10; // Total number of weapons
}
public class GameWeapon implements Serializable {
WeaponStore ws = new WeaponStore();
private static final ObjectStreamField[] serialPersistentFields
= {new ObjectStreamField("ws", WeaponStore.class)};
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
this.ws = (WeaponStore) gf.get("ws", ws);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
ObjectOutputStream.PutField pf = oos.putFields();
pf.put("ws", ws);
oos.writeFields();
}
public String toString() {
return String.valueOf(ws);
}
}
Risk Assessment:
-
Risk Level: Low
-
Probability: Probable
-
Impact: High
-
Priority: P2
-
Likelihood: L3
Do not deviate from the proper signatures of serialization methods
coding guidelines for implementing special methods required for object serialization and deserialization in Java, emphasizing the importance of adhering to specific method signatures and access specifiers to ensure proper functionality and security.
Serialization Methods:
writeObject()
andreadObject()
: These methods are responsible for custom serialization and deserialization of objects. They must have the following signatures:
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
Compliant Solution for writeObject()
and readObject()
:
Ensure the methods are private and non-static to limit accessibility and adhere to required signatures.
private void writeObject(final ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
}
private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
}
readResolve()
and writeReplace()
Methods:
These methods allow classes to control the types and instances of objects being serialized and deserialized. They can be used to replace or resolve objects before they are returned or written to the stream. While it’s possible to add any access specifier to these methods, it’s recommended to avoid making them private or static to ensure proper functionality and extensibility.
Noncompliant Code Examples for readResolve()
and writeReplace()
:
// Private declaration
class Extendable implements Serializable {
private Object readResolve() {
// ...
}
private Object writeReplace() {
// ...
}
}
// Static declaration
class Extendable implements Serializable {
protected static Object readResolve() {
// ...
}
protected static Object writeReplace() {
// ...
}
}
Compliant Solution for readResolve()
and writeReplace()
:
合规解决方案 readResolve()
writeReplace()
:
To ensure proper inheritance and functionality, declare these methods as protected and non-static.
若要确保正确的继承和功能,请将这些方法声明为受保护的和非静态的。
class Extendable implements Serializable {
protected Object readResolve() {
// ...
}
protected Object writeReplace() {
// ...
}
}
Risk Assessment: 风险评估:
-
Risk Level: High 风险等级:高
-
Probability: Likely 概率:可能
-
Impact: Low 影响:低
-
Priority: P27 优先级:P27
-
Likelihood: L1 可能性:L1
Exclude unsanitized user input from format strings
从格式字符串中排除未经清理的用户输入
guidelines regarding the usage of the format()
and printf()
methods in the PrintStream
class from the java.io
package. These methods are used for formatting output, but they pose security risks if untrusted data is incorporated into the format string.
有关包中 PrintStream
类中 format()
AND printf()
方法用法的 java.io
指南。这些方法用于格式化输出,但如果将不受信任的数据合并到格式字符串中,则会带来安全风险。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code incorporates untrusted data into a format string, potentially leaking sensitive information or allowing a denial-of-service attack.
不合规代码将不受信任的数据合并到格式字符串中,可能会泄露敏感信息或允许拒绝服务攻击。
class Format {
static Calendar c = new GregorianCalendar(1995, GregorianCalendar.MAY, 23);
public static void main(String[] args) {
// args[0] should contain the credit card expiration date
// but might contain %1$tm, %1$te or %1$tY format specifiers
System.out.format(
args[0] + " did not match! HINT: It was issued on %1$terd of some month", c
);
}
}
Compliant Solution:
The compliant solution avoids incorporating untrusted user input into the format string, rendering any format specifiers inert.
class Format {
static Calendar c = new GregorianCalendar(1995, GregorianCalendar.MAY, 23);
public static void main(String[] args) {
// args[0] is the credit card expiration date
// Perform comparison with c,
// if it doesn't match, print the following line
System.out.format(
"%s did not match! HINT: It was issued on %terd of some month",
args[0], c
);
}
}
Risk Assessment:
-
Risk Level: Medium
-
Probability: Unlikely
-
Impact: Medium
-
Priority: P4
-
Likelihood: L3
Sanitize untrusted data included in a regular expression
discusses the risks associated with regular expression (regex) injection and provides compliant solutions to mitigate these risks. It emphasizes the importance of sanitizing untrusted input used in regex patterns to prevent potential security vulnerabilities.
Risks of Regex Injection:
Regex injection occurs when untrusted input is incorporated into a regex pattern, allowing attackers to modify the original pattern in a way that deviates from the program’s intended behavior. This can lead to control flow manipulation, information leaks, or denial-of-service (DoS) vulnerabilities.
Vulnerable Constructs in Regex:
-
Matching flags: Untrusted inputs may override matching options passed to the
Pattern.compile()
method. -
Greediness: Injection attempts may change the regex to match as much of the string as possible, potentially exposing sensitive information.
-
Grouping: Attackers may manipulate regex groupings by supplying untrusted input.
Noncompliant Code Example:
The provided code dynamically constructs a regex pattern using untrusted user input, making it vulnerable to regex injection attacks.
public static void FindLogEntry(String search) {
// Construct regex dynamically from user string
String regex = "(.*? +public\\[\\d+\\] +.*" + search + ".*)";
// ...
}
Compliant Solutions:
- Whitelisting: Sanitize search terms by filtering out non-alphanumeric characters.
白名单:通过过滤掉非字母数字字符来清理搜索词。
public static void FindLogEntry(String search) {
// Sanitize search string
StringBuilder sb = new StringBuilder(search.length());
for (int i = 0; i < search.length(); ++i) {
char ch = search.charAt(i);
if (Character.isLetterOrDigit(ch) || ch == ' ' || ch == '\'') {
sb.append(ch);
}
}
search = sb.toString();
// Construct regex dynamically from sanitized user string
String regex = "(.*? +public\\[\\d+\\] +.*" + search + ".*)";
// ...
}
- Using
Pattern.quote()
: Escape any potentially malicious characters in the search string.
使用Pattern.quote()
:对搜索字符串中的任何潜在恶意字符进行转义。
public static void FindLogEntry(String search) {
// Sanitize search string
search = Pattern.quote(search);
// Construct regex dynamically from sanitized user string
String regex = "(.*? +public\\[\\d+\\] +.*" + search + ".*)";
// ...
}
Risk Assessment: 风险评估:
-
Risk Level: Medium 风险等级:中等
-
Probability: Unlikely 概率:不太可能
-
Impact: Medium 影响:中等
-
Priority: P4 优先级:P4
-
Likelihood: L3 可能性:L3
Define wrappers around native methods
定义本机方法的包装器
highlights the importance of defining wrapper methods for native methods in Java, written in languages like C and C++, to ensure security and maintainability. It emphasizes the necessity of validating inputs, performing security checks, and defensive copying to prevent vulnerabilities such as buffer overflows.
强调了在 Java 中为用 C 和 C++ 等语言编写的本机方法定义包装方法的重要性,以确保安全性和可维护性。它强调了验证输入、执行安全检查和防御性复制的必要性,以防止缓冲区溢出等漏洞。
Native Method Overview:
Native methods provide extensibility to Java by allowing integration with code written in languages like C and C++. However, using native methods can compromise portability and flexibility due to deviation from Java’s policies.
Noncompliant Code Example:
The provided code exposes a native method nativeOperation()
publicly, allowing untrusted callers to invoke it directly, bypassing security checks.
public final class NativeMethod {
// Public native method
public native void nativeOperation(byte[] data, int offset, int len);
// Wrapper method that lacks security checks and input validation
public void doOperation(byte[] data, int offset, int len) {
nativeOperation(data, offset, len);
}
static {
// Load native library in static initializer of class
System.loadLibrary("NativeMethodLib");
}
}
Compliant Solution:
The compliant solution addresses security concerns by declaring the native method as private and implementing a doOperation()
wrapper method with proper security checks, input validation, and defensive copying.
public final class NativeMethodWrapper {
// Private native method
private native void nativeOperation(byte[] data, int offset, int len);
// Wrapper method performs SecurityManager and input validation checks
public void doOperation(byte[] data, int offset, int len) {
// Permission needed to invoke native method
securityManagerCheck();
if (data == null) {
throw new NullPointerException();
}
// Copy mutable input
data = data.clone();
// Validate input
if ((offset < 0) || (len < 0) || (offset > (data.length - len))) {
throw new IllegalArgumentException();
}
nativeOperation(data, offset, len);
}
static {
// Load native library in static initializer of class
System.loadLibrary("NativeMethodLib");
}
}
Exceptions:
- JN100-J-EX0: Native methods like
int rand(void)
that don’t require security manager checks, argument validation, or defensive copying do not need wrapping.
JN100-J-EX0:像这样的int rand(void)
本机方法不需要安全管理器检查、参数验证或防御性复制,也不需要包装。
Risk Assessment: 风险评估:
-
Risk Level: Medium 风险等级:中等
-
Probability: Probable 概率:可能
-
Impact: High 影响:高
-
Priority: P4 优先级:P4
-
Likelihood: L3 可能性:L3
Do not allow exceptions to expose sensitive information
不允许异常公开敏感信息
importance of filtering sensitive information when propagating exceptions in Java programs to prevent information leaks that could aid attackers in developing exploits. It discusses the risks associated with exposing exception messages and types, providing examples of problematic exceptions and noncompliant code.
在 Java 程序中传播异常时过滤敏感信息的重要性,以防止信息泄露,从而帮助攻击者开发漏洞。它讨论了与公开异常消息和类型相关的风险,提供了有问题的异常和不合规代码的示例。
Exception Name 异常名称 | Description of Information Leak or Threat 信息泄露或威胁的描述 |
java.io .FileNotFoundException |
Underlying file system structure, user name enumeration 基础文件系统结构、用户名枚举 |
java.sql.SQLException |
Database structure, user name enumeration 数据库结构、用户名枚举 |
java.net .BindException |
Enumeration of open ports when untrusted client can choose server port 当不受信任的客户端可以选择服务器端口时,枚举开放端口 |
java.util.ConcurrentModificationException |
May provide information about thread-unsafe code 可能提供有关线程不安全代码的信息 |
javax.naming.InsufficientResourcesException |
Insufficient server resources (may aid DoS) 服务器资源不足(可能有助于 DoS) |
java.util.MissingResourceException |
Resource enumeration 资源枚举 |
java.util.jar.JarException |
Underlying file system structure 底层文件系统结构 |
java.security .acl.NotOwnerException |
Owner enumeration 所有者枚举 |
java.lang.OutOfMemoryError |
DoS |
java.lang.StackOverflowError |
DoS |
Printing the stack trace can also result in unintentionally leaking information about the structure and state of the process to an attacker. When a Java program that is run within a console terminates because of an uncaught exception, the exception’s message and stack trace are displayed on the console; the stack trace may itself contain sensitive information about the program’s internal structure. Consequently, any program that may be run on a console accessible to an untrusted user must never abort due to an uncaught exception.
打印堆栈跟踪还可能导致无意中将有关进程结构和状态的信息泄露给攻击者。当在控制台中运行的 Java 程序因未捕获的异常而终止时,控制台上会显示异常的消息和堆栈跟踪;堆栈跟踪本身可能包含有关程序内部结构的敏感信息。因此,任何可能在不受信任的用户可访问的控制台上运行的程序都不得因未捕获的异常而中止。
Risks of Exception Propagation:
-
Information Leaks: Exceptions can reveal sensitive details about the application’s internal structure, system configuration, or user environment.
-
Denial of Service (DoS): Attackers can exploit exceptions to gather information for potential DoS attacks or exploit vulnerabilities.
Noncompliant Code Example 1: Leaks from Exception Message and Type
class ExceptionExample {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis =
new FileInputStream(System.getenv("APPDATA") + args[0]);
}
}
Risk:
-
Exposes sensitive information about the file system layout to attackers.
-
Allows attackers to reconstruct the underlying file system by passing fictitious path names.
Noncompliant Code Example 2: Wrapping and Rethrowing Sensitive Exception
try {
FileInputStream fis =
new FileInputStream(System.getenv("APPDATA") + args[0]);
} catch (FileNotFoundException e) {
// Log the exception
throw new IOException("Unable to retrieve file", e);
}
Risk:
- Even when the logged exception is not directly accessible to the user, the original exception can still provide information about the file system layout to attackers.
Noncompliant Code Example 3: Sanitized Exception
class SecurityIOException extends IOException {/* ... */};
try {
FileInputStream fis =
new FileInputStream(System.getenv("APPDATA") + args[0]);
} catch (FileNotFoundException e) {
// Log the exception
throw new SecurityIOException();
}
Risk:
- Although this approach is less likely to leak useful information, it still reveals that the specified file cannot be read, enabling attackers to infer sensitive details about the file system.
尽管此方法不太可能泄露有用的信息,但它仍然显示无法读取指定的文件,从而使攻击者能够推断有关文件系统的敏感详细信息。
Compliant Solution 1: Security Policy
合规解决方案 1:安全策略
class ExceptionExample {
public static void main(String[] args) {
File file = null;
try {
file = new File(System.getenv("APPDATA") +
args[0]).getCanonicalFile();
if (!file.getPath().startsWith("c:\\homepath")) {
System.out.println("Invalid file");
return;
}
} catch (IOException x) {
System.out.println("Invalid file");
return;
}
try {
FileInputStream fis = new FileInputStream(file);
} catch (FileNotFoundException x) {
System.out.println("Invalid file");
return;
}
}
}
Solution: 溶液:
-
Implements a security policy that restricts file access to a specific directory (
c:\homepath
).
实现限制对特定目录 ( ) 的文件访问的安全策略c:\homepath
。 -
Provides a generic error message to conceal information about the file system layout outside the permitted directory.
提供一般错误消息,以将有关文件系统布局的信息隐藏在允许的目录之外。
Compliant Solution 2: Restricted Input
合规解决方案 2:受限输入
class ExceptionExample {
public static void main(String[] args) {
FileInputStream fis = null;
try {
switch(Integer.valueOf(args[0])) {
case 1:
fis = new FileInputStream("c:\\homepath\\file1");
break;
case 2:
fis = new FileInputStream("c:\\homepath\\file2");
break;
//...
default:
System.out.println("Invalid option");
break;
}
} catch (Throwable t) {
MyExceptionReporter.report(t); // Sanitize
}
}
}
Solution:
-
Operates under the policy that only specific files (
c:\homepath\file1
andc:\homepath\file2
) are permitted to be opened by the user. -
Uses a centralized exception reporting mechanism (
MyExceptionReporter
) to filter sensitive information from any resulting exceptions.
Considerations:
-
Handling Security Exceptions: Ensure that security-related exceptions are appropriately logged and sanitized.
-
Scalability: Design solutions to handle a range of inputs efficiently, considering future scalability requirements.
Risk Assessment:
-
Risk Level: Medium
-
Probability: Probable
-
Impact: High
-
Priority: P4
-
Likelihood: L3
Do not encode noncharacter data as a string
guideline addresses the issue of converting noncharacter data, such as numeric values, to strings and the potential loss of data integrity associated with such conversions. Let’s break down the provided code examples and compliant solutions.
指南解决了将非字符数据(如数值)转换为字符串的问题,以及与此类转换相关的数据完整性的潜在损失。让我们分解一下提供的代码示例和合规的解决方案。
Noncompliant Code Example:
不合规代码示例:
This code attempts to convert a BigInteger
value to a String
and then back to a BigInteger
. However, the process involves converting the BigInteger
value to a byte array using toByteArray()
method, then creating a string from the byte array using the String(byte[] bytes)
constructor, and finally converting the string back to a byte array and then to a BigInteger
. This approach risks data integrity issues because not all byte arrays can be safely converted to strings and back.
此代码尝试将 BigInteger
值转换为 , String
然后再转换回 BigInteger
.但是,该过程涉及使用 method 将 BigInteger
值转换为字节数组,然后使用 String(byte[] bytes)
构造函数从字节数组创建字符串,最后将字符串转换回字节数组,然后转换为 BigInteger
. toByteArray()
这种方法存在数据完整性问题的风险,因为并非所有字节数组都可以安全地转换为字符串并返回。
BigInteger x = new BigInteger("530500452766");
byte[] byteArray = x.toByteArray();
String s = new String(byteArray); // Risk of data corruption
byteArray = s.getBytes();
x = new BigInteger(byteArray); // Unlikely to reproduce the original value
Compliant Solution: 合规解决方案:
Using toString()
and getBytes()
:
使用 toString()
和 getBytes()
:
This compliant solution ensures data integrity by first converting the BigInteger
object to a string using the toString()
method, which generates valid character data. Then, it converts the string to a byte array and back to a BigInteger
.
此兼容解决方案通过首先使用生成有效字符数据 toString()
的方法将 BigInteger
对象转换为字符串来确保数据完整性。然后,它将字符串转换为字节数组并转换回 BigInteger
.
BigInteger x = new BigInteger("530500452766");
String s = x.toString(); // Valid character data
byte[] byteArray = s.getBytes();
String ns = new String(byteArray);
x = new BigInteger(ns);
Using Base64 Encoding: 使用 Base64 编码:
Another compliant solution involves using Base64 encoding to safely convert the BigInteger
value to a string and back without corrupting the data. Java 8 introduced the java.util.Base64
class, providing encoders and decoders for the Base64 encoding scheme.
另一种合规的解决方案涉及使用 Base64 编码安全地将 BigInteger
值转换为字符串并返回,而不会损坏数据。Java 8 引入了该 java.util.Base64
类,为 Base64 编码方案提供了编码器和解码器。
BigInteger x = new BigInteger("530500452766");
byte[] byteArray = x.toByteArray();
String s = Base64.getEncoder().encodeToString(byteArray);
byteArray = Base64.getDecoder().decode(s);
x = new BigInteger(byteArray);
Risk Assessment:
Encoding noncharacter data as a string can lead to a loss of data integrity. Therefore, it’s crucial to use appropriate methods like toString()
or encoding schemes like Base64 to ensure safe conversion without data corruption.
Do not release apps that are debuggable
the android:debuggable attribute in Android app development, emphasizing the importance of ensuring that this attribute is set to false before releasing the app. Let’s delve into the provided code examples and compliant solutions.
Noncompliant Code Example:
This code example demonstrates an Android app with the android:debuggable attribute set to true, allowing access to sensitive data via debugging commands executed in the Android Debug Bridge (ADB) shell. Users can access the app’s files and view sensitive information even without access to its source code.
$ adb shell
shell@android:/ $ run-as com.example.someapp sh
shell@android:/data/data/com.example.someapp $ id
uid=10060(app_60) gid=10060(app_60)
shell@android:/data/data/com.example.someapp $ ls files/
secret_data.txt
shell@android:/data/data/com.example.some $ cat files/secret_data.txt
password=GoogolPlex
account_number=31974286
With android:debuggable set to true, users can easily access sensitive data related to the app, posing a security risk.
将 android:debuggable 设置为 true 后,用户可以轻松访问与应用相关的敏感数据,从而带来安全风险。
Compliant Solution: 合规解决方案:
To ensure app security, the android:debuggable attribute must be set to false before releasing the app. This prevents users from accessing sensitive information and debugging the app without authorization.
为确保应用安全,在发布应用之前,必须将 android:debuggable 属性设置为 false。这样可以防止用户在未经授权的情况下访问敏感信息和调试应用。
android:debuggable="false"
Some development environments automatically set android:debuggable to true for incremental or debugging builds but set it to false for release builds.
对于增量或调试版本,某些开发环境会自动将 android:debuggable 设置为 true,但对于发布版本,则将其设置为 false。
<configuration>
<compilation debug="true"/>
</configuration>
Risk Assessment: 风险评估:
Releasing an app with android:debuggable set to true poses a high risk as it can leak sensitive information and make the app vulnerable to decompilation and alteration of its source code. Attackers can exploit this additional information to mount targeted attacks on the app’s framework, database, or other resources.
发布将 android:debuggable 设置为 true 的应用会带来高风险,因为它可能会泄露敏感信息,并使应用容易受到反编译和更改其源代码的影响。攻击者可以利用这些附加信息对应用的框架、数据库或其他资源发起有针对性的攻击。
Consider privacy concerns when using Geolocation API
使用地理位置 API 时考虑隐私问题
the Geolocation API, which allows web browsers to access the geographical location information of a user’s device. It emphasizes obtaining the user’s explicit permission before sending location information to websites to ensure privacy and security.
地理位置 API,允许 Web 浏览器访问用户设备的地理位置信息。它强调在向网站发送位置信息之前获得用户的明确许可,以确保隐私和安全。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code example overrides the onGeolocationPermissionsShowPrompt()
method without presenting a user interface to ask for the user’s permission. Instead, it unconditionally invokes the callback
with the permission to access geolocation information, potentially leaking sensitive data without user consent.
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback){
super.onGeolocationPermissionsShowPrompt(origin, callback);
callback.invoke(origin, true, false);
}
Compliant Solution #1: 合规解决方案#1:
This compliant solution presents a user interface to ask for the user’s consent before transmitting geolocation data. Depending on the user’s response, the application can control the transmission of geolocation information.
这种合规的解决方案提供了一个用户界面,用于在传输地理位置数据之前征求用户的同意。根据用户的响应,应用程序可以控制地理位置信息的传输。
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
super.onGeolocationPermissionsShowPrompt(origin, callback);
// Ask for user's permission
// When the user disallows, do not send the geolocation information
}
Compliant Solution #2: 合规解决方案#2:
In this compliant solution, the code checks a user setting to determine whether to prompt the user for permission to access geolocation information. If the setting is enabled, it shows a screen to ask for the user’s permission. If the setting is disabled, it does not transmit the geolocation data.
在此兼容的解决方案中,代码将检查用户设置,以确定是否提示用户提供访问地理位置信息的权限。如果启用了该设置,则会显示一个屏幕,用于请求用户的权限。如果禁用该设置,则不会传输地理位置数据。
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions$Callback callback) {
super.onGeolocationPermissionsShowPrompt(origin, callback);
if(MyPreferences.getBoolean("SECURITY_ENABLE_GEOLOCATION_INFORMATION", true)) {
WebViewHolder.a(this.a).permissionShowPrompt(origin, callback);
}
else {
callback.invoke(origin, false, false);
}
}
Risk Assessment: 风险评估:
Sending a user’s geolocation information without obtaining the user’s permission violates the security and privacy considerations of the Geolocation API, potentially leaking sensitive information. Therefore, it’s crucial to implement proper user interface mechanisms to ask for consent before accessing and transmitting geolocation data.
在未获得用户许可的情况下发送用户的地理位置信息违反了地理位置 API 的安全和隐私注意事项,可能会泄露敏感信息。因此,在访问和传输地理位置数据之前,实施适当的用户界面机制以征求同意至关重要。
Properly verify server certificate on SSL/TLS
在 SSL/TLS 上正确验证服务器证书
the importance of properly verifying server certificates when using SSL/TLS protocols for secure communication in Android applications. Failure to verify server certificates can lead to vulnerabilities where sensitive user data may leak through insecure SSL communication channels.
在 Android 应用程序中使用 SSL/TLS 协议进行安全通信时正确验证服务器证书的重要性。未能验证服务器证书可能会导致敏感用户数据可能通过不安全的 SSL 通信渠道泄露的漏洞。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code example demonstrates a custom MySSLSocketFactory
class that extends SSLSocketFactory
but fails to implement proper certificate verification. The checkClientTrusted()
and checkServerTrusted()
methods are overridden with blank implementations, effectively disabling SSL certificate verification. Additionally, the code sets the sAllowAllSSL
flag to true
, enabling the use of ALLOW_ALL_HOSTNAME_VERIFIER
, which disables hostname verification.
不符合的代码示例演示了一个自定义 MySSLSocketFactory
类,该类扩展 SSLSocketFactory
但未能实现正确的证书验证。 checkClientTrusted()
将 and checkServerTrusted()
方法覆盖为空白实现,从而有效地禁用 SSL 证书验证。此外,该代码将 sAllowAllSSL
标志设置为 true
,启用使用 ALLOW_ALL_HOSTNAME_VERIFIER
,从而禁用主机名验证。
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext;
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
this.sslContext = SSLContext.getInstance("TLS");
this.sslContext.init(null, new TrustManager[] {new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return null; }
}}, null);
}
public Socket createSocket() throws IOException {
return this.sslContext.getSocketFactory().createSocket();
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException, UnknownHostException {
return this.sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
}
public static HttpClient getNewHttpClient() {
DefaultHttpClient httpClient;
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
MySSLSocketFactory mySSLSocketFactory = new MySSLSocketFactory(trustStore);
if(DefineRelease.sAllowAllSSL) {
((SSLSocketFactory)mySSLSocketFactory).setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
}
// Rest of the code
}
catch(Exception e) {
// Handle exception
}
return httpClient;
}
Compliant Solution: 合规解决方案:
The compliant solution would involve implementing proper SSL certificate verification mechanisms. Depending on the specific implementation requirements, this could include:
合规的解决方案将涉及实施适当的SSL证书验证机制。根据具体的实施要求,这可能包括:
-
Ensuring that
checkClientTrusted()
andcheckServerTrusted()
methods perform appropriate certificate validation.
确保checkClientTrusted()
和checkServerTrusted()
方法执行适当的证书验证。 -
Enabling hostname verification to match the server’s certificate with the intended hostname.
启用主机名验证以将服务器的证书与预期的主机名匹配。 -
Avoiding the use of
ALLOW_ALL_HOSTNAME_VERIFIER
to prevent bypassing hostname verification.
避免使用ALLOW_ALL_HOSTNAME_VERIFIER
以防止绕过主机名验证。
Risk Assessment: 风险评估:
Failure to properly verify server certificates in SSL/TLS communication can lead to significant security risks, including man-in-the-middle attacks where an attacker intercepts and manipulates the communication between the client and the server. This can result in the exposure of sensitive user data, undermining the confidentiality and integrity of the application’s communication channels.
未能正确验证 SSL/TLS 通信中的服务器证书可能会导致重大安全风险,包括攻击者拦截并操纵客户端和服务器之间的通信的中间人攻击。这可能会导致敏感用户数据泄露,从而破坏应用程序通信通道的机密性和完整性。
Specify permissions when creating files via the NDK
通过 NDK 创建文件时指定权限
file permissions when creating files in Android applications, especially when using native code. Improper file permissions can lead to security vulnerabilities, such as exposing sensitive data or allowing unauthorized modification of files.
在 Android 应用程序中创建文件时的文件权限,尤其是在使用本机代码时。不正确的文件权限可能会导致安全漏洞,例如暴露敏感数据或允许未经授权修改文件。
Noncompliant Code Example:
不合规代码示例:
The noncompliant code example demonstrates creating a text file using native C code without explicitly setting file permissions. This results in a file that is both world-readable and world-writable, potentially exposing it to unauthorized access or modification.
不合规的代码示例演示了如何使用本机 C 代码创建文本文件,而无需显式设置文件权限。这会导致文件既是全局可读的,又是全局可写的,可能会使其暴露给未经授权的访问或修改。
FILE *fp = fopen("/data/data/com.mine.work/file.txt", "a");
fprintf(fp, "Don't alter this content.\n");
fclose(fp);
Compliant Solution (Set Umask):
兼容解决方案(设置 Umask):
In this compliant solution, the user explicitly sets the process’s umask to ensure that the created file’s permissions match those of the Android SDK. By using the umask()
C library call, the file permissions are restricted to prevent world-writable access
在此合规解决方案中,用户显式设置进程的掩码,以确保创建的文件的权限与 Android SDK 的权限匹配。通过使用 umask()
C 库调用,文件权限受到限制,以防止全局可写访问
umask(002);
FILE *fp = fopen("/data/data/com.mine.work/file.txt", "a");
fprintf(fp, "Don't corrupt this content.\n");
fclose(fp);
Compliant Solution (Specify File Permissions):
合规解决方案(指定文件权限):
Another compliant solution involves explicitly specifying the file permissions using the open()
system call. By setting appropriate permissions using the S_IRUSR
, S_IWUSR
, S_IRGRP
, and S_IWGRP
flags, the file’s access is restricted to user and group, preventing world access.
另一个合规的解决方案涉及使用 open()
系统调用显式指定文件权限。通过使用 S_IRUSR
、 S_IWUSR
、 S_IRGRP
和 S_IWGRP
标志设置适当的权限,文件的访问仅限于用户和组,从而阻止全局访问。
const char *fn = "/data/data/com.mine.work/file.txt";
const char *content = "Don't corrupt this content.\n";
fd = open(fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
err = write(fd, content, strlen(content));
close(fd);
Risk Assessment: 风险评估:
Failure to properly set file permissions when creating files in native code may result in files being accessible or modifiable by unauthorized users or applications. This can lead to the exposure or corruption of sensitive data, posing significant security risks to the application and its users.
在本机代码中创建文件时,如果未能正确设置文件权限,可能会导致未经授权的用户或应用程序访问或修改文件。这可能导致敏感数据的泄露或损坏,从而给应用程序及其用户带来重大安全风险。
Sensitive classes must not let themselves be copied
敏感类绝不能被复制
importance of preventing the copying of classes containing private, confidential, or sensitive data. Failing to define proper copy mechanisms, such as a copy constructor, can lead to security vulnerabilities, including unauthorized data access or modification.
防止复制包含私有、机密或敏感数据的类的重要性。未能定义正确的复制机制(如复制构造函数)可能会导致安全漏洞,包括未经授权的数据访问或修改。
Noncompliant Code Example:
不合规代码示例:
In the noncompliant code example, a class SensitiveClass
is defined, containing a character array for storing a file name and a Boolean variable for managing shared access. However, the class lacks a copy constructor, allowing potential vulnerabilities if the class is copied improperly.
在不合规的代码示例中,定义了一个类 SensitiveClass
,其中包含一个用于存储文件名的字符数组和一个用于管理共享访问的布尔变量。但是,该类缺少复制构造函数,如果复制不当,则存在潜在的漏洞。
class SensitiveClass {
private char[] filename;
private Boolean shared = false;
SensitiveClass(String filename) {
this.filename = filename.toCharArray();
}
final void replace() {
if (!shared) {
for(int i = 0; i < filename.length; i++) {
filename[i]= 'x' ;}
}
}
final String get() {
if (!shared) {
shared = true;
return String.valueOf(filename);
} else {
throw new IllegalStateException("Failed to get instance");
}
}
final void printFilename() {
System.out.println(String.valueOf(filename));
}
}
Malicious Subclass: 恶意子类:
A malicious subclass MaliciousSubclass
is created, which extends SensitiveClass
and overrides the clone()
method. This subclass allows unauthorized access and modification of the sensitive data.
将创建一个恶意子类,该子类 MaliciousSubclass
扩展 SensitiveClass
并覆盖该 clone()
方法。此子类允许未经授权访问和修改敏感数据。
class MaliciousSubclass extends SensitiveClass implements Cloneable {
protected MaliciousSubclass(String filename) {
super(filename);
}
@Override public MaliciousSubclass clone() {
MaliciousSubclass s = null;
try {
s = (MaliciousSubclass)super.clone();
} catch(Exception e) {
System.out.println("not cloneable");
}
return s;
}
// main method demonstrates exploitation
}
Compliant Solution (Final Class):
合规解决方案(最终课程):
Declare SensitiveClass
as final
to prevent subclassing and ensure that the class cannot be copied.
声明 SensitiveClass
为 final
以防止子类化并确保无法复制该类。
final class SensitiveClass {
// ...
}
Compliant Solution (Final clone()):
合规解决方案(最终克隆()):
Prevent the cloning of instances by providing a final
implementation of the clone()
method that always throws a CloneNotSupportedException
.
通过提供 final
clone()
始终抛出 CloneNotSupportedException
.
class SensitiveClass {
// ...
public final SensitiveClass clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
Risk Assessment: 风险评估:
Failure to prevent the copying of sensitive classes can result in unauthorized data access or modification, leading to security vulnerabilities. Implementing proper copy mechanisms or making classes noncopyable mitigates these risks and ensures the integrity of sensitive data.
未能阻止敏感类的复制可能会导致未经授权的数据访问或修改,从而导致安全漏洞。实施适当的复制机制或使类不可复制可以降低这些风险并确保敏感数据的完整性。
References 引用
-
Android Application Secure Design/Secure Coding Guidebook by Japan Smartphone Security Association(JSSEC)
日本智能手机安全协会(JSSEC)的Android应用程序安全设计/安全编码指南
原文始发于Reza Rashidi:Attacking Android