我需要在我的服务器上加密一些东西并保存结果。由于我不是安全专家,因此我想实现尽可能多的现有代码。
我在php.net上找到了一个完整的构建函数,但它说“不受填充 oracle 攻击的保护”。
什么是“填充预言机攻击”,我应该怎么做?
我需要在我的服务器上加密一些东西并保存结果。由于我不是安全专家,因此我想实现尽可能多的现有代码。
我在php.net上找到了一个完整的构建函数,但它说“不受填充 oracle 攻击的保护”。
什么是“填充预言机攻击”,我应该怎么做?
填充预言是协议缺陷和实现缺陷的混合体。也就是说,“填充预言机”通过对恶意制作的无效输入的反应泄露了一些关于秘密数据的信息。一个好的协议将首先通过MAC验证输入数据, 然后再考虑解密及其必然的填充处理。这就是“加密然后MAC”结构的意义所在。一个糟糕的协议会以错误的顺序做事,并且可能会因先应用解密而遭受泄漏;这就是在带有 CBC 加密模式的SSL中发生的情况。
如果协议不好,那么实现必须特别屏蔽攻击者试图手工制作的无效消息,以便将某些实现(例如服务器)变成填充预言。这种屏蔽是困难的(例如,幸运十三攻击在实验室条件下证明了即使采用完全屏蔽的实施方式也存在泄漏的理论可能性)。
您链接的 PHP 函数也仅与加密有关;根据定义,它不能对填充预言做任何事情。对填充预言的保护更多的是在协议级别。与 PHP 一样,危险的功能几乎没有警告,API 也不是很理想。
设计自己的安全协议比屏蔽侧信道攻击更难,所以不要那样做。即使你这样做了,也不要在 PHP 中这样做。
为了防止填充预言,您需要确保您的应用程序在填充错误时不会返回不同的错误。做到这一点的最佳方式是Encrypt-then-MAC 构造,其中将消息验证码 (MAC) 应用于密文。如果 MAC 发生故障,您甚至不需要查看填充。如果 MAC 是正确的,则在密码学上不太可能填充已被篡改。
以我的方式说,首先,密码学中的“神谕”是基于神话神谕的概念;一个与神有直接关系的人,因此可以获得普通人所没有的信息。在密码学中,预言机是“黑匣子”,可以执行攻击者难以完成的任务,因此可以为攻击者提供通常难以获得的信息。
具体来说,填充预言可以告诉攻击者他输入的消息是否被正确填充(大多数块密码模式使用某种形式的可移动数据填充来确保消息长度是密码块大小的精确倍数)。它通过尝试解密消息并在块被正确填充或未正确填充时表现出不同的行为来做到这一点,攻击者可以看到这种行为差异。填充预言机通常是不知情的参与者;攻击者通常劫持一个初始化的密码实现,在所谓的选择密文攻击中提供他选择的特制消息。
一种相关的攻击类型是定时攻击。通过测量确定块是否被正确填充所需的时间,这是可以将解密实现转变为填充预言的一种方式。一些密码模式,如 CBC,可以以任何顺序解密,因此一些实现将首先解密最后一个块以检查消息的填充,以尝试“快速失败”损坏的消息。但是,攻击者可以使用检查填充和尝试解密完整消息所需的时间之间的差异来确定两者中的哪一个发生了,即使在任何一种情况下向攻击者显示的错误都是相同的。
为了防范这些类型的攻击,在尝试解密未正确填充的消息与尝试解密消息之间必须没有明显的行为差异。最知名的方法是结合称为消息验证码的安全校验和。此 MAC 通常由安全的“密钥哈希”生成,使用加密消息的相同密钥。首先对消息进行加密,然后使用 MAC 算法和相同的密钥对密文以及有关如何加密的信息(例如密码算法、密码模式、密钥大小、块大小和 IV)进行哈希处理。
为了解密,算法首先根据两台计算机之间共享的密文和加密信息重新计算 MAC。如果计算出的 MAC 与密文中包含的 MAC 不匹配,则解密失败;消息、MAC 或两者都在传输过程中通过某种方式被更改,无论是良性的(数据损坏)还是恶意的(企图攻击)。
攻击者的侥幸心理,不知道密钥,能够改变密文,并以一致的方式其MAC被认为是困难的,我的意思大致1比2 ^(密钥)的机会,所以战略与简单地尝试所有可能的密钥相比,系统地更改消息效率会降低。此外,由于几乎总是花费相同时间的相同操作可以检测到任何会使消息无效的加密后数据问题,因此攻击者无法区分密文和 MAC 的任何系统更改所产生的差异; 它要么匹配(极不可能的;见上文)或它没有(太多可能性更大)。
Padding Oracle Attack 是一种边信道攻击,可用于解密 ECB 或 CBC 对称密码。这种攻击在密文解密期间泄漏有关填充的信息。为了防止这种情况,您可以向密文添加身份验证,例如使用 HMAC。最常用的技术是Encrypt-then-MAC。
在 PHP 中,您可以使用 Zend Framework 2 的Zend\Crypt\BlockCipher组件,该组件使用 HMAC 实现 Encrypt-then-MAC,提供了一个非常简单的 API:
use Zend\Crypt\BlockCipher;
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey('encryption key');
$ciphertext = $blockCipher->encrypt('this is a secret message');
echo "Ciphertext: $ciphertext \n";
$plaintext = $blockCipher->decrypt($ciphertext);
echo "Plaintext: $plaintext \n";