这种加密方式安全吗?

问题描述 投票:0回答:2

这是我刚刚在 php 中制作的加密方式,用它我将密码存储在数据库中 这是我的登录页面代码

<?php
session_start();
require("db_connection.php");
require("functions.php");

if (!$connect) {
    die("Connection failed: " . mysqli_connect_error());
}

if(isset($_SESSION['name'])){
    header("location: admin.php");
}


if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    if ($_POST['username']==""||$_POST['password']==""){
        header ("location: login.php");
    }

    $username = mysqli_real_escape_string($connect,$_POST['username']);
    $password = $_POST['password'];

    $found_admin = attempt_login($username,$password);

    if($found_admin) {
        $_SESSION['username'] = $found_admin['username'];
        $_SESSION['name'] = $found_admin['name'];
        header("location: admin.php");
    }else {
        $status =  "Please verify your credentials";
    }
}

?>  

我已经在单独的文件中创建了该函数,这里的任何人都可以给我建议来调整此代码 我只学习了大约一周的语言。 计划启动面向对象的 php。 功能是

function encrypt_password ($password){
    $hash_form = "$2y$10$";
    $salt_length = 22;
    $salt = generate_salt($salt_length);
    $form_and_salt = $hash_form . $salt;
    $hash = crypt($password,$form_and_salt);
    return $hash;
}

function generate_salt ($length){
    $unique_random_string = md5(uniqid(mt_rand(),true));
    $base64_string = base64_encode($unique_random_string);
    $modified_base64_string = str_replace('+', '.',$base64_string);
    $salt = substr($modified_base64_string,0,$length);
    return $salt;
}

function password_check ($password, $existing_hash){
    $hash = crypt($password,$existing_hash);
    if($hash===$existing_hash){
        return true;
    }else {
        return false;
    }
}

function find_admin_by_username($username){
    global $connect;
    $safe_username = mysqli_real_escape_string($connect,$username);

    $query = "SELECT * FROM admins WHERE username='{$safe_username}' LIMIT 1";
    $result = mysqli_query($connect , $query);
    if($admin = mysqli_fetch_assoc($result)){
        return $admin;
    }else{
        return null;
    }
}

function attempt_login($username, $password){
    $admin = find_admin_by_username($username);
    if ($admin){
        if(password_check($password,$admin['password'])){
            return $admin;
        }else {
            return false;
        }
    }else {
        return false;
    }
}
php mysql blowfish crypt password-hash
2个回答
2
投票

这种加密方式安全吗?

不。 Bcrypt 很好,但你的实现很危险。


如果您从这个答案中没有学到任何其他信息,请记住您不加密密码,而是对它们进行哈希处理。链接的文章解释了各种密码学术语和概念的细微差别。如果不出意外,如果您在其他领域遇到密码学,它应该会帮助您学习如何提出更好的问题

如果您需要存储密码,请使用

password_hash()
password_verify()
不要编写自己的加密功能,除非您知道自己在做什么。

WordPress 应该已经包含 password_compat (它允许您使用 PHP 5.3.7+ 中的推荐功能),但截至 2015 年 8 月,您应该已经使用 PHP 5.5,所以这是一个没有实际意义的问题。

如果您使用的是较旧版本的 PHP,请迫使您的网络主机立即更新到 5.5 或 5.6。如果您这样做,您将使互联网变得更安全。

使用推荐的函数(并用准备好的语句替换

mysqli_real_escape_string()
,这是正确且有效的 SQL 注入解决方案),您的代码可能如下所示:

function find_admin_by_username($username)
{
    global $connect;

    // Create a prepared statement with our query structure
    $stmt = mysqli_prepare($connect, "SELECT * FROM admins WHERE username = ? LIMIT 1");

    // Bind a string parameter (hence the "s"):
    mysqli_stmt_bind_param($stmt, "s", $username);

    // If the query was successful
    if (mysqli_stmt_execute($stmt)) {
        // Grab the result from the prepared statement...
        $result = mysqli_stmt_get_result($stmt);

        // And then return a single row from the table
        return mysqli_fetch_assoc($result);
    }
}

function attempt_login($username, $password)
{
    $admin = find_admin_by_username($username);
    if ($admin) {
        if (password_verify($password, $admin['password'])) {
            return $admin;
        }
    }
    return false;
}     

我将继续对您的代码进行注释(我的注释带有

##
前缀)。

function encrypt_password ($password){ ## You're hashing, not encrypting
    $hash_form = "$2y$10$"; ## Why is the cost of 10 hard-coded? Some people might want 11 or 12.
    $salt_length = 22; ## This doesn't need to be a variable
    $salt = generate_salt($salt_length);
    $form_and_salt = $hash_form . $salt; ## This is backwards
    $hash = crypt($password,$form_and_salt);
    return $hash;
}

function generate_salt ($length){
    $unique_random_string = md5(uniqid(mt_rand(),true)); ## This is weak. See footnote
    $base64_string = base64_encode($unique_random_string);
    $modified_base64_string = str_replace('+', '.',$base64_string);
    $salt = substr($modified_base64_string,0,$length);
    return $salt;
}

function password_check ($password, $existing_hash){
    $hash = crypt($password,$existing_hash);
    if($hash===$existing_hash){ ## Timing side-channel (albeit not a practical one)
        return true;
    }else {
        return false;
    }
}

function find_admin_by_username($username){
    global $connect;

    ## Editing and concatenating strings fails more often than prepared statements.
    ## If you want to be conservative about security, get very familiar with
    ## mysqli_prepare() and friends. You can almost never have to use
    ## mysqli_real_escape_string() again if you're careful enough.
    $safe_username = mysqli_real_escape_string($connect,$username);

    $query = "SELECT * FROM admins WHERE username='{$safe_username}' LIMIT 1";
    $result = mysqli_query($connect , $query);
    if($admin = mysqli_fetch_assoc($result)){
        return $admin;
    }else{ ## You can just leave this outside the if block
        return null; ## If you don't return anything, null is returned
    }
}

function attempt_login($username, $password){
    $admin = find_admin_by_username($username);
    if ($admin){
        if(password_check($password,$admin['password'])){
            return $admin;
        }else { ## unnecessary
            return false; ## unnecessary
        }
    }else { ## unnecessary
        return false; ## unnecessary
    }
} 

脚注
md5(uniqid(mt_rand(), true));

这是一个弱随机数生成器,切勿用于与密码学相关的任何内容。

我使用弱随机数生成器在 DEFCON 23 Underhanded Crypto Contest 的获奖作品中创建了一个后门用户身份验证库。简而言之:如果您要在任何加密实用程序的一百英里范围内生成随机字符串,则需要一个CSPRNG

请告诉我谁或什么误导了您相信

md5(uniqid(mt_rand(), true));

是一个安全的随机数生成器。我看到很多人犯了同样的错误,我想阻止糟糕的密码学建议的传播。某处有教程吗?我真的很想纠正它。

重申:md5(uniqid(mt_rand(), true));

并不安全。不要使用它。

TL;博士

    使用 PHP 提供的密码哈希功能。
  • 不要编写自己的密码学。
  • 不要使用弱随机数生成器。
  • 使用准备好的语句而不是转义。

0
投票
WpPasswordHash.php

<?php # # Portable PHP password hashing framework. # # Version 0.3 / genuine. # # Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. # # There's absolutely no warranty. # # The homepage URL for this framework is: # # http://www.openwall.com/phpass/ # # Please be sure to update the Version line if you edit this file in any way. # It is suggested that you leave the main version number intact, but indicate # your project name (after the slash) and add your own revision information. # # Please do not change the "private" password hashing method implemented in # here, thereby making your hashes incompatible. However, if you must, please # change the hash type identifier (the "$P$") to something different. # # Obviously, since this code is in the public domain, the above are not # requirements (there can be none), but merely suggestions. # class WpPasswordHash { var $itoa64; var $iteration_count_log2; var $portable_hashes; var $random_state; function WpPasswordHash($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) $iteration_count_log2 = 8; $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $random_state = microtime() . getmypid(); } function get_random_bytes($count) { $output = ''; if (($fh = @fopen('/dev/urandom', 'rb'))) { $output = fread($fh, $count); fclose($fh); } if (strlen($output) < $count) { $output = ''; for ($i = 0; $i < $count; $i += 16) { $this->random_state = md5(microtime() . $this->random_state); $output .= pack('H*', md5($this->random_state)); } $output = substr($output, 0, $count); } return $output; } function encode64($input, $count) { $output = ''; $i = 0; do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; if ($i < $count) $value |= ord($input[$i]) << 8; $output .= $this->itoa64[($value >> 6) & 0x3f]; if ($i++ >= $count) break; if ($i < $count) $value |= ord($input[$i]) << 16; $output .= $this->itoa64[($value >> 12) & 0x3f]; if ($i++ >= $count) break; $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); return $output; } function gensalt_private($input) { $output = '$P$'; $output .= $this->itoa64[min($this->iteration_count_log2 + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; $output .= $this->encode64($input, 6); return $output; } function crypt_private($password, $setting) { $output = '*0'; if (substr($setting, 0, 2) == $output) $output = '*1'; if (substr($setting, 0, 3) != '$P$') return $output; $count_log2 = strpos($this->itoa64, $setting[3]); if ($count_log2 < 7 || $count_log2 > 30) return $output; $count = 1 << $count_log2; $salt = substr($setting, 4, 8); if (strlen($salt) != 8) return $output; # We're kind of forced to use MD5 here since it's the only # cryptographic primitive available in all versions of PHP # currently in use. To implement our own low-level crypto # in PHP would result in much worse performance and # consequently in lower iteration counts and hashes that are # quicker to crack (by non-PHP code). if (PHP_VERSION >= '5') { $hash = md5($salt . $password, TRUE); do { $hash = md5($hash . $password, TRUE); } while (--$count); } else { $hash = pack('H*', md5($salt . $password)); do { $hash = pack('H*', md5($hash . $password)); } while (--$count); } $output = substr($setting, 0, 12); $output .= $this->encode64($hash, 16); return $output; } function gensalt_extended($input) { $count_log2 = min($this->iteration_count_log2 + 8, 24); # This should be odd to not reveal weak DES keys, and the # maximum valid value is (2**24 - 1) which is odd anyway. $count = (1 << $count_log2) - 1; $output = '_'; $output .= $this->itoa64[$count & 0x3f]; $output .= $this->itoa64[($count >> 6) & 0x3f]; $output .= $this->itoa64[($count >> 12) & 0x3f]; $output .= $this->itoa64[($count >> 18) & 0x3f]; $output .= $this->encode64($input, 3); return $output; } function gensalt_blowfish($input) { # This one needs to use a different order of characters and a # different encoding scheme from the one in encode64() above. # We care because the last character in our encoded string will # only represent 2 bits. While two known implementations of # bcrypt will happily accept and correct a salt string which # has the 4 unused bits set to non-zero, we do not want to take # chances and we also do not want to waste an additional byte # of entropy. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $this->iteration_count_log2 / 10); $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } function HashPassword($password) { $random = ''; if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); if (strlen($hash) == 60) return $hash; } if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { if (strlen($random) < 3) $random = $this->get_random_bytes(3); $hash = crypt($password, $this->gensalt_extended($random)); if (strlen($hash) == 20) return $hash; } if (strlen($random) < 6) $random = $this->get_random_bytes(6); $hash = $this->crypt_private($password, $this->gensalt_private($random)); if (strlen($hash) == 34) return $hash; # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new # hashes and for validating passwords against existing hashes. return '*'; } function CheckPassword($password, $stored_hash) { $hash = $this->crypt_private($password, $stored_hash); if ($hash[0] == '*') $hash = crypt($password, $stored_hash); return $hash == $stored_hash; } } ?>

然后当你想验证密码时: 单独的 php 文件:

$res = $this->_checkPassword($password, $res['User']['password']); private function _checkPassword($password, $hash) { $wp_hasher = new WpPasswordHash(8, true); $hashed_password = $wp_hasher->CheckPassword($password, $hash); return $hashed_password; }

对于加密:

private function _encryptPwd($password) { $wp_hasher = new WpPasswordHash(8, true); $hashed_password = $wp_hasher->HashPassword($password); return $hashed_password; }

我希望你理解我的代码背后的逻辑。

© www.soinside.com 2019 - 2024. All rights reserved.