Attacking Android指北

admin 2024年3月28日01:25:27评论2 views字数 36097阅读120分19秒阅读模式

在这篇文章中,我们从进攻性的角度深入研究了 Android 安全世界,揭示了攻击者用来攻击 Android 设备并渗透其敏感数据的各种技术和方法、案例、修复方式、风险级别等。从利用常见的编码缺陷到利用复杂的社会工程策略,我们探索了 Android 环境中存在的全方位攻击面。

Attacking Android指北

是否经常看到这样的Poc、却又不懂?带着疑问读下去,相信一定会有些收获

Attacking Android指北

Android 应用程序中的 ContentProvider 管理

Android 中的类 ContentProvider 有助于应用程序之间的数据共享。适当的访问控制对于防止未经授权访问敏感数据至关重要。控制访问的主要方法有以下三种:

1Public访问:

1公开 ContentProvider 允许其他应用程序访问其数据。

1使用文件 android:exported 中的属性 AndroidManifest.xml 来指定a是否 ContentProvider 是公共的。

1在 API 级别 16 之前, ContentProvider 除非明确设置,否则默认情况下 a 是公共的 android:exported="false"

1示例代码在 AndroidManifest.xml

Plaintext                  
        <provider        
    android:exported="true"                  
    android:name="MyContentProvider"                  
    android:authorities="com.example.mycontentprovider" />         </provider        

Private访问:

1可以将A ContentProvider 设为私有以限制其他应用程序的访问。

1从 API Level 17 开始,如果 未指定, ContentProvider 则默认情况下 a 是私有的。android:exported

1示例代码在 AndroidManifest.xml

Plaintext                  
        <provider        
    android:exported="false"                  
    android:name="MyContentProvider"                  
    android:authorities="com.example.mycontentprovider" />         </provider        
       

1禁止进入:

1需要更多细节来实施限制访问。本节需要进一步阐述。

不合规代码示例:

不合规代码的示例演示了 Twitter 客户端应用程序无意中通过 public 暴露敏感信息 ContentProvider

概念证明:

 ContentProvider 提供的代码片段说明了如何利用 其中的漏洞从 Twitter 客户端应用程序中提取敏感数据。

合规解决方案:

合规解决方案涉及将文件设为 ContentProvider 私有 AndroidManifest.xml ,以防止未经授权访问敏感数据。

Plaintext                  
        <provider        
    android:name=".content.AccountProvider"                  
    android:exported="false"                  
    android:authorities="jp.co.vulnerable.accountprovider" />         </provider        

风险评估:

在没有适当访问控制的情况下将其声明 ContentProvider 为公共可能会导致敏感信息泄露给恶意应用程序。

在 Android 应用程序中使用强权限保护导出的Service服务

背景:

该指南强调了与缺乏适当保护的出口服务相关的风险。任何应用程序都可以利用未受保护的服务,从而可能导致数据泄露或未经授权的活动。    

不合规代码示例:

不合规的代码示例演示了没有足够保护的导出服务,允许任意应用程序访问敏感信息:

Plaintext                  
                 
             ...
    ...          
       

合规解决方案:

1删除          

1通过删除           ,对服务的访问仅限于同一应用程序内的组件或具有相同用户 ID 的应用程序。

1使用自定义权限:

1如果目的是允许其他应用程序访问,则应使用自定义权限,而不是依赖“正常”等默认权限。这可以防止未经授权访问机密数据。

合规解决方案代码:

Plaintext                  
                 
                 
        <activity        
    android:permission="customPermission"                  
    ... >                  
                     
                 
                 
             
             
                   
                                               
               
           </activity        

在其他应用中的用法:    

Plaintext                  
        <uses-permission        
    android:name="customPermission"                  
    android:maxSdkVersion=.. />         </uses-permission        

          

Plaintext                  
Intent in = new Intent();                  
in.setAction("package_name.MyAction");                  
in.addCategory("android.intent.category.DEFAULT");                  
startActivity(in);

风险评估:

如果无法使用强大的权限保护导出的服务,则会带来很高的风险,可能会导致敏感数据泄露或拒绝服务攻击。

防范 Android ContentProvider 中的目录遍历漏洞

背景:

该指南警告了 ContentProvider.openFile() 在 Android 应用程序中使用该方法时可能出现的潜在目录遍历漏洞。目录遍历漏洞可能允许攻击者访问目标目录之外的文件,从而导致未经授权的访问或数据损坏。

不合规代码示例 1:

第一个不兼容的代码示例尝试使用 访问文件 Uri.getLastPathSegment() ,当路径经过 URL 编码时,该文件可能容易受到目录遍历攻击:

Plaintext                  
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);                  
}

不合规代码示例 2:    

第二个不合规代码示例尝试通过解码 URI 字符串来修复该漏洞,但它仍然容易受到双重编码攻击:

Plaintext                  
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);                  
}

概念证明:

恶意代码可以通过向内容提供商提供特制的 URI 字符串来利用这些漏洞,从而导致未经授权的文件访问或遍历。

合规解决方案:

合规解决方案通过解码 URI 字符串并规范化文件路径来确保防止目录遍历:

Plaintext                  
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);                  
}

适用性:

本指南适用于任何通过 ContentProvider 交换文件的 Android 应用程序,确保防止目录遍历攻击。    

风险评估:

未能正确解码和规范化 ContentProvider 接收的文件路径可能会导致目录遍历漏洞,从而导致未经授权的访问或敏感数据的损坏。

背景:

在 AndroidManifest.xml 文件中声明活动的 Intent 过滤器会将活动公开给其他应用程序,从而可能允许未经授权的访问。恶意应用程序可能会利用此漏洞来滥用所暴露活动的敏感功能。

不合规代码示例:

不合规的代码示例显示了一个在不限制访问的情况下导出活动的 AndroidManifest.xml 文件:

Plaintext                  
        <activity        
    android:configChanges="keyboard|keyboardHidden|orientation"                  
    android:name=".media.yfrog.YfrogUploadDialog"                  
    android:theme="@style/Vulnerable.Dialog"                  
    android:windowSoftInputMode="stateAlwaysHidden">                            
                 
                     
                                          
                                            
                                              
                                            
                                
         </activity        

合规解决方案(不导出活动):

在合规解决方案中,活动不会导出,从而限制其仅接受来自同一应用程序或具有相同用户 ID 的应用程序中的意图:

Plaintext                  
        <activity        
    android:configChanges="keyboard|keyboardHidden|orientation"                  
    android:name=".media.yfrog.YfrogUploadDialog"                  
    android:theme="@style/ VulnerableTheme.Dialog"                  
    android:windowSoftInputMode="stateAlwaysHidden"                  
    android:exported="false">                     
        </activity        
       

合规解决方案:

没有将活动声明为未导出,而是在活动本身内实现了调用者验证。这可确保该活动仅在从同一包调用时才继续进行:

Plaintext                  
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();                  
        }                  
        ...                  
    }                  
}

风险评估:

在按照收到的意图采取行动之前未能验证调用者的身份可能会导致敏感数据泄露或拒绝服务攻击。

避免在未加密的情况下将敏感信息存储在外部存储(SD 卡)上

背景:

Android 提供外部存储选项(例如 SD 卡)来存储应用程序数据。但是,存储在外部存储上的文件不能保证安全,并且可以被其他应用程序或用户访问。在外部存储上存储未经加密的敏感信息会带来重大的安全风险。    

不合规代码示例:

以下代码片段演示了将敏感信息直接存储到外部存储而不加密:

Plaintext                  
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                  
    }                  
  }                  
}

合规解决方案#1(在内部存储上保存文件):

合规解决方案将敏感信息存储在内部存储目录中,权限设置为 MODE_PRIVATE ,确保其他应用程序无法访问该文件:

Plaintext                  
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                  
    }                  
  }                  
}
       

风险评估:

在没有加密的情况下将敏感信息存储在外部存储上可能会导致数据泄露给恶意应用程序,从而损害数据的机密性。实施适当的加密或将数据存储在内部存储中可以减轻这种风险。

在 Android 中记录敏感信息

背景:

Android 通过该类提供内置的日志记录机制 android.util.Log 。开发人员使用各种日志记录级别( Log.dLog.eLog.iLog.vLog.w )将调试信息、错误和其他消息输出到系统日志。但是,记录敏感信息(例如访问令牌或用户位置数据)可能会带来安全风险。

记录敏感信息:

开发人员应避免使用该类记录敏感信息 android.util.Log 。相反,他们应该谨慎并确保仅记录非敏感数据以用于调试目的。

不合规代码示例:

Plaintext                  
// Logging sensitive information (access token) using Log.d                  
Log.d("Facebook-authorize", "Login Success! access_token="                  
      + getAccessToken() + " expires="                  
      + getAccessExpires());

概念证明(获取日志输出):    

Plaintext                  
// 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);

合规解决方案:

开发人员应确保不记录敏感信息。他们可以通过以下方式实现这一目标:

1检查代码以识别并删除敏感信息的任何记录。

1使用自定义日志记录机制自动关闭发布版本中的日志记录。

1使用 ProGuard 等混淆工具来删除特定的日志记录调用。

风险评估:

记录敏感信息可能会导致数据泄露、损害用户隐私并可能将敏感数据暴露给恶意应用程序。    

保护 Android 中的敏感数据

背景:

Android 应用程序经常处理敏感数据,例如用户凭据或个人信息。保护这些数据免遭其他应用程序或实体未经授权的访问至关重要。数据安全措施包括在创建文件或数据库时使用适当的文件模式以及采用加密来增强保护。

不合规代码示例:

Plaintext                  
// Creating a file that is world-readable (noncompliant)                  
openFileOutput("someFile", MODE_WORLD_READABLE);

在此示例中,文件是使用该 MODE_WORLD_READABLE 标志创建的,允许任何应用程序读取其内容,这会带来安全风险。

合规解决方案:

Plaintext                  
// Creating a file with MODE_PRIVATE (compliant)                  
openFileOutput("someFile", MODE_PRIVATE);

通过使用 MODE_PRIVATE ,该文件只能由创建该文件的应用程序访问,从而确保数据安全。

风险评估:

未能保护敏感数据可能会导致数据泄露、损害用户隐私并可能将机密信息暴露给未经授权的应用程序或实体。

缓存

缓存数据可能会带来安全风险,因为如果设备丢失或被盗,其他应用程序或未经授权的用户可能会访问这些数据。以下是指南中提到的要点的细分:

1缓存 Web 应用程序数据 :在缓存中存储 URL 历史记录、HTTP 标头、HTML 表单输入和 cookie 等数据可能会将敏感信息暴露给其他应用程序。    

1键盘缓存 :用户通过键盘输入的单词会存储在Android用户词典中,以便自动更正。任何应用程序无需许可即可访问这些数据,这可能会导致敏感信息泄露。

1缓存的相机图像 :应用程序可以缓存设备相机捕获的图像,即使在应用程序完成后仍然可以访问这些图像。在没有适当安全措施的情况下存储此类图像可能会损害用户隐私。

1GUI 对象缓存 :保留在内存中的应用程序屏幕可以让任何具有设备访问权限的人访问事务历史记录。在内存中缓存敏感信息会增加数据泄露的风险。

为了解决这些问题,开发人员应该:

1尽可能避免缓存敏感信息。

1使用 clearCache() 删除缓存数据等方法,尤其是在使用 WebView .

1利用服务器端标头来 no-cache 防止应用程序缓存特定内容。

不合规代码示例:

Plaintext                  
// Caching web application data (noncompliant)                  
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);

在此不合规的代码示例中,Web 应用程序数据使用默认缓存模式进行缓存,可能会泄露 URL 历史记录、HTTP 标头和 cookie 等敏感信息。

合规解决方案:

Plaintext                  
// Avoid caching web application data (compliant)                  
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

通过将缓存模式设置为 LOAD_NO_CACHE ,可以禁用Web应用数据的缓存,降低敏感信息泄露的风险。

风险评估:

如果无法阻止敏感数据的缓存,可能会导致未经授权访问用户信息,从而损害隐私和安全。实施措施以避免在涉及敏感数据的地方进行缓存非常重要。

本指南警告不要在 Android 应用程序中使用常量 MODE_WORLD_READABLE 和 MODE_WORLD_WRITABLE,因为它们可能会产生安全漏洞。相反,我们鼓励开发人员使用更正式的机制进行应用程序之间的交互和通信,例如 ContentProvider、BroadcastReceiver 和 Service。以下是每个机制的解释:    

1ContentProvider :该组件向应用程序提供内容,用于在多个应用程序之间共享数据。它提供了一种结构化且安全的方式来将数据公开给其他应用程序,同时保持对访问权限的控制。

1BroadcastReceiver :当感兴趣的事件发生时发送广播,应用程序可以注册接收某些广播。BroadcastReceiver 充当应用程序之间的消息传递系统,允许它们进行通信并响应系统范围的事件。

1服务 :服务允许应用程序执行后台任务并将其某些功能公开给其他应用程序。它们使长时间运行的操作能够独立于应用程序的 UI 执行。

不合规的代码示例演示了如何使用 MODE_WORLD_READABLE 和 MODE_WORLD_WRITABLE 常量在应用程序之间共享文件,但由于其安全隐患,不鼓励这样做。这些常量允许其他应用程序使用共享首选项 API 读取或写入文件,这可能会导致安全漏洞。

不合规代码示例 1:

Plaintext                  
String FILENAME = "example_file";                  
String string = "hello world!";                  
                 
FileOutputStream outputStream = openFileOutput(FILENAME, Context.MODE_WORLD_READABLE);                  
outputStream.write(string.getBytes());                  
outputStream.close();

不合规代码示例 2:

Plaintext                  
String FILENAME = "example_file";                  
String string = "hello world!";                  
                 
FileOutputStream outputStream = openFileOutput(FILENAME, Context.MODE_WORLD_WRITABLE);                  
outputStream.write(string.getBytes());                  
outputStream.close();
       

通过避免使用这些常量并选择更安全的机制(例如 ContentProvider、BroadcastReceiver 和 Service),开发人员可以最大限度地降低 Android 应用程序中存在安全漏洞的风险。

对于 OAuth,使用显式意图方法来传递访问令牌

该指南强调了在 Android 应用程序中使用显式意图而不是隐式意图来保护用户信息和防止潜在安全风险的重要性。显式意图明确指定目标组件,而隐式意图声明所有应用程序都可以使用的一般操作,可能会暴露敏感的用户操作。

不合规代码示例:

Plaintext                  
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);                  
}

在不兼容的代码示例中,隐式意图用于通过调用特定操作来发送访问令牌,而无需显式指定目标组件。这种方法可能会导致安全漏洞并将用户信息暴露给非预期接收者。

合规解决方案:

Plaintext                  
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);                  
}

在兼容的解决方案中,显式意图用于通过显式指定目标组件 (YourOAuthActivity.class) 来发送访问令牌。这种方法可确保意图仅针对预期的接收者,从而增强安全性并保护用户信息。    

不要使用隐式意图广播敏感信息

本指南的重点是保护 Android 应用程序中的广播意图,以防止敏感信息泄露或拒绝服务攻击。它强调使用显式意图或其他安全机制来限制广播意图接收者的重要性。

不合规代码示例:

Plaintext                  
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);                  
    }                  
}

在此不合规代码示例中,隐式意图用于广播敏感信息,例如设备的 IP 地址、端口号和密码。这种方法允许任何应用程序(包括恶意应用程序)接收广播消息,从而可能导致数据泄漏或拒绝服务攻击。

概念证明:

Plaintext                  
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();                  
        }                  
    }                  
}

概念验证演示了恶意广播接收器如何拦截隐式意图并访问易受攻击的应用程序发送的敏感数据。    

合规解决方案:

Plaintext                  
Intent intent = new Intent("my-sensitive-event");                  
intent.putExtra("event", "this is a test event");                  
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

在兼容的解决方案中,LocalBroadcastManager用于发送广播意图。这可确保意图仅在同一应用程序内广播和接收,从而防止其他应用程序访问敏感信息。

风险评估:使用隐式意图广播敏感信息可能会将数据暴露给恶意应用程序或导致拒绝服务攻击。采用显式意图、LocalBroadcastManager 或其他安全机制可以减轻这些风险并增强应用程序的安全性。

不允许WebView通过文件方案访问敏感本地资源

本指南重点关注在 Android 应用程序中使用 WebView 类时的安全问题和最佳实践。它提供了对 WebView 中几种方法的深入了解,如果使用不当,这些方法可能会引入安全漏洞。

WebView 安全问题:

1setJavaScriptEnabled():

1在 WebView 中启用 JavaScript 执行。

1默认值:False。

1风险:允许 JavaScript 代码执行,可能导致安全漏洞。

1setPluginState():

1启用或禁用 WebView 中的插件。

1选项:ON(始终加载)、ON_DEMAND(如果插件存在则加载)、OFF(禁用全部)。

1默认值:OFF。

1风险:恶意内容可能会利用启用的插件,从而损害安全性。    

1setAllowFileAccess():

1启用或禁用 WebView 内的文件访问。

1默认值:true。

1风险:允许或限制对本地文件的访问,根据应用程序要求影响安全性。

1setAllowContentAccess():

1启用或禁用 WebView 内的内容 URL 访问。

1默认值:true。

1风险:控制对内容提供商的访问,根据应用程序需求影响安全性。

1setAllowFileAccessFromFileURLs():

1控制 JavaScript 对文件方案 URL 内容的访问。

1默认值:true 或 false,基于 API 级别。

1风险:确定在文件方案 URL 中运行的 JavaScript 是否可以访问其他文件方案 URL,从而影响基于 API 级别的安全性。

1setAllowUniversalAccessFromFileURLs():

1控制 JavaScript 对来自文件方案 URL 的任何来源的内容的访问。

1默认值:true 或 false,基于 API 级别。

1风险:影响 JavaScript 对文件方案 URL 内容的访问,从而影响基于 API 级别的安全性。

不合规代码示例:

提供的代码演示了一个带有 WebView 组件的活动,该组件启用 JavaScript 并处理通过 Intent 传递的任何 URI(无需验证)。此设置可能会导致潜在的安全漏洞,尤其是在加载恶意内容的情况下。

Plaintext                  
// 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);                  
  }                  
}
       

不合规的代码会初始化 WebView 对象,启用 JavaScript,并加载通过 Intent 接收到的 URL,而无需进行适当的验证。如果加载恶意内容,这可能会导致安全漏洞。    

概念验证演示了恶意应用程序如何通过使用精心设计的 HTML 文件 URI 向目标应用程序发送意图来利用此漏洞。

兼容的解决方案会验证接收到的 URL,以确保它在加载到 WebView 之前以“http”或“https”开头。这有助于降低加载潜在恶意本地内容的风险。

合规解决方案:

兼容的解决方案会在使用 WebView 渲染之前验证通过 Intent 接收到的 URI。它检查 URI 是否以“http”开头,以确保仅加载受信任的 URL。此方法有助于降低加载恶意本地内容的风险。

风险评估:

在 WebView 使用中未能实施适当的安全措施可能会导致信息泄露和其他安全漏洞。必须仔细验证输入并配置 WebView 设置,以最大程度地减少这些风险。

不要在可能包含不受信任内容的 WebView 中提供 addJavascriptInterface 方法访问。(API 级别16 或以下)

关于 addJavascriptInterface 在 Android 应用程序中使用该方法的编码指南,特别针对 API 级别 JELLY_BEAN 及以下。当此方法与 WebView 中不受信任的内容一起使用时,可能会使应用程序遭受脚本攻击,从而可能危及敏感数据和应用程序控制。

不合规代码示例:

不合规的代码表明该 addJavascriptInterface 方法的使用不安全:

Plaintext                  
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");
       

通过允许 JavaScript 控制主机,该代码为潜在的脚本攻击开辟了途径,利用 Java 反射来访问注入对象的公共方法,从而获得对敏感信息的访问权限。

合规解决方案:

1避免使用 addJavascriptInterface :在这种方法中,代码 addJavascriptInterface 完全避免使用该方法。

Plaintext                  
WebView webView = new WebView(this);                  
setContentView(webView);

在清单中指定最低 SDK 版本 :另一个兼容的解决方案涉及在应用程序的清单中指定它仅适用于 API 级别 JELLY_BEAN_MR1 及更高版本,其中只能 JavascriptInterface 从 JavaScript 访问带有注释的公共方法。

Plaintext                  
                 
             
    ...          
         

适用性:

1Android 版本适用性 :适用于 Android API 版本 16 (JELLY_BEAN) 及更低版本。

风险评估:

1风险级别 :高

1概率 :可能

1影响 :中    

1优先级 :P12

1可能性 :L1

          

从格式字符串中排除未经处理的用户输入

有关使用包 中的类中的 format()printf() 方法的指南 。这些方法用于格式化输出,但如果将不受信任的数据合并到格式字符串中,则会带来安全风险。PrintStream  java.io

不合规代码示例:

不合规的代码将不受信任的数据合并到格式字符串中,可能会泄露敏感信息或允许拒绝服务攻击。

Plaintext                  
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                  
    );                  
  }                  
}

合规解决方案:

兼容的解决方案避免将不受信任的用户输入合并到格式字符串中,从而使任何格式说明符无效。

Plaintext                  
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                  
    );                  
  }                  
}
       

风险评估:

1风险级别 :中

1概率 :不太可能

1影响 :中

1优先级 :P4

1可能性 :L3

清理正则表达式中包含的不受信任的数据

讨论与正则表达式 (regex) 注入相关的风险,并提供合规解决方案来减轻这些风险。它强调了清理正则表达式模式中使用的不受信任输入以防止潜在安全漏洞的重要性。

正则表达式注入的风险:

当不受信任的输入合并到正则表达式模式中时,就会发生正则表达式注入,从而允许攻击者以偏离程序预期行为的方式修改原始模式。这可能导致控制流操纵、信息泄漏或拒绝服务 (DoS) 漏洞。

正则表达式中的易受攻击的结构:

1匹配标志 :不受信任的输入可能会覆盖传递给 Pattern.compile() 方法的匹配选项。

1贪婪 :注入尝试可能会更改正则表达式以匹配尽可能多的字符串,从而可能暴露敏感信息。

1分组 :攻击者可以通过提供不受信任的输入来操纵正则表达式分组。

不合规代码示例:

提供的代码使用不受信任的用户输入动态构造正则表达式模式,使其容易受到正则表达式注入攻击。    

Plaintext                  
public static void FindLogEntry(String search) {                  
    // Construct regex dynamically from user string                  
    String regex = "(.*? +public\[\d+\] +.*" + search + ".*)";                  
    // ...                  
}

合规解决方案:

1白名单 :通过过滤掉非字母数字字符来净化搜索词。

Plaintext                  
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 + ".*)";                  
    // ...                  
}

1使用 Pattern.quote() :转义搜索字符串中的任何潜在恶意字符。

Plaintext                  
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 + ".*)";                  
    // ...                  
}

风险评估:

1风险级别 :中

1概率 :不太可能    

1影响 :中

1优先级 :P4

1可能性 :L3

围绕native方法定义包装器

强调了为用 C 和 C++ 等语言编写的 Java 中的native方法定义包装器方法的重要性,以确保安全性和可维护性。它强调验证输入、执行安全检查和防御性复制的必要性,以防止缓冲区溢出等漏洞。

native方法概述:

native方法允许与用 C 和 C++ 等语言编写的代码集成,从而为 Java 提供了可扩展性。然而,由于偏离了 Java 的策略,使用native方法可能会损害可移植性和灵活性。

不合规代码示例:

提供的代码公开公开native方法 nativeOperation() ,允许不受信任的调用者直接调用它,绕过安全检查。

Plaintext                  
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");                  
  }                  
}

合规解决方案:

合规解决方案通过将native方法声明为私有方法并 doOperation() 通过适当的安全检查、输入验证和防御性复制实现包装方法来解决安全问题。    

Plaintext                  
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");                  
  }                  
}

例外情况:

1JN100-J-EX0 :类似的native方法 int rand(void) 不需要安全管理器检查、参数验证或防御性复制不需要包装。

风险评估:

1风险级别 :中

1概率 :可能    

1影响 :高

1优先级 :P4

1可能性 :L3

不允许异常暴露敏感信息

在 Java 程序中传播异常时过滤敏感信息的重要性,以防止可能帮助攻击者开发漏洞的信息泄漏。它讨论了与暴露异常消息和类型相关的风险,提供了有问题的异常和不合规代码的示例。

异常名称

信息泄露或威胁的描述

java.io .FileNotFoundException

底层文件系统结构、用户名枚举

java.sql.SQLException

数据库结构、用户名枚举

java.net .BindException

当不受信任的客户端可以选择服务器端口时,枚举开放端口

java.util.ConcurrentModificationException

可能提供有关线程不安全代码的信息

javax.naming.InsufficientResourcesException

服务器资源不足(可能有助于 DoS)

java.util.MissingResourceException

资源枚举

java.util.jar.JarException

底层文件系统结构

java.security .acl.NotOwnerException

所有者枚举

java.lang.OutOfMemoryError

拒绝服务

java.lang.StackOverflowError

拒绝服务

打印堆栈跟踪还可能导致无意中向攻击者泄露有关进程结构和状态的信息。当在控制台中运行的 Java 程序由于未捕获的异常而终止时,异常的消息和堆栈跟踪将显示在控制台上;堆栈跟踪本身可能包含有关程序内部结构的敏感信息。因此,任何可能在不受信任的用户可访问的控制台上运行的程序都绝不能由于未捕获的异常而中止。    

异常传播的风险:

1信息泄漏 :异常可能会泄露有关应用程序内部结构、系统配置或用户环境的敏感细节。

1拒绝服务 (DoS) :攻击者可以利用异常来收集潜在 DoS 攻击的信息或利用漏洞。

不合规代码示例 1:异常消息和类型泄漏

Plaintext                  
class ExceptionExample {                  
  public static void main(String[] args) throws FileNotFoundException {                  
    FileInputStream fis =                  
        new FileInputStream(System.getenv("APPDATA") + args[0]);                  
  }                  
}

风险:

1向攻击者公开有关文件系统布局的敏感信息。

1允许攻击者通过传递虚构的路径名来重建底层文件系统。

不合规代码示例 2:包装并重新抛出敏感异常

Plaintext                  
try {                  
  FileInputStream fis =                  
      new FileInputStream(System.getenv("APPDATA") + args[0]);                  
} catch (FileNotFoundException e) {                  
  // Log the exception                  
  throw new IOException("Unable to retrieve file", e);                  
}

风险:

1即使用户无法直接访问记录的异常,原始异常仍然可以向攻击者提供有关文件系统布局的信息。    

不合规代码示例 3:已清理的异常

Plaintext                  
class SecurityIOException extends IOException {/* ... */};                  
                 
try {                  
  FileInputStream fis =                  
      new FileInputStream(System.getenv("APPDATA") + args[0]);                  
} catch (FileNotFoundException e) {                  
  // Log the exception                  
  throw new SecurityIOException();                  
}

风险:

1尽管这种方法不太可能泄漏有用信息,但它仍然会暴露出无法读取指定文件,从而使攻击者能够推断有关文件系统的敏感详细信息。

合规方案一:安全策略

Plaintext                  
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;                  
    }                  
  }                  
}
       

解决方案:

1实施限制对特定目录 ( c:homepath) 的文件访问的安全策略 

1提供一般错误消息以隐藏有关允许目录之外的文件系统布局的信息。

合规方案二:限制输入

Plaintext                  
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                  
    }                  
  }                  
}

解决方案:

1在仅允许用户打开特定文件( c:homepathfile1c:homepathfile2) 的策略下运行。 

1使用集中式异常报告机制 ( MyExceptionReporter ) 从任何生成的异常中过滤敏感信息。    

注意事项:

1处理安全异常 :确保正确记录和清理与安全相关的异常。

1可扩展性 :设计解决方案以有效处理一系列输入,考虑未来的可扩展性要求。

风险评估:

1风险级别 :中

1概率 :可能

1影响 :高

1优先级 :P4

1可能性 :L3

不要将非字符数据编码为字符串

指南解决了将非字符数据(例如数值)转换为字符串的问题以及与此类转换相关的数据完整性的潜在损失。让我们分解提供的代码示例和合规解决方案。

不合规代码示例:

此代码尝试将值转换 BigInteger 为 a String ,然后再转换回 a BigInteger 。但是,该过程涉及 BigInteger 使用方法将值转换为字节数组 toByteArray() ,然后使用构造函数从字节数组创建字符串 String(byte[] bytes) ,最后将字符串转换回字节数组,然后转换为 BigInteger . 这种方法存在数据完整性问题的风险,因为并非所有字节数组都可以安全地转换为字符串并返回。

Plaintext                  
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

合规解决方案:

使用 toString()getBytes()    

该合规解决方案首先 BigInteger 使用该方法将对象转换为字符串 toString() ,从而生成有效的字符数据,从而确保数据完整性。然后,它将字符串转换为字节数组,然后再转换回 BigInteger .

Plaintext                  
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);

使用 Base64 编码:

另一个兼容的解决方案涉及使用 Base64 编码将 BigInteger 值安全地转换为字符串并返回,而不会损坏数据。Java 8 引入了该类 java.util.Base64 ,为 Base64 编码方案提供编码器和解码器。

Plaintext                  
BigInteger x = new BigInteger("530500452766");                  
byte[] byteArray = x.toByteArray();                  
String s = Base64.getEncoder().encodeToString(byteArray);                  
byteArray = Base64.getDecoder().decode(s);                  
x = new BigInteger(byteArray);

风险评估:

将非字符数据编码为字符串可能会导致数据完整性丢失。toString() 因此,使用适当的方法(例如Base64)或编码方案(例如 Base64)来确保安全转换而不损坏数据 至关重要。

不要发布可调试的应用程序

Android 应用程序开发中的 android:debuggable 属性,强调在发布应用程序之前确保该属性设置为 false 的重要性。让我们深入研究提供的代码示例和合规解决方案。

不合规代码示例:

此代码示例演示了将 android:debuggable 属性设置为 true 的 Android 应用程序,允许通过在 Android 调试桥 (ADB) shell 中执行的调试命令访问敏感数据。即使不访问其源代码,用户也可以访问应用程序的文件并查看敏感信息。    

Plaintext                  
$ 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

将 android:debuggable 设置为 true 后,用户可以轻松访问与应用程序相关的敏感数据,从而带来安全风险。

合规解决方案:

为了保证应用程序的安全,在发布应用程序之前必须将 android:debuggable 属性设置为 false。这可以防止用户未经授权访问敏感信息并调试应用程序。

Plaintext                  
android:debuggable="false"

某些开发环境会自动将 android:debuggable 设置为 true 以进行增量或调试构建,但将其设置为 false 以进行发布构建。

Plaintext                  
                            
             
         

风险评估:

发布 android:debuggable 设置为 true 的应用程序会带来很高的风险,因为它可能会泄露敏感信息,并使应用程序容易受到反编译和源代码更改的影响。攻击者可以利用这些附加信息对应用程序的框架、数据库或其他资源发起有针对性的攻击。

使用 Geolocation API 时考虑隐私问题

Geolocation API,允许网络浏览器访问用户设备的地理位置信息。它强调在向网站发送位置信息之前获得用户的明确许可,以确保隐私和安全。    

不合规代码示例:

不兼容的代码示例重写该 onGeolocationPermissionsShowPrompt() 方法,而不提供用户界面来请求用户的许可。相反,它无条件地调用 callback 具有访问地理位置信息权限的 ,可能会在未经用户同意的情况下泄露敏感数据。

Plaintext                  
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback){                  
    super.onGeolocationPermissionsShowPrompt(origin, callback);                  
    callback.invoke(origin, true, false);                  
}

合规解决方案#1:

该合规解决方案提供了一个用户界面,用于在传输地理定位数据之前征求用户的同意。根据用户的响应,应用程序可以控制地理位置信息的传输。

Plaintext                  
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                  
}

合规解决方案#2:

在此合规解决方案中,代码检查用户设置以确定是否提示用户授予访问地理位置信息的权限。如果启用该设置,则会显示一个屏幕以请求用户的许可。如果禁用该设置,则不会传输地理位置数据。

Plaintext                  
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);                  
    }                  
}
       

风险评估:

在未经用户许可的情况下发送用户的地理位置信息违反了 Geolocation API 的安全和隐私考虑,可能会泄露敏感信息。因此,在访问和传输地理位置数据之前实施适当的用户界面机制以征求同意至关重要。

正确验证 SSL/TLS 上的服务器证书

在 Android 应用程序中使用 SSL/TLS 协议进行安全通信时,正确验证服务器证书的重要性。未能验证服务器证书可能会导致敏感用户数据可能通过不安全的 SSL 通信通道泄露的漏洞。

不合规代码示例:

不合规的代码示例演示了一个自定义 MySSLSocketFactory 类,该类扩展 SSLSocketFactory 但无法实现正确的证书验证。和 方法被空白实现覆盖,有效禁用 SSL 证书验证 checkClientTrusted()checkServerTrusted() 此外,代码将 sAllowAllSSL 标志设置为 true ,启用 ALLOW_ALL_HOSTNAME_VERIFIER ,从而禁用主机名验证。

Plaintext                  
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;                  
}
       

合规解决方案:

合规解决方案将涉及实施适当的 SSL 证书验证机制。根据具体的实施要求,这可能包括:

1确保该 checkClientTrusted() 方法 checkServerTrusted() 执行适当的证书验证。

1启用主机名验证以将服务器的证书与预期主机名相匹配。

1避免使用 来 ALLOW_ALL_HOSTNAME_VERIFIER 防止绕过主机名验证。    

风险评估:

未能正确验证 SSL/TLS 通信中的服务器证书可能会导致重大安全风险,包括攻击者拦截和操纵客户端与服务器之间的通信的中间人攻击。这可能会导致敏感用户数据的暴露,从而破坏应用程序通信通道的机密性和完整性。

通过 NDK 创建文件时指定权限

在 Android 应用程序中创建文件时的文件权限,尤其是使用native代码时。不正确的文件权限可能会导致安全漏洞,例如暴露敏感数据或允许未经授权的文件修改。

不合规代码示例:

不兼容的代码示例演示了使用本机 C 代码创建文本文件,而无需显式设置文件权限。这会产生一个世界可读且可写的文件,可能会导致未经授权的访问或修改。

Plaintext                  
FILE *fp = fopen("/data/data/com.mine.work/file.txt", "a");                  
fprintf(fp, "Don't alter this content.n");                  
fclose(fp);

兼容解决方案(设置 Umask):

在此合规解决方案中,用户显式设置进程的 umask,以确保创建的文件的权限与 Android SDK 的权限相匹配。通过使用 umask() C库调用,限制文件权限以防止全局可写访问

Plaintext                  
umask(002);                  
FILE *fp = fopen("/data/data/com.mine.work/file.txt", "a");                  
fprintf(fp, "Don't corrupt this content.n");                  
fclose(fp);

合规解决方案(指定文件权限):

另一个兼容的解决方案涉及使用系统调用显式指定文件权限 open() 。通过使用 S_IRUSRS_IWUSRS_IRGRPS_IWGRP 标志设置适当的权限,文件的访问权限仅限于用户和组,从而防止全局访问。    

Plaintext                  
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);

风险评估:

在native代码中创建文件时未能正确设置文件权限可能会导致未经授权的用户或应用程序访问或修改文件。这可能会导致敏感数据的暴露或损坏,给应用程序及其用户带来重大的安全风险。

敏感classes不能让自己被复制

防止复制包含私有、机密或敏感数据的类的重要性。未能定义适当的复制机制(例如复制构造函数)可能会导致安全漏洞,包括未经授权的数据访问或修改。

不合规代码示例:

在不兼容的代码示例中, SensitiveClass 定义了一个类,其中包含用于存储文件名的字符数组和用于管理共享访问的布尔变量。但是,该类缺少复制构造函数,如果该类复制不正确,则可能会出现潜在的漏洞。

Plaintext                  
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));                  
  }                  
}
       

恶意子类:

 MaliciousSubclass 创建 恶意子类,该子类扩展 SensitiveClass 并重写该 clone() 方法。该子类允许对敏感数据进行未经授权的访问和修改。

Plaintext                  
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                  
}

合规解决方案(Final Class):

声明 SensitiveClassfinal 防止子类化并确保该类无法被复制。

Plaintext                  
final class SensitiveClass {                  
  // ...                  
}
       

兼容的解决方案(Final clone()):

通过提供 final 始终 clone() 抛出 CloneNotSupportedException .

Plaintext                  
class SensitiveClass {                  
  // ...                  
  public final SensitiveClass clone() throws CloneNotSupportedException {                  
    throw new CloneNotSupportedException();                  
  }                  
}

风险评估:

未能防止敏感类的复制可能会导致未经授权的数据访问或修改,从而导致安全漏洞。实施适当的复制机制或使类不可复制可以减轻这些风险并确保敏感数据的完整性。

              

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月28日01:25:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Attacking Android指北https://cn-sec.com/archives/2592463.html

发表评论

匿名网友 填写信息