56

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

让我强调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™,并且仍然(相当)确定没有人可以读取或篡改它......
这不就是全部吗?加密点?

但下面的反应倾向于:不要相信加密来实现安全。

我错过了什么??

4

13 回答 13

28

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

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

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

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

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

  • 使某人更有可能窃取该用户的记忆密钥,从而使他们能够欺骗用户的身份(然后可能更改他的密码);
  • CSRF -跨站点请求伪造。您的功能将有效地允许匿名攻击者导致不知情的用户向您的应用程序提交“经过身份验证的”请求,即使没有实际登录。
于 2009-03-03T12:27:49.450 回答
18

这涉及两个不同的问题。

首先,会话劫持。这就是第三方发现(例如)经过身份验证的 cookie 并获得对他人详细信息的访问权限的地方。

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

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

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

对此数据的加密不会使信息更值得信赖,因为对称加密容易受到暴力攻击。显然 AES-256 比 DES 更好(呵呵),但是 256 位的安全性并不一定像你想象的那么重要。

一方面,SALT 通常是根据算法生成的,否则容易受到攻击。

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

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

应该指出的是,在 PHP 中的共享托管环境中(例如),您的会话数据只是存储在文件系统上,并且同一主机上的其他任何人都可以读取,尽管他们不一定知道它用于哪个站点。因此,切勿在没有某种形式的加密的情况下存储纯文本密码、信用卡号、大量个人详细信息或任何可能在此类环境中被视为敏感的会话数据,或者更好的是,仅在会话中存储密钥并存储实际敏感信息数据库中的数据。

注意:以上不是 PHP 独有的。

但那是服务器端加密。

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

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

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

我还没有看到用于加密仅代表会话密钥的 cookie 的安全参数。

如果有人能指出相反的事情,我很乐意被证明是错误的。

于 2009-03-03T12:26:55.350 回答
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))); 
}

然后保存cookie

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

并在下一页阅读:

$data = decrypt($_COOKIE['PHPSESSION'], 'longsecretsalt');
于 2010-10-25T15:46:30.973 回答
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');

如果您无法安装 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. 使用步骤 2中的加密密钥 ( ) 使用您openssl_encrypt()的 IV 和适当的模式(例如)加密您的字符串。aes-256-ctr$eKey
  4. 使用密钥散列函数(例如 HMAC-SHA256)计算步骤 3 中密文的身份验证标签。例如hash_hmac('sha256', $iv.$ciphertext, $aKey)加密后进行身份验证以及封装 IV/nonce 非常重要。
  5. 将身份验证标签、IV 或 nonce 和密文打包在一起,并可选择使用bin2hex()or对其进行编码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. Aselector将用于数据库查找。(使用随机选择器而不只是顺序 ID 的目的是泄露您网站上的活跃用户数量。如果您愿意泄露此信息,请随意使用顺序 ID。)
    2. Avalidator将用于自动对用户进行身份验证。
  2. 计算一个哈希值validator(一个简单的 SHA-256 哈希就足够了)。
  3. selector和 的哈希存储在validator为自动登录保留的数据库表中。
  4. selector和存储validator在客户端的 cookie 中。

兑换记住我的令牌

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

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

于 2015-07-30T01:42:23.337 回答
4

您可以通过在 EAX 模式下使用 AES 安全地实现您想要的。密文会比明文大;这对于安全加密来说是正常的。

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

随机生成 AES 密钥。

确保每次加密都使用新的随机数,并使用“关联数据”字段来确保您为一个目的加密的东西不会被显示为另一个目的(因此用户名和 cookie 名称之类的东西可以进入那里)

以下反应推向:不要相信加密来实现安全性。

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

于 2009-09-23T12:32:09.480 回答
2

虽然两者都很强大,但 AES 是一种标准。

至于小块数据的安全性:越小越好。公开的加密数据越少,您可以使用密钥的时间越长。在给定算法的一个密钥内可以加密多少数据而不会使系统面临风险,总是存在理论上的限制。

于 2009-03-03T12:27:57.780 回答
2

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

尤其是 PHP 世界充满了忽略这一点的不良示例(请参阅PHP 密码学 - 谨慎操作),但这确实适用于任何语言。

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

于 2013-10-29T14:03:17.080 回答
1

为什么要加密 cookie?

在我看来,有两种情况:要么给客户密钥,要么不给。

如果您不将密钥提供给客户,那么您为什么要给他们数据?除非您正在玩一些破坏弱加密的奇怪游戏(您明确没有这样做),否则您最好将数据存储在服务器上。

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

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

于 2009-03-03T14:20:44.217 回答
1

如果您加密 cookie,服务器仍然必须对其进行解码才能读取它(以检查相同的密钥),因此任何加密的 cookie 都是毫无意义的,因为如果被盗(并且未经编辑)它仍然会导致黑客访问您的帐户. 它就像根本没有加密一样不安全。

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

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

于 2009-10-12T14:43:26.513 回答
0

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

于 2009-03-03T12:28:38.997 回答
0

我认为“放弃”任何数据,即使是关于用户名和密码的加密数据也不好......有很多 JS 可以嗅探它......我建议你在用户数据库表中创建一个字段 cookie_auth 或其他...... .

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

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

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

希望这可以帮助 :-)

feha vision.to

于 2010-01-18T17:43:54.233 回答
0

另外,我已经尝试过mcrypt_encrypt一件事,请记住。如果你这样做base64_encode(mcrypt_encrypt(...))

然后,您执行base64_decode 并输出加密数据 ( echo)。你可能会被搞砸,什么也看不到。但是,如果您这样做 mcrypt_decrypt( ... base64_decode($value) )。您将看到原始数据。

于 2011-03-14T18:09:15.637 回答
-1

说了这么多可怕的事情,虽然这是真的,但让我们看到光明的一面,一点常识和持续关注您的网站可能会一直为您节省时间。

保存 cookie 是 Web 开发的重要组成部分,因此不容忽视。但也要尽量避免;只有在用户关闭浏览器后我想扩展登录会话时,我才会看到使用 Cookie。如果不想将用户会话扩展到浏览器关闭之外,则应使用会话组件。即使使用 Session 组件,也应该注意 Session Hijacking。

无论如何,回到 Cookie 的事情;在我看来,如果遵循以下预防措施,我很确定我们可以更安全。

我把precautionary measurementin分成两个阶段

第一阶段:开发

  1. 设置path属性
  2. expiration_date
  3. 集合secure,httpOnly属性
  4. 使用最新的加密算法
  5. 使用两种算法:例如使用blowfish,然后base64_encode在其之上使用。

第 2 阶段:运营/审计

  1. 定期进行现场审核,使用burp.
于 2020-01-22T06:27:20.373 回答