许多组织,特别是财务组织,必须处理各种安全标准,例如PCI DSS。 此类认证需要数据加密。 磁盘上的透明数据加密透明数据加密是在许多工业DBMS中实现的。
Apache Ignite用于银行,因此决定在其中实施TDE。
我将描述我们如何通过社区,通过Apachev流程公开开发TDE。
以下是报告的文本版本:
我将尝试讨论体系结构,开发的复杂性以及它在开源中的真实外观。
已经做了什么,还有什么要做?
当前实现的Apache Ignite TDE。 阶段1。
它包括使用加密缓存的基本功能:
- 密钥管理
- 创建加密的缓存
- 将所有缓存数据以加密形式保存到磁盘
在阶段2中,计划启用万能钥匙旋转(更改)的可能性。
在阶段3中,可以旋转缓存键。
术语学
- 透明数据加密-保存到磁盘时对用户透明的数据加密。 对于Ignite,请使用缓存加密,因为Ignite与缓存有关。
- Ignite缓存-Apache Ignite中的键值缓存。 缓存数据可以保存到磁盘
- 页面-数据页面。 在Ignite中,所有数据都是分页的。 页面被写入磁盘,并且必须被加密。
- WAL-预写日志。 Ignite中的所有数据更改都保存在那里,我们对所有缓存执行的所有操作也是如此。
- 密钥库-标准的Java密钥库,由keytool Javascript生成。 它有效并且在任何地方都经过认证,我们使用过它。
- 主密钥-主密钥。 使用它,表的密钥被加密,缓存加密密钥。 存储在Java密钥库中。
- 缓存密钥-实际用于加密数据的密钥。 与主密钥一起,获得了两级结构。 主密钥与密钥缓存和主数据分开存储-出于安全目的,访问权限分离等。
建筑学
一切都按照以下方案实现:
- 所有缓存数据均使用新的Encryption SPI进行加密。
- 默认情况下,使用AES-一种工业加密算法。
- 主密钥存储在JKS文件中-JKS是密钥的标准Java文件。
银行和其他组织使用自己的加密算法:GOST和其他算法。 显然,我们提供了滑动Encryption SPI的机会-特定用户需要的加密实现。
工作计划

因此,我们有RAM-随机存取存储器,其中的页面包含纯数据。 使用RAM意味着我们不受黑客的保护,因为该黑客获得了root访问权并转储了所有内存。 我们保护自己免受管理员破坏,该管理员拿走硬盘并在Tushino市场(或当前正在出售相似数据的地方)上出售它。
除了具有高速缓存的页面之外,数据还存储在预写日志中,该日志将磁盘中事务中更改的记录的增量写入磁盘。 元存储存储高速缓存加密密钥。 并在一个单独的文件中-主密钥。
每次创建缓存的密钥时,在写入或传输到网络之前,我们都会使用主密钥对该密钥进行加密。 这样,在收到Ignite数据后,没有人可以获得高速缓存密钥。 只有同时窃取主密钥和数据,您才能访问它们。 这不太可能,因为访问这些文件需要各种权限。
动作算法如下:
- 在节点的开头,从jks中减去主密钥。
- 在节点的开头,读取元存储并解密缓存键。
- 在集群中加入节点时:
-验证主密钥哈希。
-检查共享缓存的键。
-保存新缓存的密钥。
- 动态创建缓存时,我们会生成一个密钥并将其保存在元存储中。
- 在读/写页面时,我们将其解密/加密。
- 加密缓存的每个WAL条目也被加密。
现在更详细:
在节点的开始,我们有一个回调,启动我们的EncryptionSPI。 根据参数,我们从jks文件中减去主密钥。
接下来,当Metastore准备就绪时,我们将获取存储的加密密钥。 在这种情况下,我们已经有一个主密钥,因此我们可以解密密钥并获得对缓存数据的访问权限。
另外,有一个非常有趣的过程-我们如何将新节点加入集群。 我们已经有一个由几个节点组成的分布式系统。 如何确保新节点配置正确,它不是攻击者?
我们执行以下操作:
- 当新节点到达时,它将从主密钥发送哈希值。 我们认为它与现有的匹配。
- 然后,我们验证共享缓存的密钥。 缓存标识符和加密的缓存密钥来自节点。 我们检查它们以确保使用相同的密钥对所有节点上的所有数据进行加密。 如果不是这样,那么我们根本无权让该节点进入群集;否则,它将按密钥和数据行进。
- 如果新节点上有任何新的密钥和缓存,请保存它们以备将来使用。
- 动态创建高速缓存时,提供了密钥生成功能。 我们生成它,将其保存在meta存储中,并可以继续执行上述操作。
第二部分是I / O操作的上层结构。 页面被写入分区文件。 我们的加载项查看哪个页面缓存,对其进行相应的加密并保存。
WAL也是如此。 有一个序列化程序可以对WAL记录对象进行序列化。 如果记录是用于加密的缓存,则我们必须对其进行加密,然后再将其保存到磁盘。
发展困难
所有或多或少复杂的开源项目共有的困难:
- 首先,您需要完全了解Ignite设备。 为什么,在哪里以及如何完成,如何以及在何处附加处理程序。
- 有必要提供向后兼容性。 这可能非常困难,并不明显。 在开发其他人使用的产品时,您需要考虑到用户想要毫无问题地进行更新。 向后兼容是正确的,也是很好的。 当您对TDE进行如此大的改进时,您将更改保存到磁盘的规则,并对某些内容进行加密。 并且必须向后兼容。
- 另一个不明显的地方与我们系统的分布有关。 当不同的客户端尝试创建相同的缓存时,您需要就加密密钥达成一致,因为默认情况下会生成两个不同的密钥。 我们已经解决了这个问题。 我将不作更详细的介绍-该解决方案值得一提。 现在,我们保证使用一把钥匙。
- 当似乎一切都准备就绪(一个熟悉的故事?):)时,下一个重要的事情导致了巨大的改进。 加密有开销。 我们有一个初始化向量-AES算法中使用的零随机数据。 它们以开放形式存储,在它们的帮助下,我们可以增加熵:同一数据在不同的加密会话中将以不同的方式进行加密。 粗略地说,即使我们有两个姓氏相同的伊凡·彼得罗夫(Ivan Petrov),每次加密时,我们也会收到不同的加密数据。 这减少了被黑客入侵的机会。
加密以16字节的块进行,如果数据未按16字节对齐,则添加填充信息-实际加密了多少数据。 在磁盘上,您需要写一个2 Kb的倍数的页面。 这些是性能要求:我们必须使用磁盘缓冲区。 如果我们写的不是2 Kb(不是4或不是8,取决于磁盘缓冲区),那么我们将立即获得很大的下降性能。
我们如何解决这个问题? 我必须爬到RAM中的PageIO中,并从每个页面切掉16个字节,当写入磁盘时会对其进行加密。 在这16个字节中,我们编写了初始化向量。
- 另一个困难是不破坏任何东西。 当您进行一些更改时,这是很平常的事情。 实际上,它并不像看起来那样简单。
- 在MVP中,结果是6000行。 很难审核,很少有人愿意这样做,尤其是那些没有时间的专家。 我们有各个部分-公共API,核心部分,SPI管理器,页面持久存储,WAL管理器。 各个子系统中的更改要求它们由不同的人员进行审查。 这也带来了额外的困难。 特别是当您在一个所有人都忙于其任务的社区中工作时。 但是,一切对我们来说都是成功的。
TDE.Phase 2和3
现在已经实施了第1阶段,您作为开发人员可以为第2阶段提供帮助。 像其他标准一样,PCI DSS需要加密系统的其他功能。 我们的系统应该能够更改主密钥。 例如,如果他受到攻击或时间刚好符合安全策略。 现在,Ignite不知道如何。 但是在将来的版本中,我们将教TDE更改主密钥。
具有更改缓存键的功能,而无需停止群集和处理数据的功能相同。 如果缓存是长期存在的,并且同时存储了一些数据(财务数据,医疗数据),则Ignite应该能够更改缓存加密密钥并即时重新加密所有内容。 我们将在第三阶段解决此问题。
总计:如何在开源项目中实现重要功能?
总结一下。 它们将与任何开源相关。 我参与了Kafka和其他项目-故事都是一样的。
- 从小任务开始。 永远不要尝试立即解决一个超大问题。 有必要了解正在发生的事情,正在发生的事情,正在被实现的情况。 谁来帮助您。 通常来说-从哪一方面来处理这个项目。
- 了解项目。 通常,所有开发人员-至少我-都会说:一切都需要重写。 一切在我面前都是糟糕的,现在我将其重写-一切都会好起来的。 最好推迟这样的声明,以弄清楚到底是什么不好,以及是否需要更改。
- 讨论是否需要改进。 我曾经有过一些经验丰富的案例,例如在Spark中来到各个社区。 他告诉我,但是社区出于某种原因不感兴趣。 无论如何都会发生。 您需要此修订,但是社区表示:不,我们不感兴趣,我们不会合并和帮助。
- 进行设计。 在开源项目中,这是强制性的。 未经委员会和经验丰富的人员同意,您不能开始编码。 在Ignite中,这在形式上并不是正确的,但总的来说,它是开发的重要组成部分。 根据项目的不同,有必要用英语或俄语进行描述。 这样就可以阅读文本,并且可以清楚地知道要执行的操作。
- 讨论公共API。 主要论点是:如果有一个易于使用的美观且易于理解的公共API,则设计是正确的。 这些东西通常彼此相邻。
还有一些不那么容易理解的更明显的技巧:
- 在不破坏任何内容的情况下实现该功能。 做测试。
- 询问并等待(这是最困难的),以征求合适的人,合适的社区成员的意见。
- 制定基准,找出性能是否下降。 在完成一些关键子系统时,这一点尤其重要。
- 等待合并,执行一些示例和文档。
感谢您的阅读!