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

Jac*_*cco 48 php security encryption cookies remember-me

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

让我强调AviD给出的正确答案:

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



我正在寻找有关加密cookie的"最佳"加密算法的信息.

我有以下要求:

  • 必须快速
    加密和解密数据(几乎)每个请求都要完成

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

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

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

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

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

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

更新
更准确地说,我们要加密2个cookie:一个包含会话信息,另一个包含"记住我"信息.

该平台是PHP上作为VPS上的Linux上的apache模块.

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

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

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

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

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

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

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

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

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

我错过了什么?

Avi*_*viD 25

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

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

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

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

好像我们对你原来的具体问题有点偏僻 - 并通过改变设计改变了你的问题的基础......
所以只要我们这样做,我也强烈建议反对这个功能坚持"记住我",有几个原因,其中最大的一个:

  • 更有可能的是,有人可能窃取该用户的记住密钥,允许他们欺骗用户的身份(然后可能更改他的密码);
  • CSRF - 跨站请求伪造.您的功能将有效地允许匿名攻击者使不知情的用户向您的应用程序提交"已验证"的请求,即使没有实际登录也是如此.

  • 添加HMAC(加密然后MAC)是必不可少的.否则填充oracles可能会破坏AES-CBC的机密性.| @ N13只要您使用好的随机密钥(如您所愿),AES-256就没有理由比AES-128弱. (5认同)
  • 最后,没有人关心隐私或安全.这就是为什么从gmail到amazon的每个大型网站都有一个"记住我"复选框.人们宁愿丢失数据或被黑客入侵而不是输入他们的密码.所以,给他们想要的东西,因为你所竞争的所有人都已经做了. (3认同)

cle*_*tus 18

这涉及两个不同的问题.

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

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

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

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

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

一方面,SALT通常根据算法生成或者易受攻击.

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

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

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

注意:以上内容并非PHP独有.

但那是服务器端加密.

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

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

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

我还没有看到加密cookie的安全性论据,该cookie只代表会话密钥.

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

  • 256位提供了大量保护,适用于大多数用途,足以应对这种情况.当然,我们假设我们正在谈论256位RANDOM数据......没有人会在任何一个世纪很快破解这一点. (6认同)
  • 其次,盐(其中应该是随机的)和"婴儿床"(也称为明文)攻击(目前)都与AES无关.总的来说,没有理由假设或考虑您的AES加密数据将被解密,除非这是一个实施问题(很可能是;-)) (4认同)

Xeo*_*oss 9

安全警告:这两个功能不安全.他们使用的是ECB模式,无法验证密文.请参阅此答案以获得更好的前进方法.

对于想要在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))); 
}
Run Code Online (Sandbox Code Playgroud)

然后保存cookie

setcookie("PHPSESSION", encrypt('thecookiedata', 'longsecretsalt'));
Run Code Online (Sandbox Code Playgroud)

并在下一页阅读:

$data = decrypt($_COOKIE['PHPSESSION'], 'longsecretsalt');
Run Code Online (Sandbox Code Playgroud)

  • 建议的功能易受操纵,因为它没有密文完整性保护.有关详细信息,请参见http://ipsec.pl/node/1085. (2认同)

Sco*_*ski 5

使用 Libsodium 的快速加密 Cookie

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

<?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');
Run Code Online (Sandbox Code Playgroud)

如果您无法安装 PECL 扩展,请让您的系统管理员或托管服务提供商为您安装。如果他们拒绝,你仍然有选择。


PHP 中的安全加密 Cookie,请保留盐

其他答案指示您使用 openssl 或 mcrypt 加密数据,但它们缺少关键步骤。如果您想在 PHP 中安全地加密数据,您必须您的消息进行身份验证。

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


前言

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

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

    如果您需要特别关注,我的雇主提供技术咨询服务,包括密码学功能的开发。

加密

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

解密

  1. 按照加密中的第 2 步拆分您的密钥。我们在解密过程中需要相同的两个密钥!
  2. (可选地,解码并)从打包的消息中解包 MAC、IV 和密文。
  3. 通过使用用户提供的 HMAC 重新计算 IV/nonce 的 HMAC 和密文来验证身份验证标签 hash_equals().
  4. 当且仅当第 3 步通过时,使用 解密密文$eKey

如果您想看看这一切如何组合在一起,请参阅此答案,其中包含示例代码

如果这听起来工作量太大,请使用defuse/php-encryptionzend-crypt并收工。


记住我饼干

但是,我们需要实现“记住我”功能。解决此问题的公认方法是设置 cookie。如果客户端提供此 cookie,则允许他或她以(几乎)相同的权限访问系统,就好像他/她提供了有效的用户名密码组合一样。

加密实际上不是这项工作的正确工具。您想遵循此过程以在 PHP 中安全记住我的 cookie

生成记住我令牌

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

兑换“记住我”令牌

  1. 将传入的 cookie 拆分为selectorvalidator
  2. 执行数据库查找(使用准备好的语句!)基于selector.
  3. 如果找到一行,则计算validator.
  4. 再次使用 将步骤 3 中计算的哈希值与存储在数据库中的哈希值进行比较hash_equals()
  5. 如果步骤 4 返回 true,则将用户登录到适当的帐户。

这是Gatekeeper用于长期用户身份验证所采用的策略,也是迄今为止为满足此要求而提出的最安全的策略。