Вернуться   SEO форум - оптимизация и продвижение сайтов > Web разработки > Программирование

Важная информация
Программирование - PHP, MySQL, JavaScript, CSS, HTML верстка и т.д.

Ответ
выдача ТОП 2Полезны

 
Опции темы Оценить тему Опции просмотра
Старый 03.02.2016, 00:47   #1
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы Универсальный скрипт. Безопасная авторизация (часть 2 из 3)

Внимание! Статья 2012 года. Переношу блог со своего сайта на форум.

Универсальный скрипт. Безопасная авторизация (часть 2 из 3)


Давно собирался написать эту статью, потому что много раз встречал вопросы по поводу написанию авторизации. А если был и не вопрос, то все равно скрипт ничем не блистал.
В общем, эта статья будет для вас шпаргалкой, которой можно пользоваться всегда и везде.
В прошлой теме я писал про безопасную авторизацию со стороны пользователя.
В этой статье я вам предоставлю универсальный скрипт, который подойдет на любой сайт.

Введение.


Эту водную часть я пишу для тех, кто вообще не знает, что такое PHP, и, должно быть, на эту статью попал случайно.

Многие сайты требуют, чтобы у пользователей был зарегистрированный профиль с логином и паролем... Но как происходит сама авторизация?

Вот как все устроено:
Мы заполняем форму авторизации (окошечко с логином и паролем), потом она отправляется на сервер, который обрабатывает её и сверяет с данными в базе. Если данные идентичны, сервер разрешает нам войти под своим аккаунтом.

Но не все так просто! Если вы внимательно прочитали прошлую тему про безопасную авторизацию со стороны клиента, то знаете, как легко можно потерять свои данные на плохом сервере.
  • существует вероятность перехвата пакета.
  • существует вероятность перебора паролей.
  • существует вероятность утечки базы данных.
  • существует вероятность кражи Cookies.
  • существует вероятность SQL injection.
Если бы при авторизации сервер не делал проверок, возможно было бы:
  • Перехватить пакет сниффером.
  • Взломать аккаунт программой для перебора паролей
  • Прекрасный пример утечки базы данных я описал в теме SQL injection
  • Скопировав Cookies одного браузера в другой
  • Войти под комбинацией: admin' or 5=5'+--+1 (пример SQL injection)
На самом деле защититься очень просто:
  1. Обрабатывать все полученные данные.
  2. Присваивать каждому пользователю его уникальный идентификатор.
  3. Пароль должен всегда быть захэширован.
  4. Ограниченное кол-во попыток ввода.
  5. Авторизация под протоколом SSL.

Цитата:
SSL (англ. Secure Sockets Layer — уровень защищённых сокетов) — криптографический протокол, который обеспечивает установление безопасного соединения между клиентом и сервером.
Это основные правила для написания авторизации!

Скрипт авторизации на PHP.


Я сразу даю вам скрипт, поймете – хорошо! Не поймете, ниже разберем и попробуем на примерах.

Скачать можете тут: post-6.rar

_database.php (новый синтаксис вы найдете тут http://php.net/manual/ru/mysqli.quic...onnections.php)
<?php
/* <comment>
** Класс MySql для связи с БД
** Функция GetConenction осуществляет связь БД MySQL
**
*/
class MySql
{
static protected $SqlConnection = null;
static function GetConnection()
{
if (self::$SqlConnection === null)
{
@require_once $_SERVER['DOCUMENT_ROOT'].'/core/_config_db.php';
if (!@$link = mysql_connect($database['location'], $database['login'], $database['password']))
{
exit('not connect db');
}
if (!@mysql_select_db($database['database'],$link))
{
exit('not select db');
}
mysql_set_charset("utf8");
}
return self::$SqlConnection=true;
}
static function CloseConnection()
{
if (self::$SqlConnection === true)
{
@mysql_close();
return self::$SqlConnection=null;
}
}
}
?>


Сам скрипт
/*
** Файл: obj_user.php
** Описание: Управление пользователями
** Зависимость: __database.php
** Версия: 2.4 (public)
** Создано: 14.08.2012
** Автор: Soroka Andrew
**
** Copyright by AndreiSoroka.com
** All rights reserved.
*/

class unuser
{
// база
private $db;
// Окончание работы класса
function __destruct() {
if ($this->db)
{
mysql_close();
}
}
// Соединение с базой данных
protected function db_connect(){
if (!$this->db)
{
require_once '_database.php';
MySql::GetConnection();
$this->db=true;
}
}


// Хэш пароля (после sha1 из js)
protected function hash_pass($text){
$text=md5(sha1($text)."solo".md5(sha1($text).$text {1}));
$text=md5($text{7}.$text.$text{0});
return($text);
}
// Проверка и подготовка логина
protected function modify_login($login){
if (!empty($login)){
$login=trim($login);
if (preg_match("%^([_\.\-]?[a-zA-Z0-9])+$%", $login))
{
if (strlen($login)>=3 and strlen($login)<=40)
{
return ($login);
}
}
}
unset($login);
return (false);
}
// Проверка и подготовка пароля
protected function modify_password($pass){
if (!empty($pass)){
if (preg_match("/[a-zA-Z0-9]/", $pass))
{
if (strlen($pass)==40)
{
$pass=$this->hash_pass($pass);
return ($pass);
}
}
}
unset($pass);
return (false);
}
// Добавление блокировки
protected function ban_user_add(){
$this->db_connect();
$time=time()+15*60; // время блокировки
$ban_query = mysql_query("SELECT * FROM `ban` WHERE `ip` = '{$_SERVER['REMOTE_ADDR']}'");
$ban_array = mysql_fetch_array($ban_query);
$mysql_num_rows=mysql_num_rows($ban_query);
// ранее был разблокирован
if ($mysql_num_rows !=0 and $ban_array['time']<time())
{
mysql_query("UPDATE `ban` SET `time`='$time', `number` = 1 WHERE `ip` ='{$_SERVER['REMOTE_ADDR']}';");
}
// ранее был заблокирован
elseif ($mysql_num_rows !=0)
{
$number=$ban_array['number']+1;
mysql_query("UPDATE `ban` SET `time`='$time', `number` = '$number' WHERE `ip` ='{$_SERVER['REMOTE_ADDR']}';");
}
// первая блокировка
else
{
mysql_query("INSERT INTO `ban` (`ip` ,`time`, `number`, `comment`) VALUES ('{$_SERVER['REMOTE_ADDR']}', '{$time}', 1, 'Блокировка при авторизации');");
unset($time);
}
}
// Проверка ip на блокировку
public function ban_user(){
$this->db_connect();
$ban_query = mysql_query("SELECT `time` FROM `ban` WHERE `ip` = '{$_SERVER['REMOTE_ADDR']}' AND `time` > '".time()."' AND `number`>4");
if (mysql_num_rows($ban_query)<1){
unset($ban_query);
return (false);
}
$ban_query=mysql_fetch_array($ban_query);
return ($ban_query['time']);
}
// Авторизация
public function login($user, $pass){
$user=$this->modify_login($user);
$pass=$this->hash_pass($pass);
if (empty($user) or empty($pass)) { $this->ban_user_add(); return(false); }
$this->db_connect();
$user_query = mysql_query("SELECT * FROM `users` WHERE `login` = '$user' AND `password` = '$pass'");
if (mysql_num_rows($user_query) == 1) // Авторизировался
{
$dbData=mysql_fetch_array($user_query);
mysql_query ("DELETE FROM `ban` WHERE `ip` = '{$_SERVER['REMOTE_ADDR']}'");
return ($dbData['id']);
}
else
{
$this->ban_user_add();
}
return(false);
}

}

?>


База данных:

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

CREATE TABLE IF NOT EXISTS `ban` (
`ip` text NOT NULL,
`time` text NOT NULL,
`number` int(4) NOT NULL,
`comment` text NOT NULL,
PRIMARY KEY (`number`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;


CREATE TABLE IF NOT EXISTS `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`login` text NOT NULL,
`password` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ;

INSERT INTO `users` (`id`, `login`, `password`) VALUES
(3, 'admin', '64c35fa708e48d2573624e0799622a81');

Примечание автора.
Логин: admin
Пароль: admin

Разбираем подробно код авторизации

У меня большой класс user. Он отвечает за авторизацию. В нем много функций, каждая выполняет определенную задачу. О каждой поговорим отдельно.

Функция login
Функция login – это основная функция авторизации. Она задействует все остальные функции и решает - удачная ли попытка входа.
  • Функция получает две переменные user и pass. В них хранится логин и пароль.
  • Логин мы проверяем на корректность ввода функцией modify_login.
  • Пароль мы хэшируем функцией hash_pass.
  • Если у логина или пароля пустое значение (напр. функция modify_login аннулировала значение) – мы записываем неудачную попытку в базу функцией ban_add_user. И возвращаем результат функции login: false.
  • Соединяемся с базой данных с помощью функции db_connect
  • Ищем сочетание полученного логина и пароля, в случае удачи очищаем список банов с данным ip и возвращаем login: id пользователя.
  • В противном случае записываем неудачную попытку в базу функцией ban_add_user. И возвращаем результат функции login: false.

Функция db_connect
Данная функция делает подключение к базе данных. Подробнее:
  • Перед функцией мы создали приватную переменную $db и присвоили пустое значение.
  • Мы проверяем переменную на отсутствие значения (false or null).
  • Если значение отсутствует, мы соединяемся в базой данных и присваиваем переменной $db значение true.
При повторном вызове функции действий происходить не будет.

Функция hash_pass
Хэш пароля делается для того, что если взломают базу данных, то пароль они все равно не узнают.

Цитата:
Примечание автора. Хэш – шифрование данных, без возможности расшифровки.
Но в нашем мире к каждому действию есть противодействие, например радужные таблицы.
Цитата:
Примечание. Радужные таблицы (англ. rainbow table) используются для вскрытия паролей, преобразованных при помощи сложнообратимой хеш-функции, а также для атак на симметричные шифры на основе известного открытого текста.
Наша задача – сделать хэш более интересным.

Способы усиления хэша:
  1. Хэш в хэше.
  2. Соль – добавить к хэшу пару произвольных символов.
  3. Зависимы хэш – зависит от уникальной переменной (например логина).
  4. Фарш – перемешать значение хэша.
  5. Интеллектуальный хэш – хэш меняет алгоритм, в зависимости от длины и значений.
К сожалению коллизий не избежать, и можно встретить у двух разных значений один хэш, но вероятность этого очень мала.

Подробнее про это я напишу в другой статье. (P.s. не напишу)

В теории функция работает со строками, достаточно приложить немного воображения, чтобы придумать алгоритм шифрования.
Пример кода, не используя класс unuser
// получаем пароль
$pass=$_POST['pass'];
// хэшируем пароль
$pass=md5(sha1($pass).md5(sha1($pass).$pass{1})."s ");
$pass=md5($pass{1}.$pass.$pass{2})


Функция modify_login
Функция проверяет логин на корректность.
  • Функция получает строковую переменную.
  • Удаляет пробелы в начале и концу строки.
  • Проверяет на символы (разрешены символы латинского алфавита и цифры, между ними может стоять дефис, точка или нижнее подчеркивание)
  • Проверяет строку на длину (от 3 до 40 символов включительно)
  • Если все проверки прошли, возвращаем modify_login: строка
  • Если хоть одна проверка не прошла, возвращаем modify_login: false
Пример кода, не используя класс unuser
login=null;
if (!empty($_POST['login'])){
// проверка на допустимую длину
if (strlen($_POST['login'])>=3 and strlen($_POST['login'])<=40)
{
// проверка на допустимые символы
if (preg_match("%^([_\.\-]?[a-zA-Z0-9])+$%", $_POST['login'])) {
$login=$_POST['login'];
}
}
}
// получаем актуальную переменную $login


Функция ban_user_add
Функция для добавления штрафов.
  • Соединяемся с базой данных
  • Задаем время штрафа
  • Ищем в базе пользователя с данным IP
  • Если мы нашли запись, но время штрафа истекло, создаем как первую неудачную попытку
  • Если мы нашли запись, и время штрафа еще не вышло, увеличиваем номер штрафа.
  • Если мы не нашли запись, создаем как первую неудачную попытку.

Функция ban_user
Функция для ограничения доступа.
  • Соединяемся с базой данных
  • Ищем в базе пользователя с данным IP и пятью актуальными штрафами.
  • Если не нашли, возвращаем функции ban_user: false (значит пользователь не заблокирован)
  • Если нашли, возвращаем функции ban_user: время окончания блокировки.

Как пользоваться скриптом?

Как пользоваться скриптом?

Очень просто, достаточно подключить наш класс
 require_once $_SERVER['DOCUMENT_ROOT'].'/core/obj_user.php';
if (empty($user)) $user=new unuser;

Проверить есть ли пользователь в бане
if ($user->ban_user())
{
// есть в бане
}

И попытаться авторизироваться
if ($user->login($_POST['login'],$_POST['pass']))
{
// авторизировались
}


Примеры авторизации на PHP

Скачать пример авторизации можно здесь: post-6.rar
2012 год andreisoroka.com


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
"Спасибо" от:
cthulchu (28.05.2016)
Старый 09.02.2016, 13:44   #2
 
Аватар для grisha2217
 
Сообщений: 1
FR (активность): 5

Доп. информация
По умолчанию

Цитата:
Сообщение от -Unick Посмотреть сообщение
Присваивать каждому пользователю его уникальный идентификатор.
Эмм, где это написано в коде?
Где проверка на длину пароля? Если будет очень много символов в пароле, будут проблемы со скоростью запроса к БД, тем самым увеличивая расход используемой памяти.
Почему используете mysql, а не pdo?
grisha2217 вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 12.02.2016, 19:00   #3
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы

Цитата:
Сообщение от grisha2217 Посмотреть сообщение
Эмм, где это написано в коде?
при создании таблицы в бд

Цитата:
Сообщение от grisha2217 Посмотреть сообщение
Где проверка на длину пароля? Если будет очень много символов в пароле, будут проблемы со скоростью запроса к БД, тем самым увеличивая расход используемой памяти.
не важно сколько символов пароль, после md5 всегда будет одинаковое кол-во символов на выходе (md5 уже устарел, лучше использовать какой-ниб sha-2)

Цитата:
Сообщение от grisha2217 Посмотреть сообщение
Почему используете mysql, а не pdo?
на самом деле технологий так много, что подобных вопросов бесконечное множество.
давайте я на него не буду отвечать?)


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 04.03.2016, 16:42   #4
 
Аватар для Megamen077
 
Сообщений: 3
FR (активность): 15

Доп. информация
По умолчанию

Добрый день. Подскажите пожалуйста. Столкнулся с такой проблемой - пытаюсь реализовать смену пароля. сделал страницу с формой и такой обработчик -

$_POST['password'];
$password=sha1($password);
$password=md5(sha1($password).md5(sha1($password). $password{1})."s ");
$password=md5($password{1}.$password.$password{2}) ;
return($password);

$query ="UPDATE users SET password='$password'";
$result = mysql_query($query) or die("Ошибка");

if($result)
echo "<span style='color:blue;'>Данные обновлены </span>";

ввожу в форму "admin", он сохраняет, но это значение не совпадает с изначально указанным в скрипте. я что-то не так делаю с кодировкой (с шифрованием). если не сложно подскажите пожалуйста, я в этом не очень разбираюсь, всю голову сломал, разные варианты пробовал.
Megamen077 вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 04.03.2016, 17:06   #5
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы

@Megamen077, а вы алгоритм хеша меняли при авторизации?


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 04.03.2016, 17:50   #6
 
Аватар для Megamen077
 
Сообщений: 3
FR (активность): 15

Доп. информация
По умолчанию

нет, не менял. а тот алгоритм который я использовал (взял из obj_user.php ) если ввести в форме смены пароля admin, то вместо "64c35fa708e48d2573624e0799622a81" записывает в базу данных другое значение - вот такое "fa34a0b03fc9ca2cadc939b0ea7061b4"
Megamen077 вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 04.03.2016, 18:04   #7
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы

@Megamen077, при авторизации и смены пароля должен использоваться одинаковый алгоритм кеширования
можете сделать функцию
protected function hash_pass($text){

публичной
public function hash_pass($text)


И при смене пароля использовать
$password=$user->hash_pass($password)


Ну или вообще самому расширять obj_user.php, добавить в него метод изменения пароля
Megamen077: сообщение полезно


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 04.03.2016, 18:08   #8
 
Аватар для Megamen077
 
Сообщений: 3
FR (активность): 15

Доп. информация
По умолчанию

Большое спасибо за ответ, сегодня попробую ваш вариант
Megamen077 вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 26.05.2016, 12:17   #9
 
Аватар для turgantay
 
Сообщений: 1
FR (активность): 5

Доп. информация
По умолчанию

как можна получить логин, чтобы показать вверхном углу
turgantay вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 26.05.2016, 16:27   #10
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы

@turgantay, после авторизации сохранить его в сессию и выводить


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 28.05.2016, 22:26   #11
 
Аватар для Cheremicin
 
Сообщений: 5
FR (активность): 25

Доп. информация
По умолчанию

@Unick, Здравствуйте.
Столкнулся с такой проблемой:
После авторизации при переходе на другую страницу, которая содержит подключение класса и сессии, происходит потеря или разрыв сессии(не знаю, что именно и как).
Обнаружил это, после того, как зашел на свой сайт с другой машины и не смог нормально пользоваться. Авторизуюсь, а дальше переход на любую другую страницу теряет сессию. При чем на моей машине все работает. Проверил с разных браузеров и на разных компьютерах. Где-то работает, где-то нет..
В чем может быть проблема?
Cheremicin вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 01.06.2016, 01:04   #12
 
Аватар для Unick
 
Сообщений: 698
FR (активность): 31,962

Доп. информация
По умолчанию Автор темы

@Cheremicin, сессия - это своего рода хранилище на сервере. Доступ к которому идет по ключу, который хранится в куках
1. Проверьте, записываются куки?
если да
2. Выведите содержание кук на каждой странице (ну и проверьте что везде их подключаете)
3. Проверьте алгоритм, который проверяет что пользователь авторизирован

На этом мои телепатические способности заканчиваются. Не разберетесь, показывайте исходники...
Cheremicin: сообщение полезно


andreisoroka.com
Unick вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
"Спасибо" от:
cthulchu (01.06.2016)
Старый 01.06.2016, 08:30   #13
 
Аватар для Hodge
 
Сообщений: 685
FR (активность): 14,957

Доп. информация
По умолчанию

Цитата:
Сообщение от Cheremicin Посмотреть сообщение
Столкнулся с такой проблемой:
После авторизации при переходе на другую страницу, которая содержит подключение класса и сессии, происходит потеря или разрыв сессии(не знаю, что именно и как).
Не пишите куку.

Цитата:
Сообщение от Cheremicin Посмотреть сообщение
При чем на моей машине все работает.
Когда пробовали на своей машине, куку писали.
Она, собственно, только в вашем браузере и живет, в единственном экземпляре.

Цитата:
Сообщение от -Unick Посмотреть сообщение
Не разберетесь, показывайте исходники...
Просто покажите код или те части, что отвечают за авторизацию. Если весь код показывать не будете, убедитесь, что пишите или удаляете куку до момента формирования заголовка http (простым языком, до момента вывода любого первого символа на страницу).
Hodge вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 01.06.2016, 22:54   #14
 
Аватар для Cheremicin
 
Сообщений: 5
FR (активность): 25

Доп. информация
По умолчанию

Цитата:
Сообщение от Unick Посмотреть сообщение
@Cheremicin, сессия - это своего рода хранилище на сервере. Доступ к которому идет по ключу, который хранится в куках
1. Проверьте, записываются куки?
если да
2. Выведите содержание кук на каждой странице (ну и проверьте что везде их подключаете)
3. Проверьте алгоритм, который проверяет что пользователь авторизирован

На этом мои телепатические способности заканчиваются. Не разберетесь, показывайте исходники...
Хм. Я вот задумался.
А ведь в тексте кода приведенного вами, нет упоминания о куках...
Я о них и не подумал.
Или я не прочитал 3-ю часть статьи? ))

Сообщение добавлено 02.06.2016 в 00:01

Вот так у меня выглядит начало каждого php файла:

session_start();
header('Content-Type: text/html;charset=UTF-8');
$loginurl = '../login.php';

if ($_SESSION['access']!='allowed' or $_SESSION['user']['ip']!=$_SERVER['REMOTE_ADDR']){
header("Location: $loginurl");
exit();
};

и дальше код страницы.
Cheremicin вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Старый 02.06.2016, 00:02   #15
 
Аватар для cthulchu
 
Сообщений: 3,659
FR (активность): 106,746

Доп. информация
По умолчанию

а почему у вас много php-файлов с одинаковым началом? Уник, у тебя, что, не REST API тут? Это, пхпшные сессии всегда с куками связаны. Больше нету вариантов держать их. Не, ну можно извращаться хидден филдами и можно попробовать юзать веб-сторедж, но это бред.

И я настаиваю на использовании REST всюду.
cthulchu вне форума  
Ответить с цитированием Сказать Плохо за это бесполезное сообщение Быстрый ответ на это сообщение
Ответ

Быстрый ответ
Ваше имя пользователя: Регистрация. Для входа нажмите здесь
Случайный вопрос

Сообщение:
Опции


Опции темы
Опции просмотра Оценка этой теме
Оценка этой теме:

Ваши права в разделе
Вы не можете создавать новые темы
Вы можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Взлом VK. Безопасная авторизация (часть 1 из 3) Unick Программирование 13 23.10.2017 21:22
Безопасная реконструкция сайта express-rus Оптимизация страниц сайта 5 15.01.2016 09:24
[ Вопрос ] Часть страниц из раздела в индексе, а часть - нет. Akustika Индексация сайта 0 30.06.2014 14:20
Авторизация в гугл Aqula Программирование 1 14.05.2012 15:14
универсальный скрипт ввода любых капч - Allsubmitter и многих других программ ancorid Юмор 13 02.05.2011 19:14

Текущее время: 02:46. Часовой пояс GMT +3.