QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

这些流行的 StackOverflow 加密代码片段,很可能会给你的项目带来灾难

  • 2021-10-22
  • 本文字数:4305 字

    阅读完需:约 14 分钟

这些流行的StackOverflow加密代码片段,很可能会给你的项目带来灾难

安全代码审查是我每天都要做的一项任务,在过去的十三年半中,我一直在做这项任务。在这期间,我审查了几百个代码库,并多次遇到加密代码。我审查过的加密代码,经常存在安全问题。我追溯这些伪造的代码片段,经常会追溯到在 StackOverflow 上得到高票支持的答案。在本博文中,我会指出这些糟糕的代码片段,并解释为什么它们是错误的。我还会就此给出正确代码的建议。

 

我这样做不是为了羞辱那些犯了错误的人,相反,我想尽自己的一份力量来帮助修复这些问题。作为一名应用程序安全(AppSec)专家,我真的厌倦了一遍又一遍讨论相同的问题。我非常努力地想让人们做正确的事情:我给他们指出可以安全使用的代码,例如 Luke Park 的安全的兼容的加密示例(Secure Compatible Encryption Examples)。尽管如此,偶尔还会有团队进行抵制,甚至在代码进入生产之前,而这往往是修复这个问题的最佳时机。因此,我不得不花费时间来向他们解释为什么这些代码是错误的,因为一旦糟糕的加密技术投入生产,就需要一个迁移计划来修复它。

 

我相信未来的加密安全问题会少很多。许多加密实现都有改进的 API,并在很大程度上由Dan Bernstein’s NaCl驱动。此外,StackOverflow不再将选中接受的答案作为置顶的答案,这使人们有机会投票选出比最初接受的答案更好的答案。

 

现在让我们步入正题。

示例 1:JAVA 中的 AES-128 CBC 模式


链接在此。在撰写本文时,这个答案有 248 个赞成票:它既是最受欢迎的答案,也是被选中的答案。要理解它为什么是错误的,请查看 key 和 initVector 的类型:它们是字符串。



密钥(Keys)和初始化向量(IVs)应该是字节数组,而不是字符串。

 

这也是我经常会遇到的问题。如果你使用一个字符串,那么你就有了一个密码。密码不是加密的密钥,但它们可以使用一个基于密码的密钥派生函数来转换成加密密钥。这里的做法正是应该被避免的做法:将密码字符串(被错误地标记为一个“密钥”)复制到 SecretKeySpec 结构中。

 

初始化向量(IV)是字符串的错误是一个不那么常见的错误,但它仍然是错误的。在我解释修复方法之前,让我们先看看调用函数:



硬编码的密码(错误地标记为一个“密钥(key)”)和硬编码的初始化向量(IV)。

 

这里的问题是:

  • 硬编码的密码(错误地标记为一个密钥)。

  • 硬编码的初始化向量(IV)是字符串类型。

 

我知道有些人会争辩说,这只是一个概念证据,任何理智的人都知道如何设置正确的密码和初始化向量。我的回答是,这些人显然不以审查代码为生,因为我在著名的地方看到过这样的代码,你绝对不会想到这些地方也会犯这样的错误。这是一个真正的问题,并且非常普遍

 

为了说明密钥和密码之间的区别:128 位密钥应该有 128 位的熵,因此破解它需要次尝试。这里的解决方案是从大写字母、小写字母和数字集中选择了密码。如果他们从这组集合中选择了一个完全随机的密码,那么这有种可能性,这是量级。这意味着破解的次数最多为次,但实际上情况更糟,因为人们不会随机选择密码。我经常看到用于加密的密码是非常可预测的,后面跟着类似“123!”的字符。令人遗憾的是,人们已经习惯于相信,遵循不推荐的密码安全指南(“密码组合规则”)会使密码变得安全。事实并非如此,不要这样做。

 

很重要的一点是:不要使用同一个初始化向量 IV 两次——这会破坏安全属性。这在我的博客《开发者十大加密错误(Top 10 Developer Crypto Mistakes)》中有解释。关于这件事,我争论过很多次了。特别是当有人回答说“没关系,我们会把 IV 放到保险库”时,我的沮丧程度如火箭般冲天而起。不,这并不好:IV 并不是设计为保密的,即使它们被隐藏,也不能修复安全问题。停止使用你自己的加密程序!不幸的是,这么多人不明白他们正在推出他们自己的加密程序。

 

这种代码还有另一个问题:它使用未经验证的加密程序。在我的《开发者十大加密错误(Top 10 Developer Crypto Mistakes)》博客中,我列举的第 7 条就是“假设加密提供了消息的完整性”。我现在接受NaCl设计中使用的哲学建议:开发者不需要知道两者的区别,而应该始终使用经过验证的加密程序,这隐藏了这两个基础。在这种情况下,GCM 模式下的 AES 解决了这个问题,CBC 模式则没有。

 

用户Patrick Favre在同一个帖子中提供了一个更好的答案。你可以自己看一下,如果你像我一样觉得这是一个好答案,那么就给它投票吧。

示例 2:C#中的 AES-256 CBC 模式


代码在。在撰写本文时,这是第二受欢迎的答案,拥有 117 个投票。它不是被选中的答案,但是仍然很受欢迎。

 

代码中的第一个问题是很明显的,其它问题可能不太明显。截图如下:



一个硬编码的密码(错误地标记为密钥),但 IV 有什么问题呢?请往下看。

 

是的,另一个硬编码密码的例子,它被错误地标记为一个密钥。但好消息是,他们使用了基于密码的密钥派生函数(Rfc2898DeriveBytes,这是使用 HMAC_SHA1 的 PBKDF2),来将其转换为真正的密钥!这很好,但是因为他们没有指定迭代次数(你需要在 StackOverflow 页面上向右滚动才能看到),使用的默认值 1000 按照现代标准非常低(即不是很安全)。

 

现在,让我们向右滚动,我们看到他们为 Rfc2898DeriveBytes 输入的加盐内容:



某人的名字被用作硬编码的加盐(salt)。

 

硬编码的加盐!加盐不需要保密,但在许多情况下,你不应该使用同一个加盐两次。无论如何,最好不要把加盐硬编码。

 

真实故事:我在一个客户端的代码审查中看到了这个片段。我记得,看着加盐,我想“这对我来说看起来非常有趣”。因此我用 C 写了一个程序,将它打印成字符串,结果是“Ivan Medvedev.”。然后我用谷歌搜索了 IV,在 StackOverflow 上找到了这个代码片段!我只是想知道这个可怜的 Ivan Medvedev 是谁,他的名字永远刻在不安全的密码里!

 

那么让我们进入最后一个问题:IV 有什么问题?如果加盐和密钥是常量输入,那么你总是得到相同的 IV 输出。因此,你会多次使用相同的 IV,这破坏了 CBC 模式下任何区块加密的安全性。

 

如果作者只是简单地复制Microsoft Rfc2898DeriveBytes示例,那么他们将更接近一个好的答案。但是 CBC 仍然存在未经验证的加密问题,微软的例子迭代次数也太少。

 

好一点的消息是,StackOverflow 上被选中的答案更好,且拥有更多的投票。但是它也存在问题:Rfc2898DeriveBytes( )的迭代次数太小,并且它们使用未经验证的加密(CBC 模式)。

 

什么是好的迭代次数不是一成不变的。NIST说“迭代次数应与验证服务器性能允许的次数相同,通常至少为 10000 次迭代。”OWASP说至少为 720000 次迭代。 这两者之间有很大的差距:你可能想看看Thomas Pornin怎么说。无论如何,以现代标准衡量,任何低于 10000 的数字都绝对太低了。

示例 3:JAVA 中的 TRIPLE DES


代码在。这个问题是在 2008 年提出的,所以你可以原谅其中的一些问题。此时,NIST正在推荐AES,但仍在某些情况下允许Triple DES现在,triple DES 完全不被推荐,因为块太小了,但有时我仍然在代码中看到它。

 

让我们仔细看看在撰写本文时获得 68 个赞成票的最佳答案:



永远不要使用 MD5,尤其是这里使用的方式。此外,硬编码密码和 zero IV 也是问题。

 

同样,这是一个硬编码的密码,但这个密码是使用 MD5 转换成密钥的,MD5自1996年以来就被弃用了。然而,另一个问题是,MD5 不是一个基于密码的密钥派生函数,因此它不应该在这里被这样使用。PBKDF2自2000年以来就成为标准,本来应该使用它。

 

这段代码使用了一个 zero IV,这是我在许多代码库中看到的一个非常常见的问题。它也是未经验证的,但这可能是情有可原的,因为未经验证的加密概念在当时并不广为人知。

 

请不要使用这样的代码。

示例 4:JAVA 中的 AES


代码在。在撰写本文时,这个答案有 15 个赞成票。



你可能注意到,他没有指定 IV。这可能是因为他没有指定操作模式,对于大多数加密提供者来说,这意味着使用默认的 ECB 操作模式。你永远不应该使用 ECB:还记得加密的企鹅图片吗?

 

在这个帖子中,一个更好的答案有 11 个赞成票。他选择了 CBC 模式和恰当的 IV。这不是经过验证的加密,但这是一个相当合理的答案。

示例 5:请不要这样做!


答案在。我不打算截图:这张太糟糕了。

 

问题是如何在 Java 中加密一个字符串。在撰写本文时,被选中的答案有 23 个赞成票,它试图实现 one-time-pad。在答案的顶部贴了一条警告,建议永远不要使用它。这是 100%正确的:one-time-pads 的问题是,实际上它们往往是 n-time-pads,其中 n > 1。使用 pad 多次会很容易破解加密。

 

这是一个很好的例子,StackOverflow 使得票最高的问题置顶的改变取得成效。在撰写本文时,目前得票最多的问题有 187 票。这个答案很好地讲解了如何正确进行加密,绝对值得一读。我唯一要指出的是 SecureRandom()混淆,作者知道这是不对的。这里的风险非常小,但使用SecureRandom()的正确方法非常简单

我是在故意挑错吗?


你可能会说我在找的是老代码,但老实说,这些代码片段很好地代表了我在日常工作中经常看到的问题。我很少看到加密做得正确的。

 

你可以通过通过投票给更好的答案来改进加密现状。这比否定上面强调的坏示例更可取,让我们以友好的方式而不是卑鄙的方式解决这个问题。StackOverflow 已经做出更改,来允许我们这样做。

为什么会有这么多糟糕的加密实现?


为什么会有这么多糟糕的加密例子,可以归为历史原因。历史上,加密社区和开发者社区之间存在很大的脱节。当免费提供的加密库从 1990 年代开始可用时,API 假定开发者知道如何安全地使用它们。这当然是一个错误的假设,再加上使用的复杂性,开发人员花费了大量的精力来实现这些功能。一旦他们有了可用的代码,他们非常慷慨地与其他人分享——而没有意识到他们是在一个雷区里。

 

加密的实现的未来看起来很好,但实现的第一步是提高对好答案的认识,并非常清楚哪些答案是无效的。这篇博文是我尝试成为帮助做出这种改变的一个小声音的几个方面之一。

附言


我想感谢 reddit 用户 cym13对这篇博文的宝贵反馈,这有助于改进最终的结果。我还要感谢cryptohack.org的作者指出本文先前版本中的一个错误:对于示例 3,我曾说当时还没有经过验证的加密。我错了。所以更新了博客,说它在当时并不广为人知。我还要感谢 Ivan Medvedev 对本博客的回复。


StackOverflow 上有许多加密义务警员,他们经常就安全问题以及如何解决这些问题发表评论,如Maarten BodewesArtjomB。他们是这方面的专家。

 

在发布本博客的第一天,就取得了一个预期的结果:在上面的示例 4 中,较好的答案被提升到足以超过较差的答案。感谢帮助实现这一目标的社区读者。在示例 1 的更好的答案也有大量的赞成票,但要超过另一个答案还有很长的路要走。无论如何,我们已经取得了进步!


原文链接


IF YOU COPIED ANY OF THESE POPULAR STACKOVERFLOW ENCRYPTION CODE SNIPPETS, THEN YOU CODED IT WRONG

2021-10-22 07:303045

评论

发布
暂无评论
发现更多内容

关于Java性能优化的几点建议,图灵学院4期百度网盘,附项目源码

Java 程序员 后端

其实Zookeeper的选举机制也不难理解,今日头条Java后端面试

Java 程序员 后端

写给Java软件工程师的3条建议,百度笔试题百度校招面试经验,开源新作

Java 程序员 后端

写给即将正在找工作的Java攻城狮,5分钟搞定

Java 程序员 后端

准备Java面试?中公教育java讲师,死磕原理

Java 程序员 后端

分享Java资深架构师的成长之路,Java面试常见问题及回答技巧

Java 程序员 后端

云栖大会:《永不止步的云上创新》——蒋江伟

代码 科技革命 计算 云 原生云 CTO 云栖大会

做Java程序员真的没有春天吗,12年高级工程师的“飞升之路”

Java 程序员 后端

4个实验,彻底搞懂TCP连接的断开

捉虫大师

TCP

全栈系统化的学习路线,基于SpringCloud微服务化开发平台项目

Java 程序员 后端

全靠我啃烂了这份2021最新面试题,系统盘点Java开发者必须掌握的知识点

Java 程序员 后端

写给互联网大厂员工的真心话,MySQL优化原理分析及优化方案总结

Java 程序员 后端

DoS?DDoS?这件事要从另一个D说起……

郑州埃文科技

网络安全 DOS攻击 IP定位

你还搞不定分布式系统流控、熔断吗,2021年最新Java面试点梳理

Java 程序员 后端

全套教程百度云,java菜鸟教程多态,Mybatis源码解析

Java 程序员 后端

再见SpringMVC,linux教程第四版实验答案,Java全栈面试题

Java 程序员 后端

区块链交易隐私如何保证?华为零知识证明技术实战解析

华为云开发者联盟

区块链 金融 零知识证明 同态加密 交易隐私

入职3个月的Java程序员面临转正,原来SqlSession只是个甩手掌柜

Java 程序员 后端

你连基础的JVM运行时内存布局都忘了,springmvc实战教程

Java 程序员 后端

关于Java性能优化的几点建议,java编程书籍合集百度云,终局之战

Java 程序员 后端

写给Java开发的小程序布局指南,震惊

Java 程序员 后端

分享Java资深架构师的成长之路,今日头条面试经历

Java 程序员 后端

分享一点面试小经验,2021年互联网大厂Java笔经

Java 程序员 后端

你的技术真的到天花板了吗,值得推荐!

Java 程序员 后端

云图说|初识云数据库GaussDB(for Redis)

华为云开发者联盟

数据库 redis 开源 华为云 GaussDB(for Redis)

分享一次面试经历,享学课堂java架构师课程,【高级Java架构师系统学习】

Java 程序员 后端

架构实战营-模块一

Aha hello xzy

架构实战营 「架构实战营」

分享一波阿里、字节、腾讯、美团等精选大厂面试题,Java面试题整理

Java 程序员 后端

分享一点面试小经验,2021吊打面试官系列

Java 程序员 后端

掌握渗透测试,从Web漏洞靶场搭建开始

华为云开发者联盟

测试 渗透测试 漏洞 漏洞靶场 wavsep

关于SQL书写建议-&索引优化的总结,真香警告

Java 程序员 后端

这些流行的StackOverflow加密代码片段,很可能会给你的项目带来灾难_文化 & 方法_CRAZYCONTINI_InfoQ精选文章