什么加密算法最适合加密cookie?

时间:2009-03-03 12:18:15

标签: php security cookies encryption remember-me

  

由于这个问题相当受欢迎,我认为给它更新很有用。

     

让我强调AviD对此问题的正确答案:   

     

您不应存储任何需要在Cookie中加密的数据。而是在Cookie中存储大小合适的(128位/ 16字节)随机密钥并存储您想要保密的信息在服务器上,由cookie的密钥标识。



我正在寻找有关加密Cookie的“最佳”加密算法的信息。

我有以下要求:

  • 一定要快 将对(几乎)每个请求进行加密和解密数据

  • 它将在小数据集上运行,通常是大约100个字符或更少的字符串

  • 它必须是安全的,但它不像我们确保银行交易安全

  • 我们需要能够解密信息,以便SHA1等出来。

现在我已经读过Blowfish快速而安全,我读过AES快速而安全。 Blowfish的块大小较小。

我认为这两种算法都提供了足够的安全性?所以速度将成为决定性因素。 但我真的不知道这些算法是否适合小字符串,以及是否有更适合加密cookie的算法。

所以我的问题是:
什么加密算法最适合加密cookie数据?

更新
更确切地说,我们想要加密2个cookie:一个包含会话信息,另一个包含“记住我”信息。

该平台是PHP上的Linux上的apache模块。

更新2
我同意cletus将任何信息存储在cookie中是不安全的。

但是,我们要求实现'记住我'功能。可接受的方法是设置cookie。如果客户端提供此cookie,则允许他或她以(几乎)相同的权限访问系统,就像他/她提供有效的用户名密码组合一样。

所以我们至少要加密cookie中的所有数据,以便它: a)恶意用户无法读取内容,
b)恶意用户无法制作自己的Cookie或篡改它。

(在我们对它做任何事情之前,对所有来自cookie的数据进行消毒并检查其有效性,但这是另一个故事)

会话cookie不再包含sessionId / timestamp。它可以在没有加密的情况下使用,但我认为加密它没什么害处? (计算时间除外)。

因此我们必须在cookie中存储一些数据,加密它的最佳方法是什么?

更新3
对这个问题的回答使我重新考虑所选择的方法。我确实可以做同样的事情而不需要加密。我不应该加密数据,而应该只发送没有上下文且无法猜到的无意义的数据。

然而,我也不知所措:
我认为加密使我们能够将数据发送到BigBadWorld™,并且仍然(相当)确定没有人可以读取或篡改它...
这不是加密的全部意义吗?

但是下面的反应推动:不要相信加密来实现安全性。

我错过了什么?

13 个答案:

答案 0 :(得分:24)

没有真正的理由不使用256位的AES。确保在CBC mode和PKCS#7填充中使用它。 正如你所说,快速而安全。

我已阅读(未测试)Blowfish可能稍快一些......但是Blowfish的主要缺点是设置时间长,这会使您的情况变得糟糕。此外,AES更“经过验证”。

这假设确实 对对称加密您的cookie数据是必要的。正如其他人所指出的那样,它确实不是必要的,并且只有少数边缘情况除此之外别无选择。通常,它更适合您更改设计,并返回随机会话标识符,或者必要时单向哈希(使用SHA-256)。
在您的情况下,除了“常规”随机会话标识符,您的问题是“记住我”功能 - 这也应该实现为:

  • 一个长的随机数,存储在数据库中并映射到用户帐户;
  • 或包含例如的密钥哈希(例如HMAC)。用户名,时间戳,mebbe盐和秘密服务器密钥。这当然可以在服务器端进行验证......

似乎我们已经对您原来的具体问题略微偏离了主题 - 并通过更改设计改变了问题的基础....
所以,只要我们这样做,我也强烈建议反对持续“记住我”的这个特征,原因有几个,其中最大的是:

  • 更有可能是某人窃取了该用户的记忆密钥,允许他们欺骗用户的身份(然后可能更改密码);
  • CSRF - Cross Site Request Forgery。您的功能将有效地允许匿名攻击者让不知情的用户向您的应用程序提交“经过身份验证的”请求,即使没有实际登录也是如此。

答案 1 :(得分:19)

这涉及两个不同的问题。

首先,会话劫持。这是第三方发现经过身份验证的cookie并获取其他人详细信息的地方。

其次,会话数据安全性。我的意思是你将数据存储在cookie中(例如用户名)。这不是一个好主意。任何此类数据从根本上都是不值得信任的,就像HTML表单数据不可信(无论您使用什么Javascript验证和/或HTML长度限制,如果有的话),因为客户可以自由提交他们想要的内容。

您经常会发现人们(正确地)主张对HTML表单数据进行清理,但cookie表面数据会被盲目接受。大错。事实上,我从不在cookie中存储任何信息。我将其视为会话密钥,这就是全部。

如果您打算将数据存储在Cookie中,我强烈建议您重新考虑。

加密此数据不会使信息更受信任,因为对称加密容易受到暴力攻击。显然,AES-256比DES(heh)更好,但256位的安全性并不一定意味着它的意思。

首先,SALT通常是根据算法生成的,否则很容易受到攻击。

另一方面,cookie数据是crib攻击的主要候选者。如果已知或怀疑加密数据中的用户名是嘿,那就是你的婴儿床。

这让我们回到第一点:劫持。

应该指出的是,在PHP的共享托管环境中(作为一个示例),您的会话数据只是存储在文件系统上,并且可以由同一主机上的任何其他人读取,尽管他们不一定知道哪个站点是为了。因此,在没有某种形式的加密的情况下,永远不要存储明文密码,信用卡号,广泛的个人详细信息或在会话数据中可能被视为敏感的任何内容,或者更好的是,只需在会话中存储密钥并存储实际的敏感信息数据库中的数据。

注意:上述内容并非PHP独有。

但那是服务器端加密。

现在你可能会争辩说用一些额外的数据加密会话会使它更加安全,不会被劫持。一个常见的例子是用户的IP地址。问题是很多人在许多不同的地方使用相同的PC /笔记本电脑(例如Wifi热点,工作,家庭)。此外,许多环境将使用各种IP地址作为源地址,尤其是在企业环境中。

您也可以使用用户代理,但这是可以猜测的。

实际上,据我所知,根本没有真正的理由使用cookie加密。我从来没有想过有这个问题,但鉴于这个问题,我一直在寻求证明是对还是错。我找到了一些关于人们建议加密cookie数据的方法的线程,透明地用Apache模块做等等,但这些都似乎都是通过保护存储在cookie中的数据(这是你不应该做的)来实现的。

我还没有看到加密cookie的安全性参数,该cookie只代表会话密钥。

如果有人可以指出相反的事情,我将很高兴被证明是错误的。

答案 2 :(得分:9)

  

安全警告:这两项功能不安全。他们正在使用ECB mode而未能使用authenticate the ciphertext。有关更好的前进方法,请参阅this answer

对于想要在PHP脚本中使用此方法的读者。以下是使用256位Rijndael(非AES)的工作示例。

function encrypt($text, $salt) 
{ 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); 
} 

function decrypt($text, $salt) 
{ 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))); 
}

然后保存cookie

setcookie("PHPSESSION", encrypt('thecookiedata', 'longsecretsalt'));

并阅读下一页:

$data = decrypt($_COOKIE['PHPSESSION'], 'longsecretsalt');

答案 3 :(得分:4)

通过在EAX模式下使用AES,您可以安全地实现所需。密文将大于明文;这对于安全加密是正常的。

攻击者当然会从密文中知道明文的长度,但他们无法确定其他任何内容。

随机生成AES密钥。

确保并为每次加密使用新的随机数,并使用“关联数据”字段确保您为一个目的加密的内容不会显示为另一个(因此诸如用户名和cookie名称之类的内容)可以去那里)

  

下面的反应推动:做   不信任加密来完成   安全

更多“如果你不是加密专家,你会低估出错的容易程度”。例如,AFAICT此线程中没有其他人讨论了链接模式或消息完整性,它涵盖了两个常见的初学者错误。

答案 4 :(得分:4)

使用Libsodium的快速加密Cookie

如果您需要PHP中快速,安全的加密Cookie,请查看Halite如何实现它们。 Halite依靠the libsodium PECL extension来提供安全加密。

<?php
use \ParagonIE\Halite\Cookie;
use \ParagonIE\Halite\Symmetric\Key;
use \ParagonIE\Halite\Symmetric\SecretKey;

// You can also use Key::deriveFromPassword($password, $salt, Key::CRYPTO_SECRETBOX);
$encryption_key = new SecretKey($some_constant_32byte_string_here);

$cookie = new Cookie($encryption_key);

$cookie->store('index', $any_value);
$some_value = $cookie->fetch('other_index');

如果您无法安装PECL扩展程序,请让您的系统管理员或托管服务提供商为您执行此操作。如果他们拒绝,你仍然可以选择。

用PHP保护加密的Cookie,请保留盐

其他答案指示您使用openssl或mcrypt加密数据,但是他们错过了至关重要的一步。如果您想要safely encrypt data in PHP,则必须对您的消息进行身份验证。

使用OpenSSL扩展,您需要遵循的过程如下所示:

序言

  • (甚至在考虑加密之前)生成128位,192位或256位随机字符串。这将是您的主密钥

    请勿使用人类可读的密码。如果您出于某种原因必须使用人类可读的密码,请向Cryptography SE寻求指导。

    如果您需要特别关注,我的雇主会提供technology consulting services,包括加密功能的开发。

加密

  1. 生成随机初始化向量(IV)或随机数。例如random_bytes(openssl_cipher_iv_length('aes-256-cbc'))
  2. 使用HKDF或类似算法将主密钥拆分为两个密钥:
    1. 加密密钥($eKey
    2. 身份验证密钥($aKey
  3. 使用您的IV和适当的修改(例如openssl_encrypt())使用aes-256-ctr加密您的字符串,并使用第2步中的加密密钥($eKey)。
  4. 使用密钥哈希函数(如HMAC-SHA256)计算步骤3中密文的身份验证标记。例如hash_hmac('sha256', $iv.$ciphertext, $aKey)加密后进行身份验证非常重要,并且还要封装IV / nonce。
  5. 将身份验证标记,IV或随机数和密文打包在一起,并可选择使用bin2hex()base64_encode()对其进行编码。 (警告:此方法可能泄漏缓存时间信息。)
  6. 解密

    1. 根据加密步骤2拆分密钥。在解密期间我们需要相同的两个密钥!
    2. (可选,解码并)解压缩打包邮件中的MAC,IV和密文。
    3. 使用hash_equals()通过用户提供的HMAC重新计算IV / nonce和密文的HMAC来验证身份验证标记。
    4. 当且仅当步骤3通过时,使用$eKey解密密文。
    5. 如果您想了解这一切是如何一起看的,请参阅this answer which has sample code

      如果这听起来太多,请使用defuse/php-encryptionzend-crypt并将其称为一天。

      记住我的饼干

        

      但是,我们要求实现'记住我'功能。可接受的方法是设置cookie。如果客户端提供此cookie,则允许他或她以(几乎)相同的权限访问系统,就像他/她提供有效的用户名密码组合一样。

      加密实际上不是这项工作的正确工具。您想对secure remember me cookies in PHP

      执行此过程

      生成记住我标记

      1. 生成两个随机字符串:
        1. 将用于数据库查找的selector。 (随机选择器而不仅仅是一个顺序ID的目的是泄漏您的网站上有多少活跃用户。如果您愿意泄露此信息,请随意使用顺序ID 。)
        2. 一个validator,用于自动验证用户。
      2. 计算validator的哈希值(简单的SHA-256哈希就足够了)。
      3. selectorvalidator的哈希值存储在为自动登录保留的数据库表中。
      4. selectorvalidator存储在客户端的Cookie中。
      5. 兑换记住我的令牌

        1. 将传入的Cookie拆分为selectorvalidator
        2. 根据selector执行数据库查找(使用prepared statements!)。
        3. 如果找到一行,请计算validator
        4. 的哈希值
        5. 使用hash_equals()将步骤3中计算的哈希值与数据库中存储的哈希值进行比较。
        6. 如果步骤4返回true,请将用户登录到相应的帐户。
        7. 这是Gatekeeper用于长期用户身份验证的策略,它是迄今为止满足此要求的最安全策略。

答案 5 :(得分:2)

虽然两者都非常强大,但AES是标准。

对于小块数据的安全性:越小越好。泄露的加密数据越少,您使用密钥的时间就越长。在不给系统带来风险的情况下,在给定算法的一个密钥内可以加密多少数据总是存在理论上的限制。

答案 6 :(得分:2)

如果你加密cookie,服务器仍然需要对它进行解码才能读取它(检查相同的密钥),因此任何加密的cookie都是没有意义的,因为如果被盗(并且未被编辑),它仍将导致黑客正确到您的帐户。它就像不加密一样不安全。

我相信有人偷你的cookie的真正问题是服务器和客户端之间的连接。使用主机提供的SSL连接。

至于你的cookie,你需要在数据库中为每个用户创建一个长的随机id,(让它更改每个登录)并将其设置为cookie或会话。可以通过php检查包含密钥的cookie,如果它等于数据库中的帐户或表,则将数据转储到网页上,就像正常一样。

答案 7 :(得分:2)

正如之前评论中指出的那样,您必须完整性保护应用于您发送给用户并接受回复的任何密文。否则,可以修改受保护的数据,或者恢复加密密钥。

特别是PHP世界充满了忽略这一点的坏例子(见PHP cryptography - proceed with care),但这适用于任何语言。

我见过的一个很好的例子是PHP-CryptLib,它使用组合加密认证模式来完成这项工作。对于Python pyOCB提供了类似的功能。

答案 8 :(得分:1)

为什么要加密cookie?

正如我所看到的,有两种情况:要么给客户钥匙,要么你没有。

如果您没有将密钥提供给客户端,那么您为什么要向他们提供数据?除非你打破一些奇怪的游戏,打破弱加密(你明确没有),你也可以将数据存储在服务器上。

如果您 将客户端的密钥交给客户端,那么为什么要在第一时间对其进行加密?如果你不加密密钥的通信,那么加密cookie是没有意义的:MITM可以查看cookie并向你发送他想要的任何cookie。如果您使用加密通道到客户端,为什么加密存储数据的额外开销呢?

如果您担心客户端计算机上的其他用户正在阅读cookie,请放弃并假设浏览器设置好的权限位:)

答案 9 :(得分:0)

AES(也称为Rijndael)是最受欢迎的。块大小是128位,只有16个字节,你说的是“大约100个字符”。

答案 10 :(得分:0)

我认为“赠送”任何数据甚至是加密的,当涉及用户名和密码时都不好...... 有很多JS可以嗅到它...... 我建议你在用户数据库表中创建一个字段cookie_auth或者其他......

首次登录后收集:当前:浏览器,IP,一些自己的盐键,加上你的主机名var ......

在该字段中创建哈希并存储... 设置一个cookie ... 当cookie“响应”将所有这些与存储的哈希进行比较并完成...

即使有人“偷”了一个cookie,他们也无法使用它: - )

希望这会有所帮助: - )

FEHA vision.to

答案 11 :(得分:0)

此外,我已经尝试了mcrypt_encrypt,请记住一件事。如果你做base64_encode(mcrypt_encrypt(...))

然后,您执行base64_decode并输出加密数据(echo)。你可能会被搞砸而没有看到任何东西。但是,如果你做mcrypt_decrypt( ... base64_decode($value) )。您将看到原始数据。

答案 12 :(得分:-1)

说了这么多可怕的话,但这是对的,但让我们看看乐观的一面,一点常识和对网站的持续监视可能会一直节省您的时间。

保存cookie是Web开发的重要组成部分,因此人们不能忽略它。但是我们也应该尽量避免;我仅在即使用户关闭浏览器后仍想扩展登录会话时,才看到Cookies的使用。如果不希望将用户会话扩展到关闭浏览器之外,则应使用会话组件。即使使用会话组件,也应该注意会话劫持。

无论如何,回到Cookie的东西;我认为,如果遵循以下预防措施,我很确定我们会在安全方面。

我将precautionary measurement分为两个阶段

第一阶段:开发

  1. 设置path属性
  2. 设置expiration_date
  3. 设置securehttpOnly属性
  4. 使用最新的加密算法
  5. 使用两种算法:例如,使用blowfish,然后在其顶部使用base64_encode

阶段2:操作/审核

  1. 使用burp之类的工具定期进行站点审核。