这是我在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;
}
}
请帮忙!
答案 0 :(得分:2)
这种加密方法被认为是安全的吗?
没有。 Bcrypt很好,但是你的实现很危险。
如果你从这个答案中得不到任何其他信息,请记住you don't encrypt passwords, you hash them。链接的文章解释了各种加密术语和概念的细微差别。如果没有别的,如果您在其他方面遇到加密,它应该有助learn how to ask better questions。
如果您需要存储密码,请使用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()
替换为准备好的语句,即correct and effective solution to SQL Injection),您的代码可能如下所示:
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));
这是一个弱随机数生成器,不应该用于与加密相关的任何事情。
我使用弱随机数生成器在Underhanded Crypto Contest at DEFCON 23的获胜条目中创建了一个后门用户身份验证库。缺点是:如果你在距离任何加密实用程序一百英里的范围内generating a random string,则需要CSPRNG。
请告诉我是谁或误导你的人相信md5(uniqid(mt_rand(), true));
是一个安全的随机数生成器。我见过很多人都犯了同样的错误,我想阻止不良密码学建议的传播。它是某个教程吗?我真的想纠正它。
重申:md5(uniqid(mt_rand(), true));
不安全。不要使用它。
答案 1 :(得分: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;
}
我希望你理解我的代码背后的逻辑。