草庐IT

php - 如何在php中加密/解密数据?

coder 2023-04-24 原文

我目前是一名学生,正在学习 PHP,我正在尝试使用 PHP 对数据进行简单的加密/解密。我做了一些在线研究,其中一些令人困惑(至少对我而言)。

这是我想要做的:

我有一个由这些字段组成的表 (用户 ID、Fname、Lname、电子邮件、密码)

我想要的是将所有字段加密然后解密(是否可以使用 sha256 进行加密/解密,如果没有任何加密算法)

我想学习的另一件事是如何创建一个单向 hash(sha256)结合良好的“盐”。
(基本上我只想有一个简单的加密/解密实现,hash(sha256)+salt) 先生/女士,您的回答将大有帮助,我们将不胜感激。谢谢++

最佳答案

前言

从您的表定义开始:

- UserID
- Fname
- Lname
- Email
- Password
- IV

以下是变化:
  • 字段 Fname , LnameEmail将使用对称密码进行加密,由 OpenSSL 提供,
  • IV字段将存储 initialisation vector用于加密。存储要求取决于使用的密码和模式;稍后会详细介绍这一点。
  • Password字段将使用单向密码散列进行散列,

  • 加密

    密码和模式

    选择最佳加密密码和模式超出了本答案的范围,但最终选择会影响加密 key 和初始化向量的大小;对于这篇文章,我们将使用 AES-256-CBC,它具有 16 字节的固定块大小和 16、24 或 32 字节的 key 大小。

    加密 key

    一个好的加密 key 是由可靠的随机数生成器生成的二进制 blob。建议使用以下示例 (>= 5.3):
    $key_size = 32; // 256 bits
    $encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
    // $strong will be true if the key is crypto safe
    

    这可以执行一次或多次(如果您希望创建加密 key 链)。尽可能将这些保密。



    初始化向量为加密增加了随机性,这是 CBC 模式所必需的。理想情况下,这些值应该只使用一次(技术上每个加密 key 一次),因此对行的任何部分的更新都应该重新生成它。

    提供了一个函数来帮助您生成 IV:
    $iv_size = 16; // 128 bits
    $iv = openssl_random_pseudo_bytes($iv_size, $strong);
    

    示例

    让我们使用较早的 $encryption_key 加密名称字段和 $iv ;为此,我们必须将数据填充到块大小:
    function pkcs7_pad($data, $size)
    {
        $length = $size - strlen($data) % $size;
        return $data . str_repeat(chr($length), $length);
    }
    
    $name = 'Jack';
    $enc_name = openssl_encrypt(
        pkcs7_pad($name, 16), // padded data
        'AES-256-CBC',        // cipher and mode
        $encryption_key,      // secret key
        0,                    // options (not used)
        $iv                   // initialisation vector
    );
    

    存储要求

    加密输出,如 IV,是二进制的;可以通过使用指定的列类型(例如 BINARY)来将这些值存储在数据库中。或 VARBINARY .

    输出值和 IV 一样,是二进制的;要将这些值存储在 MySQL 中,请考虑使用 BINARY or VARBINARY 列。如果这不是一个选项,您还可以使用 base64_encode() 将二进制数据转换为文本表示。或 bin2hex() ,这样做需要多出 33% 到 100% 的存储空间。

    解密

    存储值的解密是类似的:
    function pkcs7_unpad($data)
    {
        return substr($data, 0, -ord($data[strlen($data) - 1]));
    }
    
    $row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
    // $enc_name = base64_decode($row['Name']);
    // $enc_name = hex2bin($row['Name']);
    $enc_name = $row['Name'];
    // $iv = base64_decode($row['IV']);
    // $iv = hex2bin($row['IV']);
    $iv = $row['IV'];
    
    $name = pkcs7_unpad(openssl_decrypt(
        $enc_name,
        'AES-256-CBC',
        $encryption_key,
        0,
        $iv
    ));
    

    认证加密

    您可以通过附加从 key (不同于加密 key )和密文生成的签名来进一步提高生成的密文的完整性。在密文解密之前,首先要验证签名(最好采用恒定时间比较的方法)。

    示例
    // generate once, keep safe
    $auth_key = openssl_random_pseudo_bytes(32, $strong);
    
    // authentication
    $auth = hash_hmac('sha256', $enc_name, $auth_key, true);
    $auth_enc_name = $auth . $enc_name;
    
    // verification
    $auth = substr($auth_enc_name, 0, 32);
    $enc_name = substr($auth_enc_name, 32);
    $actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);
    
    if (hash_equals($auth, $actual_auth)) {
        // perform decryption
    }
    

    另见: hash_equals()

    散列

    必须尽可能避免在数据库中存储可逆密码;您只想验证密码而不是知道其内容。如果用户丢失了密码,最好让他们重置密码,而不是将原始密码发送给他们(确保密码重置只能在有限的时间内完成)。

    应用散列函数是一种单向操作;之后可以安全地用于验证,而不会泄露原始数据;对于密码,蛮力方法是一种可行的方法来发现它,因为它的长度相对较短,而且很多人选择的密码都很差。

    使用诸如 MD5 或 SHA1 之类的散列算法来根据已知散列值验证文件内容。它们经过了极大的优化,可以在保持准确的同时尽可能快地进行验证。鉴于它们的输出空间相对有限,很容易构建一个具有已知密码和它们各自的哈希输出的数据库,彩虹表。

    在对密码进行散列之前向密码中添加盐会使彩虹表变得无用,但最近的硬件进步使蛮力查找成为一种可行的方法。这就是为什么您需要一个故意变慢且根本无法优化的散列算法。它还应该能够为更快的硬件增加负载,而不会影响验证现有密码哈希值的能力,以使其面向 future 。

    目前有两种流行的选择:
  • PBKDF2(基于密码的 key 派生函数 v2)
  • bcrypt(又名河豚)

  • 这个答案将使用 bcrypt 的例子。

    一代

    可以像这样生成密码哈希:
    $password = 'my password';
    $random = openssl_random_pseudo_bytes(18);
    $salt = sprintf('$2y$%02d$%s',
        13, // 2^n cost factor
        substr(strtr(base64_encode($random), '+', '.'), 0, 22)
    );
    
    $hash = crypt($password, $salt);
    

    盐是用 openssl_random_pseudo_bytes() 生成的形成一个随机的数据块,然后运行 ​​base64_encode()strtr()匹配所需的字母 [A-Za-z0-9/.] .

    crypt() 函数根据算法(Blowfish 的 $2y$)、成本因子(13 的因子在 3GHz 机器上大约需要 0.40 秒)和 22 个字符的盐来执行散列。

    验证

    获取包含用户信息的行后,您可以通过以下方式验证密码:
    $given_password = $_POST['password']; // the submitted password
    $db_hash = $row['Password']; // field with the password hash
    
    $given_hash = crypt($given_password, $db_hash);
    
    if (isEqual($given_hash, $db_hash)) {
        // user password verified
    }
    
    // constant time string compare
    function isEqual($str1, $str2)
    {
        $n1 = strlen($str1);
        if (strlen($str2) != $n1) {
            return false;
        }
        for ($i = 0, $diff = 0; $i != $n1; ++$i) {
            $diff |= ord($str1[$i]) ^ ord($str2[$i]);
        }
        return !$diff;
    }
    

    要验证密码,请调用 crypt()再次但您将先前计算的哈希值作为盐值传递。如果给定的密码与散列匹配,则返回值产生相同的散列。为了验证散列,通常建议使用恒定时间比较函数来避免时序攻击。

    使用 PHP 5.5 进行密码散列

    PHP 5.5 引入了 password hashing functions您可以使用它来简化上述散列方法:
    $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
    

    并验证:
    if (password_verify($given_password, $db_hash)) {
        // password valid
    }
    

    另见: password_hash() , password_verify()

    关于php - 如何在php中加密/解密数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10916284/

    有关php - 如何在php中加密/解密数据?的更多相关文章

    1. ruby - 如何在 Ruby 中顺序创建 PI - 2

      出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

    2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

      如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

    3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    6. ruby - 如何在续集中重新加载表模式? - 2

      鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

    7. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

      我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

    8. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

      我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

    9. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

      这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

    10. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

      我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

    随机推荐