在NodeJS中使用加密货币API的挑战

导致NodeJS应用程序不安全的主要原因之一是加密货币API的不安全或使用不当。对此类API和底层加密货币概念不太熟悉的开发人员通常会难以选择安全的配置选项,甚至无法启动并运行其代码。本文假定读者熟悉以下概念:

  • (加密货币)密钥:密钥是用于加密货币和解密数据的一条信息,可以是独立的(对于对称加密货币,意味着加密货币和解密均使用同一密钥执行)或密钥的一部分-pair,其中专用密钥用于加密货币数据,而相应的公用密钥用于解密此数据(更多信息)。
  • 初始化向量:(理想情况下)一个防止重复进行数据加密的随机数(请参阅此博客文章或(stackoverflow网站上的更多内容)。
  • SALT:SALT是加密货币过程中使用的随机数据,以确保相同的输入并不总是导致相同的输出。阅读更多有关什么是加密货币SALT以及与iv有何区别的更多信息。
  • 密码(Cipher):一种算法,可以按照特定协议对数据进行加密和解密。

让我们从一个例子开始:马特(Matt)是一家中型公司的开发人员,还没有获得很多密码学方面的经验。他学习了一些概念,并了解了某些密码学算法的优缺点,但是在应用这些算法时,他仍然是一个初学者。现在,他的项目负责人已为他分配了一项需要加密的任务。任务是这样的:“我们应该对系统中的每条短信都保持机密。对它们进行加密以进行存储,以便以后在需要时可以对其进行解密。我们需要尽快使用此加密货币功能。”马特从Google搜索开始,读取了一些堆栈溢出帖子使他指向Crypto模块中的Cipher对象。大多数密码算法使用两条信息,即秘密密钥和初始化向量(iv)。 Matt打开编辑器,然后开始编写以下JS代码:const crypto = require('crypto');

const key = process.env.PRIVATE_KEY;
const iv = Buffer.alloc(8,0);
const algorithm ='des';

const cipher = crypto.createCipheriv(algorithm,key,iv);

cipher.update('第一个传入的数据');
cipher.update('传入的第二个数据');

让加密货币= cipher.final();

//其他地方,稍后
常量解密器= crypto.createDecipheriv(算法,密钥,iv);

cipher.update(加密货币);

在前几行中,从环境变量中检索密钥,创建一个缓冲区以用作iv并选择了密码算法。接下来,创建密码对象,然后使用应该加密的数据进行更新。第12行的调用完成了加密货币,并将结果存储在变量中。为了解密该数据,使用相同的算法,密钥和iv创建解密对象。然后用加密的数据更新该解密对象,并再次使用存储在变量中的(再次)未加密的数据来完成解密。这当然肯定不会没有错误,但是会导致“无效的密钥长度错误”。使用密钥对数据进行加密的密码算法需要一定长度的密钥,具体取决于所选择的密码算法.Mat经过大量研究后发现,密钥的长度必须与算法的块长度相同。稍后,他找到了scryptSync函数,该函数从密码和随机SALT中导出特定长度的密钥。然后,他调整自己的密钥并达到以下要求:const key = crypto.scryptSync(process.env.PRIVATE_KEY,'salt',16);现在,密码将起作用。 Matt存储加密的结果并测试解密,这会产生以下错误:错误:06065064:数字信封例程:EVP_DecryptFinal_ex:不良解密有经验的用户知道发生此错误是因为他没有将更新调用的结果串联起来。这导致数据长度错误,无法正确解密。但是,对于没有经验的Matt来说,这看起来像胡言乱语,这会让他头疼一段时间。最后,Matt会发现他必须将更新和最终调用的所有结果串联起来,并相应地调整代码:const cipher = crypto.createCipheriv(算法,密钥,iv);

让加密货币= cipher.update('第一部分进来');
加密货币+ = cipher.update('进来的第二部分');
加密货币+ = cipher.final();
 
//其他地方,稍后
常量解密器= crypto.createDecipheriv(算法,密钥,iv);
 
让解密=解密器。更新(加密货币);
 
解密+ =解密器.final();不幸的是,Matt收到了一个新错误:错误:0606506D:数字信封例程:EVP_DecryptFinal_ex:错误的最终块长度在进行了一些研究之后,他发现默认情况下,更新函数的输入被视为缓冲区,但是Matt使用的是字符串。然后,他还意识到,他可以设置输入的编码和所需的输出,以告诉NodeJS将输入视为字符串并以给定的编码返回字符串。调整后,代码终于可以正常工作,如下所示:const crypto = require('crypto');
 
const key = crypto.scryptSync(process.env.PRIVATE_KEY,'salt',8);
const iv = Buffer.alloc(8,0);
const algorithm ='des';
 
const cipher = crypto.createCipheriv(algorithm,key,iv);
 
让加密货币= cipher.update('第一部分进来','utf8','hex');
加密货币+ = cipher.update('传入的第二部分','utf8','hex');
加密货币+ = cipher.final('hex');
 
//其他地方,稍后
常量解密器= crypto.createDecipheriv(算法,密钥,iv);
 
让解密的=解密器。更新(加密的,'十六进制','utf8');
 
解密+ =解密器.final('utf8');在第3行中,密钥是从环境变量,SALT和所需长度中得出的。然后分配一个长度为16字节的缓冲区以用作iv,并指定要用于加密的算法。然后创建密码并使用应创建的数据进行更新。由于设置了编码,因此在加密货币之前和之后,数据输入均被视为字符串。在最后一次通话之后,Matt收到了存储在变量中的加密货币数据。以后,将创建解密对象并使用加密货币数据进行更新。然后再次设置编码,以确保正确处理数据。在最后一次调用之后,Matt检索存储在变量中的解密数据。

最后,加密货币功能似乎可以正常工作,但是它安全吗?

简短的答案是“否”:salt是纯文本格式,不是随机的,初始化向量也不是随机的,算法比des安全得多,依此类推。但是,马特(Matt)已经花了很多时间来解决使密码代码正常工作所带来的挑战,如果他本可以告诉API他想先加密货币数据然后在以后解密的话,那会容易得多。搜索(安全)算法,而不必了解密钥和iv必须保留多长时间,并且在出现问题时提供更多有用的错误消息。在下一篇文章中,我们讨论Fluent加密货币如何实现这一点。 :https://interestingengineering.com/11-cryptographic-methods-that-marked-history-from-the-caesar-cipher-to-enigma-code-and-beyond

资讯来源:由0x资讯编译自HACKERNOON。版权归作者所有,原文链接:https://hackernoon.com/a-glimpse-into-the-challenges-of-working-with-cryptography-apis-in-nodejs-kn12275i。未经许可,不得转载
提示:投资有风险,入市需谨慎,本资讯不作为投资理财建议。请理性投资,切实提高风险防范意识;如有发现的违法犯罪线索,可积极向有关部门举报反映。
你可能还喜欢