如何在不使用String的情况下从Java发送密码到Postgresql? 回答评论:

问题描述

我知道在密码方面,char[]String更可取,因为String是不可变的。但是我还无法弄清楚如何将其传递给准备好的语句。考虑以下代码:

Connection con = MyDBManager.connect();
PreparedStatement stm = con.prepareStatement(
    "INSERT INTO my_table(username,password) VALUES(? ?)");
String username = "Jonny";
stm.setString(username);
char[] password = new char[] {'a','b','c','d'};
stm.SetString(password.toString());

它有效,但是我怀疑调用password.toString()不能达到使用char[]的全部目的,那么我应该怎么做?

是的,我知道密码不应以明文形式存储。密码已散列,但我仍然想利用char[]

的好处

这似乎可行,并且我还将数据库中的字段从text更改为bytea,并且(我认为)理论上应该提供与假定的char[]相同的好处,但是我强烈感觉自己做的不对:

byte[] password = new byte[] {'a','d'};
stm.SetBytes(password);

也许我的方法是完全错误的,但是我的要求是密码哈希永远不应该存在于不可变的对象中。除了PreparedStatement

外,我还接受其他方式将其发送到数据库

回答评论:

我从@rzwitserloot的答案中学到了很多东西,但不幸的是,它没有帮助。永远不要在不可更改的对象中包含密码哈希,这是不可协商的要求。

解决方法

为什么密码采用char []形式?

我知道在密码方面char []比String更可取,因为String是不可变的。

如果您的意思是:所以现在您不能更改密码。那是不对的。

也许您指的是您不能将它们擦干净的事实”,但是以防万一,请允许我解释一下:

只有char[]比String更可取的原因,并且它是高度可疑的:有了char[],您可以“擦除”数组。也就是说,一旦您的进程(您的Java代码)不再需要密码,您就可以显式运行Arrays.fill(thePassword,(char) 0);,而RAM不再包含该密码。您不能使用字符串来执行此操作-您可以使参考无效,但这只是“清除藏宝图”。它不是在挖掘宝藏并将其粉碎成碎片。愿意挖掘整个海滩的人仍然会找到它。

这听起来不错,这是为什么一大堆基于密码的API用char[]而不用String处理的唯一解释。但是,作为一个原则这是非常可疑的,您绝对不应该依赖它:

  • 如果您无法信任的其他某个进程可以访问您的进程的内存,则您已经很忙了。您应该从源头上解决此问题,而不要尝试通过减少曝光来缓解此问题。这类似于您的动脉中有一个巨大的缺口孔,并通过钩住不断供应的血袋(战斗症状)而不是用绷带包扎(闭合实际孔)来固定它。如果某种程度上无法解决问题,我想战斗的症状总比没有好,但是这是一个糟糕的选择。
  • 在操作系统和CPU缓存之间,您没有真正的保证可以清除char数组,这保证了密码在黑客可能无法获取的硬件任何部分都不存在。

对于它的价值,我不会认为将密码中存储字符串的任何代码都不安全。实际上,我怕将代码以char []形式存储-我担心作者会误解它提供的保护,否则他们会忘记将其归零:这会使代码的读者做出错误的假设(即,那么密码将被清除掉,这可能不是正确的,这意味着如果有任何东西设法转储了内存内容,那么密码将无法恢复,这也可能不是真的。

注意:出于安全考虑,养成编写詹姆斯·邦德电影剧本的习惯是个好主意。这是给你的电影剧本:

您可以在虚拟PC上的云托管环境中运行服务器。这种云的运营商搞砸了,在删除服务器(实际上只是终止了在运行数百台PC的主机上运行的虚拟PC的终止)后,他们不会清除进程的内存强>。有人决定尝试滥用此方法:他们要求云托管者给他们一台装有Linux的PC,然后他们安装一个简单的应用程序,该程序扫描虚拟PC自己的内存中是否有任何看起来像密码的内容并报告,然后关闭。机器停机并终止它,并要求另外一个。

通过使用char[] AND 清除密码,您可以更安全地避免这种情况。.但是如果服务器只是崩溃或死机而已-这恰恰是例如应该使用Amazon EC2(请参阅netflix的混乱猴子文档,整个社区普遍认为这是正确的处理方法),在这种情况下,存在一些密码正巧处于char []阶段的风险。更不用说内存中的所有其他这些位置可能最终会进入。

看看债券级脚本如何帮助澄清问题?它显示出清除char数组有一些意义,但这并不是万无一失的。

如何在postgres中存储密码

散列也是不好的。至少取决于您对“哈希”一词的含义。

正确的方法涉及两件事:

  • 一个“盐”-问题是,很多人都使用iloveyou作为密码。不管使用哪种哈希算法,哈希算法的性质都将相同的输入哈希到相同的输出,因此,如果我完整地转储了数据库,我要做的就是运行SELECT passhash,COUNT(*) AS ct FROM accounts GROUP BY passhash ORDER BY ct DESC LIMIT 1,并且瞧-那个哈希?那是iloveyou,然后我可以做SELECT username FROM accounts WHERE passhash = ?,列表中的所有人?我可以通过密码iloveyou以他们的身份登录。就那么简单。一个盐解决了这个问题:这个想法是,您为每个帐户生成一个随机数(在创建帐户时),然后将该随机数存储在数据库中。您存储的哈希值是对值进行哈希处理的结果:CONCATENATE(盐,密码)。现在,每个白痴用户使用iloveyou作为密码时,都会得到不同的哈希值。要检查密码,请输入输入的密码,检索盐,重新创建CONCAT(盐,通行证),对其进行哈希处理,然后确认其与数据库条目匹配。

  • 您需要一种既缓慢又怪异的哈希算法。例如,SHA-256被比特币使用,因此有大量的专用计算机可以为SHA-256生成数十亿毫秒的哈希,并且那里有专用的硬件(您没有的硬件)它比您拥有的任何计算机都要快得多。不好,您不希望黑客拥有优势。因此,您需要一种哈希算法,该算法确实很慢,并且没有(轻松)针对自定义硬件进行优化。 BCrypt,SCrypt和PBKDF是为此常用的变体。它们都很好(BCrypt年龄较大,从某种意义上说“更差”,但也更简单且经过验证。选择想要的任何一个)。

  • 请注意,那里的大多数库已经可以处理整个盐业,无需显式生成和存储它们(这些库的API很简单:“给我一些东西可以放入数据库此密码”和“用户输入此密码,这是您告诉我之前放入数据库中的内容。是否与之匹配?”。“放入数据库中的内容”同时包含了salt和哈希结果组合成一个字符串。

好吧,那么我如何获得bcrypt库给我的postgres的“放入数据库的东西”?

它将通过电线或至少一个本地套接字进入postgres WAL日志,然后该日志随处可见。这个东西实际上也就在数据库中。

以某种方式可以从系统硬件中消除此哈希值的想法是完全不可能的

所以只需创建一个java.lang.String。很好。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...