Перейти к содержимому



Как сделать авторизацию на сайте?

#31 tro9an

tro9an
  • Пользователь
  • 34 сообщений
  • Репутация: 0
0

Отправлено 21 Сентябрь 2010 - 20:52

Здрасте всем, пару дней назад закончил учить php, и захотель что-нибудь написать, попробовал авторизацию, и вот что получилось:
(просьба сильно не ругать т.к это моя первая работа, а лучше подскажите как улучшить код, и вообще подходит ли он для авторизации)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title>Главная страница</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>

<body>
<table width="800" align="center" class="border_table">
<tr>
<td> </td>
</tr>
<tr>
<td><table width="100%">
<tr>
<td width="76%" height="20"><table width="250" align="center" class="border_table">
<tr>
<td>
<form action="vhod.php" method="post">
<p align="center" >
<label>
<strong>Ведите логин:</strong>
</label>
<input name="login" type="text" size="20" maxlength="20" />
</p>
<p align="center">
<label>
<strong>Ведите пароль:</strong>
</label>
<input name="pass" type="text" size="20" maxlength="20" />
</p>
<p class="knopka_vhod">
<label>
<input name="submit" type="submit" value="Вход" />
</label>
<a href="regestracion.php" class="reg">Регестрация</a>
</p>

</form>

</td>
</tr>
</table>

</td>
<td width="24%" class="border_td_right"> </td>
</tr>
</table></td>
</tr>
<tr>
<td> </td>
</tr>
</table>
</body>
</html>


<?php
session_start();
include ("db.php");
/*Соединяюсь с базой*/
$login=$_POST['login'];
$pass=$_POST['pass'];
$submit=$_POST['submit'];
/*Преобразую глобальные переменные в обычные*/

$login=stripslashes($login);
$pass=stripslashes($pass);
$login=htmlspecialchars($login);
$pass=htmlspecialchars($pass);
/*Защищаю базу от PHP,MySQL и т.д кодов*/
/*Проверяю нажата ли кнопка*/
if (isset ($submit)) {
$result=mysql_query("SELECT name,pass FROM user_info WHERE name='$login' AND pass='$pass'",$db);
$myrow=mysql_fetch_array($result);
if (mysql_num_rows($result)>0) {
if ($myrow['name']==$login and $myrow['pass']==$pass){
$_SESSION['ok'] = 1;
$filename = $_SESSION['url'];
echo "<meta http-equiv='refresh' content='0; url=login.php'>";
echo "<a href='$filename'>Нажмите ссылку, если Ваш браузер не поддерживает автоматическую переадресацию!</a>";
}
else{
exit("Логин и пароль введены неверно");
}
}
else {
exit("Такого пользователя не существует");
}
}
else {
exit();
}
?>

  • 0

#32 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 21 Сентябрь 2010 - 22:30

tro9an,
1) Вы делаете лишние брыкания. Почитайте что делает функция stripslashes и для чего она используется.
Обычно при запросе к БД, где используются данные пришедшие от пользователя, они экранируются - для этого служат функции addslashes, mysql_escape_string, mysql_real_escape_string. Конечно нужно учитывать магические кавычки, если они включены, то экранировать не стоит, иначе получите данные, не соответствующие реальным.
2) Пароль обычно хранят в базе в шифрованном виде, обычно используют необратимое шифрование MD5. Вы же используете пароль без шифровки.
3) для login и pass не нужно применять функцию htmlspecialchars - она заменяет HTML символы, эквивалентами. Зачем вам подобным образом защищаться, если есть экранировка?
4) Зачем вам сначала переписывать из одного места памяти в другое данные, а затем вторым действием обрабатывать данные. Нельзя ли это сделать в одно действие?
5) Переадресацию можно сделать с помощью функции в PHP - header(), чем печатать meta.

Для отдельного скрипта авторизации данный модуль конечно подойдет. Но встраивать подобный код в проекты - потеря читаемости и зависимости. Для начала в самый раз. Советую вам разработать скрипт простейшего форума, поднаберетесь опыта ;) Удачи в начинаниях!
  • 0

#33 tro9an

tro9an
  • Пользователь
  • 34 сообщений
  • Репутация: 0

Отправлено 21 Сентябрь 2010 - 22:40

ZiTosS, спасибо что указали недостатки, буду исправлятся.
Я не совсем понял фразу "Простейший форум", поясните пожалуйста что-он в себе должен содержать?(подробно и по пунктам)
Как зашифровать пароль?
  • 0

#34 gaaarfild

gaaarfild
  • Пользователь
  • 596 сообщений
  • Репутация: 0

Отправлено 26 Сентябрь 2010 - 15:08

Зашифровать пароль можно функцией md5();
Делается это примерно так

$encr_pass = md5($_POST['password']);

В переменной $encr_pass вы получите зашифрованный пароль.

А простейший форум должен содержать пользователей, регистрацию новых пользователей. Возможность создать тему и возможность другим пользователям отвечать на нее.
И так же, в дальнейшем, возможность редактировать пользователям только свои посты, а пользователям с админскими правами редактировать любые посты и удалять их.
Вот и все.
Так же, для развития, регистрацию можно сделать с подтверждением по email.

$login=$_POST['login'];
$pass=$_POST['pass'];
$submit=$_POST['submit'];

Вот эти строчки - абсолютно бессмысленные. Используйте сразу данные глобального массива POST. Зачем создавать 2 переменные без всяких изменений?
  • 0

#35 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 26 Сентябрь 2010 - 15:27

tro9an, Как зашифровать пароль описали выше. Данный хеш MD5 нужно занести в таблицу при регистрации. А при авторизации проводить над полем пароля эту же операцию хеширования md5() и затем уже сравнивать с данными в базе.

По поводу простейшего форума:
1) Есть пользовательская часть, для общения в которой нужно авторизоваться или зарегистрироваться и есть админская панель, вход в которую ограничен данными пароля админа (тут есть 2 варианта, либо в таблице пользователей имеется поле is_admin, либо создавать отдельную табличку админов, первый вариант лучше)
2) Гости (неавторизованные пользователи) могут просматривать темы форума, но не могут в них отвечать. Пользователи могут как просматривать темы, так и отвечать в них. А так же создавать собственные темы.
3) Управление темами форума осуществляется через админ-панель. Администратор, войдя в панель имеет право удалить/изменить: тему, данные пользователя и т.д.

Так же ещё можете ввести категории (часто их и называют форумами). То есть в каждой категории темы определенной тематики.
Если вам не нравится идея с админ-панелью, можете реализовать управление в пользовательской части ;) Тут уж вам решать.
  • 0

#36 tro9an

tro9an
  • Пользователь
  • 34 сообщений
  • Репутация: 0

Отправлено 03 Октябрь 2010 - 17:28

ZiTosS, Вы написали:

в таблице пользователей имеется поле is_admin


я несовсем понимаю что мне даст создание дополнительного поля, обьясните пожалуйста
  • 0

#37 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 04 Октябрь 2010 - 23:20

tro9an, это поле - флаг. 1 - пользователь имеет доступ к админке. 0 - пользователь НЕ имеет доступ к админке.
Вот за что отвечает это поле. Естественно хотя бы один администратор на форуме должен быть, поэтому значение поля is_admin не может быть нулевым у всех записей таблицы пользователей.
Затем, когда пользователь попытается войти в админку, вам достаточно проверить данное поле таблицы.
  • 0

#38 marker

marker
  • Пользователь
  • 52 сообщений
  • Репутация: 0

Отправлено 21 Октябрь 2010 - 20:28

Посмотрел все варианты и решил предложить кое какую поправку и предложить свое:

ajax -> login.php

$mysqli = new mysqli($h_server, $h_user, $h_pass, $h_db);

if($_SESSION['online']==true) die( 'Вы уже авторизованы.' );

if((isset($_COOKIE['login_time']) && $_COOKIE['login_time']>2)) die( 'Вы вводите неверные данные слишком часто.' );
// Если прочитаете что написано ниже то поймете почему так

$form['login'] = trim($_POST['login']);
$form['password'] = trim($_POST['password']);
$hash = md5($form['login'].$form['password']);
// Вот та упущенная деталь, хеш зависит от логина и пароля в совокупности и не нужно md5(md5(..)) - это уже слишком.

if(empty($form['login']) || empty($form['password'])) die( 'Пожалуйста введите логин и пароль.' );

$result = $mysqli->query("SELECT * FROM `users` WHERE `login` = '".$form['login']."' AND `hash` = '".$hash."' LIMIT 1") or die( $mysqli->error );
if($result->num_rows > 0){
$row = $result->fetch_array(MYSQLI_ASSOC);
$_SESSION['online']=true;
$_SESSION['login']=$row['login'];
$_SESSION['name']=$row['name'];
$_SESSION['email']=$row['email'];
$_SESSION['id']=$row['id'];
$_SESSION['level']=$row['level'];
$_SESSION['avatar']=$row['avatar'];

// Так потому что некоторые столбцы записывать в сессию не нужно

$sessionid = session_id();
$userip = $_SERVER["REMOTE_ADDR"];

setcookie("login_time", ""); // Очищаем защиту от ошибок
setcookie('hashid', $sessionid, time()+60*60*24*2, "/"); // Ставим индентификатор сессии, чтобы брать его из базы
setcookie('ngh', $row['id'], time()+60*60*24*2, "/"); // Индентификатор id пользователя

$mysqli->query("UPDATE `users` SET `sid`='".session_id()."', `ip`='".$userip."' WHERE `id`='".$row['id']."' LIMIT 1");
// Здесь можно еще добавить к обновлению новую дату, чтобы проверять в будущем онлайн статистику

print "complete"; // так как код выполняеться в httprequest я отправляю завершающий тег, чтобы с помощью js его поймать и обновить страницу

}else{
// При неверном вводе логина ставлю куку со счетчиком попыток
if(!isset($_COOKIE['login_time'])){
setcookie("login_time", "1", time() + 60);
}else{
setcookie("login_time", $_COOKIE['login_time'] + 1, time() + 60, "/");
}
die( 'Неверные данные.' );
}

$result->close();

$mysqli->close();


init.php -> marker class -> firstlogin

$mysqli = new mysqli($h_server, $h_user, $h_pass, $h_db);

if($_SESSION['online']==true){
// Если пользователь уже авторизован просто обновляем дату его пребывания
$mysqli->query("UPDATE `users` SET `lastupdate`='".time()."' WHERE `id`='".$_SESSION['id']."' LIMIT 1");
}else{
// Чтобы избежать иньекции в MYSQL и вообще лишнего запроса проверяем данные в куки
if(isset($_COOKIE['ngh']) AND preg_match("/^[0-9]+$/",$_COOKIE['ngh']) AND preg_match("/^[a-zA-Z0-9]+$/",$_COOKIE['hashid'])){
if($result = $mysqli->query("SELECT * FROM `users` WHERE `id`='".$_COOKIE['ngh']."' AND `sid`='".$_COOKIE['hashid']."' LIMIT 1")){
if($result->num_rows > 0){
$row = $result->fetch_array(MYSQLI_ASSOC);
$_SESSION['online']=true;
$_SESSION['login']=$row['login'];
$_SESSION['name']=$row['name'];
$_SESSION['email']=$row['email'];
$_SESSION['id']=$row['id'];
$_SESSION['accos']=$row['level'];
$_SESSION['avatar']=$row['avatar'];

$sessionid = session_id();
$userip = $_SERVER["REMOTE_ADDR"];

setcookie('hashid', $sessionid, time()+60*60*24*5, "/");
setcookie('ngh', $row['id'], time()+60*60*24*5, "/");

$mysqli->query("UPDATE `users` SET `sid`='".session_id()."', `ip`='".$userip."' WHERE `id`='".$row['id']."' LIMIT 1");
}else{
setcookie('ngh', '');
}
}else die( $mysqli->error );
}
}

$mysqli->close();


Сильно не ругайте. :lol:
  • 0

#39 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 25 Октябрь 2010 - 13:26

marker,
1) Рассмотрен частный случай на библиотеке mysqli, на стандартном синтаксисе придётся ещё делать экранировку, которая у вас, как я понимаю встроена в библиотеку mysqli или же у вас на сервере magic_quotes установлен в On.
2) Так же вы показали файл авторизации на Ajax, но не показали клиентскую часть обработки.
3) Ваш скрипт не идеален, с точки зрения того, что если кто-либо украдет куки другого человека, то он получает доступ к сайту от имени этого пользователя. Обычно, для реализации восстановления сессии уже авторизованного пользователя через определенный промежуток времени(сессия прервалась), в куки надо хранить не просто id сессии, но и данные об ОС + браузер + IP. IP не обязателен(Для пущей защиты).
4) Вот это считаю излишним:
preg_match("/^[0-9]+$/",$_COOKIE['ngh'])

Что же просто не сделать так:
$ngh = intval($_COOKIE['ngh']);

5) Хранить бан при исчерпании определенного числа попыток в куках неправильно. Т.к. куки запросто чистятся и подбор продолжается :lol:
Лучше создать отдельную табличку с IP адресами + неверные попытки с этих IP. Как только кол-во неверных попыток превышает норму, пользователя блокируют и больше не позволяют вводить данные для авторизации. Очищение данной таблицы через определенный промежуток времени лучше всего доверить планировщику, например CRON.
  • 0

#40 marker

marker
  • Пользователь
  • 52 сообщений
  • Репутация: 0

Отправлено 26 Октябрь 2010 - 01:24

Хранить бан при исчерпании определенного числа попыток в куках неправильно. Т.к. куки запросто чистятся и подбор продолжается :)
Лучше создать отдельную табличку с IP адресами + неверные попытки с этих IP. Как только кол-во неверных попыток превышает норму, пользователя блокируют и больше не позволяют вводить данные для авторизации. Очищение данной таблицы через определенный промежуток времени лучше всего доверить планировщику, например CRON.

Не хачу нагружать БД запросами :lol: . а Cron же нельзя управлять с помощью php?
  • 0

robot

robot
  • Пользователь PRO
  • 2 652 сообщений
  • Репутация: 85


Оформление форума – IPBSkins.ru