我们将如何在Java中实现安全的静态登录凭证系统?
我们最近进行了一次安全审核,它暴露了此处安装的系统中的几个弱点。由此产生的任务之一是,我们需要更新我们的合作伙伴凭证系统以使其更加安全。
"旧的"处理方式是生成一个(错误的)密码,并使用ID将其提供给合作伙伴,然后他们将通过HTTP将其ID和该密码的Base 64编码副本连同其所有XML请求一起发送给伙伴。然后,我们对其进行解码并进行验证。
这些密码不会更改(因为我们的合作伙伴将不得不更改编码/配置来更改它们,并且在多个环境中与数百个合作伙伴协调密码到期将是一场噩梦),而且无需人工输入或者人类可读的。如果我们的合作伙伴有更好但相对简单的实施方式,我愿意对此进行更改。
基本上可以归结为两件事:我需要一个更安全的Java密码生成系统,并确保以安全的方式传输它们。
我已经找到了一些手动生成的密码生成器,但是没有什么能真正成为实现此目的的标准方法(也许是出于充分的理由)。与通过https进行简单的Base 64编码相比,还有一种更安全的传输方式。
我们将如何处理密码生成器,并且我们认为适当的传输方法足够安全?
编辑:XML出现在SOAP消息中,凭据位于标头中,而不位于XML本身中。另外,由于密码是对每个合作伙伴的一次性操作,因此在设置密码时,我们不必太担心生成器的效率。
解决方案
我们不能使用SSL密钥进行身份验证吗?
我不清楚审核小组为什么将通过HTTPS通过SSL传输密码视为"不安全"。因此,当我们要求两件事时,似乎第二件事-确保密码以安全的方式传输-已经被很好地处理了。
对于第一个,我们必须知道审核将密码公开为不安全的情况...
密码产生
就编码用于传输的密码而言,唯一能真正增加安全性的编码是加密。使用Base-64或者十六进制并不是为了安全,而是为了能够以XML之类的文本格式包含它。
熵用于衡量密码质量。因此,选择带有随机"抛硬币"的每个比特将为我们提供最佳质量的密码。我们希望密码与其他加密密钥一样强,所以我建议至少使用128位熵。
有两种简单的方法,具体取决于我们要将密码编码为文本的方式(从安全角度来看,这实际上并不重要)。
对于Base-64,请使用以下命令:
SecureRandom rnd = new SecureRandom(); /* Byte array length is multiple of LCM(log2(64), 8) / 8 = 3. */ byte[] password = new byte[18]; rnd.nextBytes(password); String encoded = Base64.encode(password);
以下内容不需要我们提供Base-64编码器。产生的编码不是那么紧凑(从26个字符代替24个字符),并且密码没有那么多的熵。 (但是130位已经足够了,相当于人类选择的至少30个字符的密码。)
SecureRandom rnd = new SecureRandom(); /* Bit length is multiple of log2(32) = 5. */ String encoded = new BigInteger(130, rnd).toString(32);
创建新的SecureRandom对象的计算量很大,因此,如果要频繁生成密码,则可能需要创建一个实例并将其保留。
更好的方法
将密码嵌入XML本身似乎是一个错误。
首先,在处理发送者发送给任何文档之前,我们似乎希望对发送者进行身份验证。假设我讨厌胆量,并开始向我们发送巨型XML文件以执行拒绝服务攻击。我们是否只需要解析XML才能发现我不是合法的合作伙伴?如果Servlet只是拒绝未认证用户的请求会更好吗?
其次,合法伙伴的密码在HTTPS传输过程中受到了保护,但是现在它们很可能以"明文"方式存储在系统中。那是不好的安全性。
更好的方法是在合作伙伴向我们发送带有HTTP请求标头中的凭据的文档时对它们进行身份验证。如果仅允许HTTPS,则可以将密码从文档中完全取出,然后将其放入HTTP"基本"身份验证标头中。它在传输过程中通过SSL进行保护,并且不会以明文形式存储在系统中(我们仅存储用于身份验证的单向哈希)。
HTTP基本身份验证简单易行,得到广泛支持,与SSL客户端证书相比,我们和合作伙伴将更容易实现。
保护文件内容
如果文档内容本身是敏感的,则实际上应该由发送者对其进行加密,并由我们以其加密形式进行存储。最好的方法是使用公钥加密,但这将是另一个问题。
我放弃了整个密码方法,而是开始使用允许2面身份验证的SSL连接的客户端证书。
我们必须为每个客户端生成并签署单独的证书。在SSL握手中,我们请求客户端的证书并进行验证。如果失败,则连接以401状态代码结束。
证书随时都可以在我们身边被吊销,从而可以轻松断开以前的客户。
由于所有这些都发生在通信之前的握手中,因此不可能用数据淹没服务器。