今天在闲逛的时候突然发现了一篇文章:https://my.oschina.net/jasonultimate/blog/166968。讲了关于JAVA安全处理密码字符串的问题,很受触发。
很多时候我自己常常喊着安全安全,结果细节上还是不够注意。
当然,这篇文章谈的是纯JAVA环境,和安卓开发有所不同,但是也很有借鉴意义。
在之前的安卓客户端开发过程当中,我对于密码的操作就是使用EditText输入,然后getText().ToString()然后就用String拿到了。当然了,使用的都是局部变量,一般用完块也就结束了,靠GC回收处理。但是,确实没有考虑到在这个过程中密码实际上被明文存储了。
如果我用的是C/C++,直接操作内存覆写就完事了,压根没有这种问题。底层大法好!底层大法强!
百度了一下相关的文章不少,但是看着看着不免有一个问题,反正都是说String不能修改字符串巴拉巴拉,叫你用一个char[],还有的人只会吐槽“你想太多了”,又说有一个replace方法,难道这个方法就是读取原先的然后改一下再存吗?我感觉有一点问题呀。
我琢磨着开发JAVA的大佬不至于一边不给你改String一边给你提供一个Replace方法吧。
有问题,追踪源码。
String的replace方法:
public String replace(char oldChar, char newChar) {
// BEGIN Android-changed: Replace with implementation using native doReplace method.
if (oldChar != newChar) {
final int len = length();
for (int i = 0; i < len; ++i) {
if (charAt(i) == oldChar) {
return doReplace(oldChar, newChar);
}
}
}
// END Android-changed: Replace with implementation using native doReplace method.
return this;
}
可以明显看到,里面调用了doReplace方法,再找
// BEGIN Android-added: Native method to access char storage managed by runtime.
// Implementation of replace(char oldChar, char newChar) called when we found a match.
@FastNative
private native String doReplace(char oldChar, char newChar);
裂开哈哈哈,我是一个没有感情的native方法,必须要本地提供。
追踪到这里也是暂时告一段落,因为接下去不是源码的锅了,只能使用我的百度大法(没有VPN的可怜人),一搜,啥都没有,全是教你怎么用replace的,吐槽一下这个算法,我这明明是连起来的关键字好吗。
然后Bing,在一个叫geeksforgeeks(极客为极客?)的网站里找到这么一句对于replace的描述:
Return Value
It returns a string derived from this string by replacing every occurrence of oldch with newch.
这里面用了一个词叫derived,这不是继承、衍生吗?难道真的没有覆写原先的吗?回头去源文件一看:
/**
* Returns a string resulting from replacing all occurrences of
* {@code oldChar} in this string with {@code newChar}.
* <p>
* If the character {@code oldChar} does not occur in the
* character sequence represented by this {@code String} object,
* then a reference to this {@code String} object is returned.
* Otherwise, a {@code String} object is returned that
* represents a character sequence identical to the character sequence
* represented by this {@code String} object, except that every
* occurrence of {@code oldChar} is replaced by an occurrence
* of {@code newChar}.
* <p>
* Examples:
* <blockquote><pre>
* "mesquite in your cellar".replace('e', 'o')
* returns "mosquito in your collar"
* "the war of baronets".replace('r', 'y')
* returns "the way of bayonets"
* "sparring with a purple porpoise".replace('p', 't')
* returns "starring with a turtle tortoise"
* "JonL".replace('q', 'x') returns "JonL" (no change)
* </pre></blockquote>
*
* @param oldChar the old character.
* @param newChar the new character.
* @return a string derived from this string by replacing every
* occurrence of {@code oldChar} with {@code newChar}.
*/
确实是这么说的,居然是一个衍生的、继承的String。然后继续去找,,,此处很遗憾终究没有找到native的实现代码,所以暂时只能得出以下结论:String类不能够保证replace()会覆写原先内存地址的字符串,所以还是要用char[]来存储edittext获得的字符串为佳,用完直接做一点<<1或者其他覆写操作就可以了。
当然我在浏览过程当中也看到了更多的建议,比如说获取的时候先加密,然后一直加密存储,用的时候解密就完事了。这样,嗯,好像可以,但是目前我觉得还是没有必要。
今天的结尾写给自己:世界上没有绝对的概念,也没有绝对的安全,只有不同的安全等级。谨慎处理安全问题,衡量风险和成本,战略上高度重视,战术上因地制宜。