Я проверил, где проблема.
Прежде всего, я добавил эхо в process_login.php и удалил заголовок (‘location … protected_page.php). Функция входа в систему (в functions.php) делает свою работу и пишет ПРАВИЛЬНО: $_SESSION['user_id']
так же как $_SESSION['username']
а также $_SESSION['login_string']
,
Затем я удалил эхо в process_login.php и переписал заголовок (‘location .. to protected_page.php. (Я вернулся к исходному коду). И после этого добавил эхо в login_check перед любым, если function login_check($mysqli) {
echo "<br>userid:".$_SESSION['user_id'];
echo "<br>username:".$_SESSION['username'];
echo "<br>loginstring:".$_SESSION['login_string'];
Результат пуст, поэтому переменные SESSION записываются правильно при входе в функцию, но после заголовка на protected_page переменные, похоже, удаляются. Может быть, функция sec_session_start () работает плохо? В любом случае я буду следовать советам @Manikiran и продолжу работать, удаляя шифры или что-то в этом роде …. Спасибо @Manikiran и @lps
Я пытаюсь сделать базовую систему входа в систему. Однако то, что я сделал, не работает. По сути, система представляет собой index.php, в который вы помещаете свои учетные данные, затем php проверяет, существует ли ваш пользователь и правильный ли пароль. Затем пользователь перенаправляется на protected_page.php. Хорошо, часть аутентификации работает нормально, когда имя пользователя и пароль верны, веб-страница перенаправляет вас на protected_page.php, а когда имя пользователя и пароль не верны, страница сообщает вам об этом.
Сейчас, эта проблема: в protected_page.php, очевидно, есть сеансовый «чекер», я имею в виду, что если вы правильно соединились со своим пользователем и паролем, на странице будет показана личная html-информация, если вы не подключены, страница не должна показывать вам ничего. Это то, что не работает, несмотря на то, что вы правильно подключаетесь к пользователю, на странице сообщается, что вы не вошли в систему ….
(примечание: index.php также содержит защищенный HTML, который тоже не работает)
Код:
(примечание: sec_session_starts () предназначен для предотвращения перехвата сессии и содержит, конечно, session_start ())
index.php: (Я думаю, это работает нормально, так как он перенаправляет на protected_page (), когда он должен это сделать. Может быть, проблема в том, что функция login (…) в той части, где я устанавливаю данные сеанса …).
<?php
include_once 'includes/db_connect.php';
include_once 'includes/functions.php';
sec_session_start();
if (login_check($mysqli) == true) {
$logged = 'in';
} else {
$logged = 'out';
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Secure Login: Log In</title>
<script type="text/JavaScript" src="js/sha512.js"></script>
<script type="text/JavaScript" src="js/forms.js"></script>
</head>
<body>
<?php
if (isset($_GET['error'])) {
echo '<p class="error">Error Logging In!</p>';
}
?>
<form action="includes/process_login.php" method="post" name="login_form">
Email: <input type="text" name="email" />
Password: <input type="password"name="password"id="password"/>
<input type="button"value="Login"onclick="formhash(this.form, this.form.password);" />
</form>
<?php
if (login_check($mysqli) == true) {
echo '<p>Currently logged ' . $logged . ' as ' . htmlentities($_SESSION['username']) . '.</p>';
echo '<p>Do you want to change user? <a href="includes/logout.php">Log out</a>.</p>';
} else {
echo '<p>Currently logged ' . $logged . '.</p>';
echo "<p>If you don't have a login, please <a href='register.php'>register</a></p>";
}
?>
</body>
</html>
process_login.php (работает нормально, я думаю)
<?php
include_once 'db_connect.php';
include_once 'functions.php';
sec_session_start(); // Our custom secure way of starting a PHP session.
if (isset($_POST['email'], $_POST['p'])) {
$email = $_POST['email'];
$password = $_POST['p']; // The hashed password.
if (login($email, $password, $mysqli) == true) {
// Login success
header('Location: ../protected_page.php');
} else {
// Login failed
header('Location: ../index.php?error=1');
}
} else {
// The correct POST variables were not sent to this page.
echo 'Invalid Request';
}
functions.php:
<?php
include_once 'psl-config.php';
require_once 'passwordLib.php';function sec_session_start() {
$session_name = 'sec_session_id'; // Set a custom session name
$secure = true;
// This stops JavaScript being able to access the session id.
$httponly = true;
// Forces sessions to only use cookies.
if (ini_set('session.use_only_cookies', 1) === FALSE) {
header("Location: ../error.php?err=Could not initiate a safe session (ini_set)");
exit();
}
// Gets current cookies params.
$cookieParams = session_get_cookie_params();
session_set_cookie_params($cookieParams["lifetime"],
$cookieParams["path"],
$cookieParams["domain"],
$secure,
$httponly);
// Sets the session name to the one set above.
session_name($session_name);
session_start(); // Start the PHP session
session_regenerate_id(true); // regenerated the session, delete the old one.
}function login($email, $password, $mysqli) {
// Using prepared statements means that SQL injection is not possible.
if ($stmt = $mysqli->prepare("SELECT id, username, password
FROM members
WHERE email = ?
LIMIT 1")) {
$stmt->bind_param('s', $email); // Bind "$email" to parameter.
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
// get variables from result.
$stmt->bind_result($user_id, $username, $db_password);
$stmt->fetch();
if ($stmt->num_rows == 1) {
// If the user exists we check if the account is locked
// from too many login attempts
if (checkbrute($user_id, $mysqli) == true) {
// Account is locked
// Send an email to user saying their account is locked
return false;
} else {
// Check if the password in the database matches
// the password the user submitted. We are using
// the password_verify function to avoid timing attacks.
if (password_verify($password, $db_password)) {
// Password is correct!
// Get the user-agent string of the user.
$user_browser = $_SERVER['HTTP_USER_AGENT'];
// XSS protection as we might print this value
$user_id = preg_replace("/[^0-9]+/", "", $user_id);
$_SESSION['user_id'] = $user_id;
// XSS protection as we might print this value
$username = preg_replace("/[^a-zA-Z0-9_\-]+/",
"",
$username);
$_SESSION['username'] = $username;
$_SESSION['login_string'] = hash('sha512',
$db_password . $user_browser);
// Login successful.
return true;
} else {
// Password is not correct
// We record this attempt in the database
$now = time();
$mysqli->query("INSERT INTO login_attempts(user_id, time)
VALUES ('$user_id', '$now')");
return false;
}
}
} else {
// No user exists.
return false;
}
}
}
function checkbrute($user_id, $mysqli) {
// Get timestamp of current time
$now = time();
// All login attempts are counted from the past 2 hours.
$valid_attempts = $now - (2 * 60 * 60);
if ($stmt = $mysqli->prepare("SELECT time
FROM login_attempts
WHERE user_id = ?
AND time > '$valid_attempts'")) {
$stmt->bind_param('i', $user_id);
// Execute the prepared query.
$stmt->execute();
$stmt->store_result();
// If there have been more than 5 failed logins
if ($stmt->num_rows > 5) {
return true;
} else {
return false;
}
}
}
function login_check($mysqli) {
// Check if all session variables are set
if (isset($_SESSION['user_id'],
$_SESSION['username'],
$_SESSION['login_string'])) {
$user_id = $_SESSION['user_id'];
$login_string = $_SESSION['login_string'];
$username = $_SESSION['username'];
// Get the user-agent string of the user.
$user_browser = $_SERVER['HTTP_USER_AGENT'];
if ($stmt = $mysqli->prepare("SELECT password
FROM members
WHERE id = ? LIMIT 1")) {
// Bind "$user_id" to parameter.
$stmt->bind_param('i', $user_id);
$stmt->execute(); // Execute the prepared query.
$stmt->store_result();
if ($stmt->num_rows == 1) {
// If the user exists get variables from result.
$stmt->bind_result($password);
$stmt->fetch();
$login_check = hash('sha512', $password . $user_browser);
if (hash_equals($login_check, $login_string) ){
// Logged In!!!!
return true;
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
} else {
// Not logged in
return false;
}
}
function esc_url($url) {
if ('' == $url) {
return $url;
}
$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
$strip = array('%0d', '%0a', '%0D', '%0A');
$url = (string) $url;
$count = 1;
while ($count) {
$url = str_replace($strip, '', $url, $count);
}
$url = str_replace(';//', '://', $url);
$url = htmlentities($url);
$url = str_replace('&', '&', $url);
$url = str_replace("'", ''', $url);
if ($url[0] !== '/') {
// We're only interested in relative links from $_SERVER['PHP_SELF']
return '';
} else {
return $url;
}
}
protected_page.php: (или, возможно, проблема в login_check, где он проверяет данные сеанса
<?php
include_once 'includes/db_connect.php';
include_once 'includes/functions.php';
sec_session_start();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Secure Login: Protected Page</title>
<link rel="stylesheet" href="styles/main.css" />
</head>
<body>
<?php if (login_check($mysqli) == true) : ?>
<p>Welcome <?php echo htmlentities($_SESSION['username']); ?>!</p>
<p>
This is an example protected page. To access this page, users
must be logged in. At some stage, we'll also check the role of
the user, so pages will be able to determine the type of user
authorised to access the page.
</p>
<p>Return to <a href="index.php">login page</a></p>
<?php else : ?>
<p>
<span class="error">You are not authorized to access this page.</span> Please <a href="index.php">login</a>.
</p>
<?php endif; ?>
</body>
</html>
psl-config и db_connect соединяются с базой данных, и она работает нормально, я думаю. passwordLib.php — библиотека, так как мой сервер имеет php 5.4, и я использую функции php 5. Эта библиотека предназначена, чтобы помочь с некоторыми паролями шифрования или что-то еще, и я думаю, проблема не здесь ни. В любом случае я поставил код:
<?php
/**
* PHP 5.5-like password hashing functions
*
* Provides a password_hash() and password_verify() function as appeared in PHP 5.5.0
*
* See: http://php.net/password_hash and http://php.net/password_verify
*
* @link https://github.com/Antnee/phpPasswordHashingLib
*/
require_once('passwordLibClass.php');
if (!function_exists('password_hash')){
function password_hash($password, $algo=PASSWORD_DEFAULT, $options=array()){
$crypt = NEW Antnee\PhpPasswordLib\PhpPasswordLib;
$crypt->setAlgorithm($algo);
$debug = isset($options['debug'])
? $options['debug']
: NULL;
$password = $crypt->generateCryptPassword($password, $options, $debug);
return $password;
}
}
if (!function_exists('password_verify')){
function password_verify($password, $hash){
return (crypt($password, $hash) === $hash);
}
}
if (!function_exists('password_needs_rehash')){
function password_needs_rehash($hash, $algo, $options=array()){
$crypt = NEW Antnee\PhpPasswordLib\PhpPasswordLib;
return !$crypt->verifyCryptSetting($hash, $algo, $options);
}
}
if (!function_exists('password_get_info')){
function password_get_info($hash){
$crypt = NEW Antnee\PhpPasswordLib\PhpPasswordLib;
return $crypt->getInfo($hash);
}
}
passwordlibclass.php:
<?php
/**
* PHP 5.5-like password hashing functions
*
* Provides a password_hash() and password_verify() function as appeared in PHP 5.5.0
*
* See: http://php.net/password_hash and http://php.net/password_verify
*
* @link https://github.com/Antnee/phpPasswordHashingLib
*/namespace Antnee\PhpPasswordLib;
if (!defined('PASSWORD_BCRYPT')) define('PASSWORD_BCRYPT', 1);
// Note that SHA hashes are not implemented in password_hash() or password_verify() in PHP 5.5
// and are not recommended for use. Recommend only the default BCrypt option
if (!defined('PASSWORD_SHA256')) define('PASSWORD_SHA256', -1);
if (!defined('PASSWORD_SHA512')) define('PASSWORD_SHA512', -2);
if (!defined('PASSWORD_DEFAULT')) define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
class PhpPasswordLib{
CONST BLOWFISH_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
CONST BLOWFISH_CRYPT_SETTING = '$2a$';
CONST BLOWFISH_CRYPT_SETTING_ALT = '$2y$'; // Available from PHP 5.3.7
CONST BLOWFISH_ROUNDS = 10;
CONST BLOWFISH_NAME = 'bcrypt';
// Note that SHA hashes are not implemented in password_hash() or password_verify() in PHP 5.5
// and are not recommended for use. Recommend only the default BCrypt option
CONST SHA256_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
CONST SHA256_CRYPT_SETTING = '$5$';
CONST SHA256_ROUNDS = 5000;
CONST SHA256_NAME = 'sha256';
CONST SHA512_CHAR_RANGE = './0123456789ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
CONST SHA512_CRYPT_SETTING = '$6$';
CONST SHA512_ROUNDS = 5000;
CONST SHA512_NAME = 'sha512';/**
* Default Crypt Algorithm
*
* @var INT
*/
private $algorithm = PASSWORD_BCRYPT;/**
* Name of the current algorithm
*
* @var STRING
*/
private $algoName;/**
* Setting for PHP Crypt function, defines algorithm
*
* Default setting is '$2a$' : BCrypt
*
* @var STRING
*/
protected $cryptSetting;/**
* Setting for PHP Crypt function, defines processing cost
*
* Default setting is '08$' for BCrypt rounds
*
* @var INT
*/
protected $rounds;/**
* Salt Character Count for Crypt Functions
*
* @var INT
*/
protected $addSaltChars;/**
* Salt Character Range for Crypt Functions
*
* @var STRING
*/
protected $saltCharRange;/**
* Class Constructor
*/
public function __construct(){
// Initialise default algorithm
$this->setAlgorithm($this->algorithm);
}/**
* Generate Crypt Password
*
* @param STRING $password The password to encode
* @param ARRAY $options Cost value, and Salt if required
* @param BOOL $debug If true will return time to calculate hash
* @return STRING The encoded password
*/
public function generateCryptPassword($password, $options = array(), $debug = FALSE){
$startTime = microtime(TRUE);
if (isset($options['cost'])) $this->setCost($options['cost']);
$salt = $this->cryptSalt(@$options['salt']);
$crypt = crypt($password, $salt);
$endTime = microtime(TRUE);
if ($debug){
$calcTime = $endTime - $startTime;
return $calcTime;
}
return $crypt;
}/**
* Generate Crypt Salt
*
* Generates a salt suitable for Crypt using the defined crypt settings
*
* @param STRING $salt Override random salt with predefined value
* @return STRING
*/
public function cryptSalt($salt=NULL){
if (empty($salt)){
for ($i = 0; $i<$this->addSaltChars; $i++){
$salt .= $this->saltCharRange[rand(0,(strlen($this->saltCharRange)-1))];
}
}
$salt = $this->cryptSetting.$this->rounds.$salt.'$';
return $salt;
}/**
* Set Crypt Setting
*
* @param type $setting
* @return \Antnee\PhpPasswordLib\PhpPasswordLib
*/
public function cryptSetting($setting){
$this->cryptSetting = $setting;
return $this;
}/**
* Salt Character Count
*
* @param INT $count Number of characters to set
* @return \Antnee\PhpPasswordLib\PhpPasswordLib|boolean
*/
public function addSaltChars($count){
if (is_int($count)){
$this->addSaltChars = $count;
return $this;
} else {
return FALSE;
}
}/**
* Salt Character Range
*
* @param STRING $chars
* @return \Antnee\PhpPasswordLib\PhpPasswordLib|boolean
*/
public function saltCharRange($chars){
if (is_string($chars)){
$this->saltCharRange = $chars;
return $this;
} else {
return FALSE;
}
}/**
* Set Crypt Algorithm
*
* @param INT $algo
* @return \Antnee\PhpPasswordLib\PhpPasswordLib
*/
public function setAlgorithm($algo=NULL){
switch ($algo){
case PASSWORD_SHA256:
$this->algorithm = PASSWORD_SHA256;
$this->cryptSetting(self::SHA256_CRYPT_SETTING);
$this->setCost(self::SHA256_ROUNDS);
$this->addSaltChars(16);
$this->saltCharRange(self::SHA256_CHAR_RANGE);
$this->algoName = self::SHA256_NAME;
break;
case PASSWORD_SHA512:
$this->algorithm = PASSWORD_SHA512;
$this->cryptSetting(self::SHA512_CRYPT_SETTING);
$this->setCost(self::SHA512_ROUNDS);
$this->addSaltChars(16);
$this->saltCharRange(self::SHA512_CHAR_RANGE);
$this->algoName = self::SHA512_NAME;
break;
case PASSWORD_BCRYPT:
default:
$this->algorithm = PASSWORD_BCRYPT;
if (version_compare(PHP_VERSION, '5.3.7') >= 1){
// Use improved Blowfish algorithm if supported
$this->cryptSetting(self::BLOWFISH_CRYPT_SETTING_ALT);
} else {
$this->cryptSetting(self::BLOWFISH_CRYPT_SETTING);
}
$this->setCost(self::BLOWFISH_ROUNDS);
$this->addSaltChars(22);
$this->saltCharRange(self::BLOWFISH_CHAR_RANGE);
$this->algoName = self::BLOWFISH_NAME;
break;
}
return $this;
}/**
* Set Cost
*
* @todo implement
*
* @return \Antnee\PhpPasswordLib\PhpPasswordLib
*/
public function setCost($rounds){
switch ($this->algorithm){
case PASSWORD_BCRYPT:
$this->rounds = $this->setBlowfishCost($rounds);
break;
case PASSWORD_SHA256:
case PASSWORD_SHA512:
$this->rounds = $this->setShaCost($rounds);
break;
}
return $this;
}/**
* Set Blowfish hash cost
*
* Minimum 4, maximum 31. Value is base-2 log of actual number of rounds, so
* 4 = 16, 8 = 256, 16 = 65,536 and 31 = 2,147,483,648
* Defaults to 8 if value is out of range or incorrect type
*
* @param int $rounds
* @return STRING
*/
private function setBlowfishCost($rounds){
if (!is_int($rounds) || $rounds < 4 || $rounds > 31){
$rounds = $rounds = self::BLOWFISH_ROUNDS;
}
return sprintf("%02d", $rounds)."$";
}/**
* Set SHA hash cost
*
* Minimum 1000, maximum 999,999,999
* Defaults to 5000 if value is out of range or incorrect type
*
* @param INT $rounds
* @return STRING
*/
private function setShaCost($rounds){
if (!is_int($rounds) || $rounds < 1000 || $rounds > 999999999){
switch ($this->algorithm){
case PASSWORD_SHA256:
$rounds = self::SHA256_ROUNDS;
case PASSWORD_SHA512:
default:
$rounds = self::SHA512_ROUNDS;
}
}
return "rounds=" . $rounds ."$";
}/**
* Get hash info
*
* @param STRING $hash
* @return ARRAY
*/
public function getInfo($hash){
$params = explode("$", $hash);
if (count($params) < 4) return FALSE;
switch ($params['1']){
case '2a':
case '2y':
case '2x':
$algo = PASSWORD_BCRYPT;
$algoName = self::BLOWFISH_NAME;
break;
case '5':
$algo = PASSWORD_SHA256;
$algoName = self::SHA256_NAME;
break;
case '6':
$algo = PASSWORD_SHA512;
$algoName = self::SHA512_NAME;
break;
default:
return FALSE;
}
$cost = preg_replace("/[^0-9,.]/", "", $params['2']);
return array(
'algo' => $algo,
'algoName' => $algoName,
'options' => array(
'cost' => $cost
),
);
}/**
* Verify Crypt Setting
*
* Checks that the hash provided is encrypted at the current settings or not,
* returning BOOL accordingly
*
* @param STRING $hash
* @return BOOL
*/
public function verifyCryptSetting($hash, $algo, $options=array()){
$this->setAlgorithm($algo);
if (isset($options['cost'])) $this->setCost($options['cost']);
$setting = $this->cryptSetting.$this->rounds;
return (substr($hash, 0, strlen($setting)) === $setting);
}
}
Заключительный комментарий: я обещаю, что много искал, но не нашел этой конкретной проблемы в других вопросах. Спасибо.
Основы системы входа в систему с сессиями работает примерно так:
Проверьте, успешно ли выполнен вход в систему, и, если это так, установите переменные сеанса. Включите это на странице входа или в свой сценарий, на который отправляется ваша форма:
<?php
session_start();
if(isset($_SESSION['usr_id'])!="") {
header("Location: index.php");
}
include_once 'dbconnect.php';
//Login
if (isset($_POST['login'])) {
$email = mysqli_real_escape_string($con, $_POST['email']);
$password = mysqli_real_escape_string($con, $_POST['password']);
$result = mysqli_query($con, "SELECT * FROM users WHERE email = '" . $email. "' and password = '" . md5($password) . "'");
if ($row = mysqli_fetch_array($result)) {
$_SESSION['usr_id'] = $row['id'];
$_SESSION['usr_name'] = $row['name'];
header("Location: index.php");
} else {
$errormsg = "Incorrect Email or Password!!!";
}
}
?>
Затем вы проверяете, существует ли сеанс на каждой защищенной странице. Если переменная сеанса не существует, перенаправьте на страницу входа.
<?php
session_start();
include_once 'dbconnect.php';
if(!isset($_SESSION['usr_id'])) {
header("Location: login.php");
}
?>
Надеюсь, поможет.
Проблема в том, что hash_equals () работает не очень хорошо, несмотря на то, что две сравниваемые переменные абсолютно одинаковы, эта функция возвращает false. Я пытался изменить его на разные библиотеки, но никак не мог. (Я помню, что мой хост имеет php 5.4, и я не могу использовать функцию password_hash ().
Единственное решение — это изменить это или найти библиотеку, которая правильно заменяет password_hash ().