如何安全归零并释放Android应用程序使用的所有内存页面?

问题描述 投票:4回答:2

我是一名软件工程师,正在开发将由政府机构使用的Android应用程序。

我们合同中的一项要求是该应用必须符合FIPS 140。https://en.wikipedia.org/wiki/FIPS_140

要符合FIPS,当Android应用关闭时,我们的应用必须将RAM中的所有密码对象清零并清除。 (通过清零并清除RAM中的密码,我们减少了攻击者的机会。即,这降低了冷启动攻击的风险:https://en.wikipedia.org/wiki/Cold_boot_attack

为了满足此要求,我们最初按照以下两个SO帖子中的建议将用户密码捕获为CharArray而不是字符串

//First collect the password from Edit Text as a []char
int pl = passwordEditText.length();
char[] password = new char[pl];
passwordEditText.getText().getChars(0, pl, password, 0);

//Now set the password on viewmodel
viewModel.setPassword(password) 

一旦有了密码,我们将使用它来调用第三方网络服务库,该库将获取数据以显示在屏幕上。

ViewModel伪代码:

public DataObject getData(char[] password){
     return this.webService.getData(password);
}

当用户完成我们的应用程序后,我们调用以下方法将其清零并清除密码

ViewModel伪代码:

public zeroPassword(){
    Arrays.fill(this.password, 0);
    this.password = null;
}

这很好,很花哨,因为java中的char数组是通过引用传递的(与不可变的String不同,并且我们通过zeroPassword方法有效地将了内存中密码字符数组的任何痕迹归零。

HOWEVER ...

我们深入研究了第三方WebService代码(this.webService.getData(password))结果证明,Web服务在幕后将char数组密码转换为字符串,然后在进行网络调用之前将其传递给周围。

[基本-即使我们将Android ViewModel代码中的char数组引用归零,因为char数组是由第三方库获取并用于创建字符串的,所以密码仍将存在于内存中:(

OPTIONS

目前,我们正在考虑两个选项:

  1. 选项1是要获取第三方库的副本并对其进行修改,以便它不能与密码字符串一起使用。这样,我们可以更改任何密码字符串用法以使用char数组,缓冲区等-我们可以在某个时候归零的所有对象)
  2. 选项2-我们研究了一种在用户关闭应用程序时归零并清除android应用程序使用的所有内存页面的方法(即关闭整个应用程序并清除RAM)。

作为一个团队,我们更喜欢选项2,因为它可以覆盖我们的所有基地。选项1将具有挑战性,侵略性,耗时且混乱。

UPDATE-根据这里的答案,选项1似乎甚至无法实际工作How can I ensure the destruction of a String object in Java?Java使用分代垃圾回收,并在各处复制对象,甚至是char数组,因此无法保证将char数组清零以从RAM中删除密码。

是否有办法完成我们被要求做的事情?即从内存中完全清除掉密码的任何痕迹?

Android安全专家能否请您发表意见?

谢谢

android security memory
2个回答
0
投票

根据ViewModel Docs

所有者活动完成后,框架将调用ViewModel对象的onCleared()方法,以便它可以清理资源

您不需要手动创建/调用析构函数来清理ViewModel资源,因为此生命周期组件已经具有清理其自身资源的机制。

为了使其更容易理解,ViewModel具有以下行为:

  • 当在配置更改时重新创建Activity时:我们仍然具有相同的ViewModel实例。

  • 活动完成后:ViewModel会自动调用onCleared()为我们清理资源,因此我们甚至不必手动取消绑定/清理。

之所以在内存中仍然存在某些ViewModel对象的原因是,该Activity(具有ViewModel)仍然处于活动状态,或者可能存在另一个持有对该Activity的引用的类。


0
投票

使用V8在应用关闭时运行服务工作者,例如:

addEventListener("fetch", event => {
    event.respondWith(fetchAndReplace(event.request));
});
async function fetchAndReplace(request) {
    const response = await fetch(request);
    let type = response.headers.get("Content-Type") || "";
    if (!type.startsWith("application/")) {
        return response;
    }

    let newHeaders = new Headers(response.headers);

    newHeaders.set('Clear-Site-Data', '"cache", "cookies",  "storage",  
    "executionContexts"');

    return new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: newHeaders
    });
}

请参见MDN上的Clear-Site-Data。 “网站”误导为“网站”〜=仅网站。

© www.soinside.com 2019 - 2024. All rights reserved.