如何正确区分并使用加密与认证技术?

在密码学专家之中,“加密并不是认证”是一个简单的共识。但很多不了解密码学的开发者,并不知道这句话的意义。如果这个知识更广为人知和深入理解,那么将会避免很多的设计错误。

这一概念本身并不困难,但在表面之下,还有更多丰富的细节和玄妙之处有待发现。本文就是讲述开发者对于加密和认证二者的混淆与误用,并附上了优秀的解决方案。

0x01 加密与认证之间有哪些区别?

加密是呈现信息,使其在没有正确的密钥情况下,变得难以卒读的过程。在简单的对称加密中,同一个密钥被用于加密和解密。在非对称加密中,可以使用用户的公钥对信息加密,使得只有对应私钥的拥有者才能读取它。

认证是呈现信息,使其抗篡改(通常在某一非常低的概率之内,小于1除以已知宇宙中粒子的数量),同时也证明它起源于预期发送者的过程。

注意:当本文提及真实性时,是专门指的信息真实性,而不是身份真实性。这是一个PKI和密钥管理问题,我们可能在未来的博客中详细说明。

就CIA triad而言:加密提供机密性,认证提供完整性。

加密不提供完整性;被篡改的信息(通常)还能解密,但结果通常会是垃圾。单独加密也不抑制恶意第三方发送加密信息。

认证不提供机密性;可以为明文信息提供抗篡改。

在程序员中,常见的错误是混淆这两个概念。你能很容易找到这样的一个库或者框架:加密cookie数据,然后在仅仅解密它之后就无条件地信任与使用之。

0x02 加密

我们之前定义了加密,并且详细说明了它是提供机密性,但不提供完整性和真实性的。你可以篡改加密信息,并将产生的垃圾给予接收者。而且你甚至可以利用这种垃圾产生机制,来绕过安全控制。

考虑在加密cookie的情况下,有如下代码:

  1. function setUnsafeCookie($name, $cookieData, $key)  
  2. {  
  3.     $iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);  
  4.     return setcookie(  
  5.         $name,   
  6.         base64_encode(  
  7.             $iv.  
  8.             mcrypt_encrypt(  
  9.                 MCRYPT_RIJNDAEL_128,  
  10.                 $key,  
  11.                 json_encode($cookieData),  
  12.                 MCRYPT_MODE_CBC,  
  13.                 $iv  
  14.             )  
  15.         )  
  16.     );  
  17. }  
  18. function getUnsafeCookie($name, $key)  
  19. {  
  20.     if (!isset($_COOKIE[$name])) {  
  21.         return null;  
  22.     }  
  23.     $decoded = base64_decode($_COOKIE[$name]);  
  24.     $iv = mb_substr($decoded, 0, 16, '8bit');  
  25.     $ciphertext = mb_substr($decoded, 16, null, '8bit');  
  26.    
  27.     $decrypted = rtrim(  
  28.         mcrypt_decrypt(  
  29.             MCRYPT_RIJNDAEL_128,  
  30.             $key,  
  31.             $ciphertext,  
  32.             MCRYPT_MODE_CBC,  
  33.             $iv  
  34.         ),  
  35.         "\0"  
  36.     );  
  37.    
  38.     return json_decode($decrypted, true);  

上面的代码提供了在密码段链接模块的AES加密,如果你传入32字节的字符串作为$key,你甚至可以声称,为你的cookie提供了256位的AES加密,然后人们可能被误导相信它是安全的。

0x03 如何攻击未经认证的加密

比方说,在登录到这个应用程序之后,你会发现你收到一个会话cookie,看起来就像

kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

让我们改变一个字节的第一块(初始化向量),并反复发送我们的新的cookie,直到出现一些变化。应该采取共4096次HTTP请求,以尝试变量IV所有可能的单字节变化。在上面的例子中,经过2405次请求后,我们得到一个看起来像这样的字符串:

kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

相比之下,在base64编码的cookie中只有一个字符不同(kHv9PAlStPZaZ J vs kHv9PAlStPZaZ Z):

– kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

+ kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==

我们存储在这个cookie里的原始数据,是看起来像这样的数组:

  1. array(2) {  
  2.   ["admin"]=> 
  3.   int(0)  
  4.   ["user"]=> 
  5.   "aaaaaaaaaaaaa"  
  6.  } 

但在仅仅改变初始化向量的一个字节之后,我们就能够改写我们的阅读信息:

  1. array(2) {  
  2.   ["admin"]=> 
  3.   int(1)  
  4.   ["user"]=> 
  5.   "aaaaaaaaaaaaa"  

根据底层应用程序的设置方法,你或许可以翻转一位进而提升成为一名管理员。即使你的cookie是加密的。 如果你想再现我们的结果,我们的加密密钥是十六进制下的:000102030405060708090a0b0c0d0e0f

#p#

0x04 认证

如上所述,认证旨在提供信息的完整性(我们指显著抗篡改能力),而这证明它来自预期的源(真实性)。这样做的典型方法是,为信息计算一个密钥散列消息认证码(HMAC的简称),并将信息与它连结。

  1. function hmac_sign($message, $key)  
  2. {  
  3.     return hash_hmac('sha256', $message, $key) . $message;  
  4. }  
  5. function hmac_verify($bundle, $key)  
  6. {  
  7.     $msgMAC = mb_substr($bundle, 0, 64, '8bit');  
  8.     $message = mb_substr($bundle, 64, null, '8bit');  
  9.     return hash_equals(  
  10.         hash_hmac('sha256', $message, $key),  
  11.         $msgMAC  
  12.     );  

重要的是,这里使用一个适当的哈希工具,如HMAC,而不仅仅是一个简单的散列函数。

  1. function unsafe_hash_sign($message, $key)  
  2. {  
  3.     return md5($key.$message) . $message;  
  4. }  
  5. function unsafe_hash_verify($bundle, $key)  
  6. {  
  7.     $msgHash = mb_substr($bundle, 0, 64, '8bit');  
  8.     $message = mb_substr($bundle, 64, null, '8bit');  
  9.     return md5($key.$message) == $msgHash;  

我在这两个函数名前面加了unsafe,是因为它们还是易受到一些缺点的危害:

Timing Attacks

Chosen Prefix Attacks on MD5 (PDF)

Non-strict equality operator bugs (largely specific to PHP)

现在,我们有点接近我们强大的对称加密认证的目标。目前仍有几个问题,如:

如果我们的原始信息以空字节结尾会发生什么?

有没有一个比mcrypt扩展库默认使用的更好的填充策略?

由于使用AES,有哪些通信方面是易受攻击的?

幸运的是,这些问题在现有的加密函数库中已有了解答。我们强烈推荐你使用现有的库,而不是写自己的加密功能。对于PHP开发人员来说,你应该使用defuse/php-encryption(或者libsodium)。

0x05 用Libsodium安全加密Cookies

  1. /*  
  2. // At some point, we run this command:  
  3. $key = Sodium::randombytes_buf(Sodium::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);  
  4. */  
  5.    
  6. /**  
  7.  * Store ciphertext in a cookie  
  8.  *   
  9.  * @param string $name - cookie name  
  10.  * @param mixed $cookieData - cookie data  
  11.  * @param string $key - crypto key  
  12.  */  
  13. function setSafeCookie($name, $cookieData, $key)  
  14. {  
  15.     $nonce = Sodium::randombytes_buf(Sodium::CRYPTO_SECRETBOX_NONCEBYTES);  
  16.    
  17.     return setcookie(  
  18.         $name,  
  19.         base64_encode(  
  20.             $nonce.  
  21.             Sodium::crypto_secretbox(  
  22.                 json_encode($cookieData),  
  23.                 $nonce,  
  24.                 $key  
  25.             )  
  26.         )  
  27.     );  
  28. }  
  29.    
  30. /**  
  31.  * Decrypt a cookie, expand to array  
  32.  *   
  33.  * @param string $name - cookie name  
  34.  * @param string $key - crypto key  
  35.  */  
  36. function getSafeCookie($name, $key)  
  37. {  
  38.     $hexSize = 2 * Sodium::Sodium::CRYPTO_SECRETBOX_NONCEBYTES;  
  39.     if (!isset($_COOKIE[$name])) {  
  40.         return array();  
  41.     }  
  42.    
  43.     $decoded = base64_decode($_COOKIE[$name]);  
  44.     $nonce = mb_substr($decoded, 0, $hexSize, '8bit');  
  45.     $ciphertext = mb_substr($decoded, $hexSize, null, '8bit');  
  46.    
  47.     $decrypted = Sodium::crypto_secretbox_open(  
  48.         $ciphertext,  
  49.         $nonce,  
  50.         $key  
  51.     );  
  52.     if (empty($decrypted)) {  
  53.         return array();  
  54.     }  
  55.    
  56.     return json_decode($decrypted, true);  

对于没有libsodium库的开发人员,我们的一个博客读者,提供了一个安全cookie实现的例子,其使用了defuse/php-encryption(我们推荐的PHP库)。

0x06 使用关联数据的认证加密

在我们前面的示例中,我们集中精力于,同时使用加密和认证,使其作为必须小心使用的单独组件,以避免加密的悲剧。具体而言,我们专注于密码段链接模块的AES加密。

然而,密码学家已经开发出更新,更具有弹性的加密模型,其加密和认证信息在同一操作。这些模型被称为AEAD模型(Authenticated Encryption with Associated Data)。关联数据意味着,无论你的应用程序需要什么进行认证,都不加密。

AEAD模型通常用于有状态的目的,如网络通信中,其中一个随机数可以很容易地管理。

AEAD两个可靠的实现是AES-GCM和ChaCha20-Poly1305。

AES-GCM是Galois/Counter模式中的高级加密标准(又名Rijndael算法加密)。这种模式在OpenSSL的最新版本中加入,但它目前在PHP中还不被支持。

ChaCha20-Poly1305结合了ChaCha20流密码与Poly1305消息认证码。这种模式在libsodium PHP扩展可用。Sodium::crypto_aead_chacha20poly1305_encrypt() Sodium::crypto_aead_chacha20poly1305_decrypt()

总结一下,你该记住的

加密不是认证

加密提供机密性

认证提供完整性

将两者混为一谈你就得自担风险

为了完成CIA triad,你需要单独解决可用性。这通常不是一个加密问题。

更重要的是:在密码学专家的监督下,使用具有韧性被证实记录的库,而不是自己在那里闭门造车,你会好得多。

原文:https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/128172.html<

(0)
管理的头像管理
上一篇2025-02-25 00:46
下一篇 2025-02-25 00:47

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注