由于这个问题相当受欢迎,我认为对其进行更新很有用。

让我强调一下 正确答案 正如给出的 阿维德 对于这个问题:

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



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

我有以下要求:

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

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

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

  • 我们需要能够解密信息,因此 SHA1 之类的东西就无法使用了。

现在我了解到 Blowfish 快速且安全,并且我了解到 AES 快速且安全。Blowfish 具有较小的块大小。

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

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

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

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

更新2
我同意 克莱图斯 在 cookie 中存储任何信息都是不安全的。

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

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

(在我们对 cookie 进行任何操作之前,所有来自 cookie 的数据都会被清理并检查其有效性,但那是另一回事了)

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

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

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

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

但以下反应推动:不要相信加密可以实现安全。

我缺少什么?

有帮助吗?

解决方案

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

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

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

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

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

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

其他提示

这涉及两个不同的问题。

首先, 会话劫持. 。例如,第三方可以在此处发现经过身份验证的 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 的安全论据,该 cookie 只代表会话密钥。

如果有人能指出相反的观点,我会很高兴被证明是错的。

安全警告: :这两个函数并不安全。他们正在使用 欧洲央行模式 并且未能 验证密文. 。看 这个答案 为了更好的前进方向。

对于那些想要在 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');

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

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

随机生成 AES 密钥。

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

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

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

含 Libsodium 的快速加密 Cookie

如果您需要在 PHP 中快速、安全的加密 cookie,请查看如何 石盐 实施它们。岩盐依靠 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 位随机字符串。这将是你的 主密钥.

    不要使用人类可读的密码。 如果你由于某种原因, 必须 使用人类可读的密码,询问 密码学SE 以获得指导。

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

加密

  1. 生成随机初始化向量 (IV) 或随机数。例如 random_bytes(openssl_cipher_iv_length('aes-256-cbc'))
  2. 使用 香港发展基金会 或类似的算法将主密钥拆分为两个密钥:
    1. 加密密钥($eKey)
    2. 身份验证密钥($aKey)
  3. 加密你的字符串 openssl_encrypt() 与您的静脉注射和适当的模式(例如 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.

如果您想了解这一切看起来如何,请参阅 这个答案有示例代码.

如果这听起来工作量太大,请使用 化解/php 加密 或者 zend 隐秘 到此为止。


记住我饼干

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

加密实际上并不是完成这项工作的正确工具。您想要遵循此流程 PHP 中的安全记住我 cookie:

生成“记住我”令牌

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

兑换“记住我”令牌

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

这就是策略 看门人 用于长期用户身份验证,这是迄今为止为满足此要求而提出的最安全的策略。

虽然 AES 都是非常强大的标准,但它是一个标准。

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

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

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

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

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

尤其是 PHP 世界充满了忽视这一点的坏例子(参见 PHP 密码学 - 谨慎行事)但这确实适用于任何语言。

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

为什么要加密cookie?

据我看来,有两种情况:您要么向客户提供密钥,要么不提供。

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

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

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

AES(也称为 Rijndael)是最流行的。块大小为 128 位,即 16 个字节,而您所说的是“大约 100 个字符”。

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

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

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

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

希望这可以帮助 :-)

费哈 愿景

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top