Технологии безопасности

Сайт посвященный вопросам безопасности

Создание личной записной книжки на php-mysql с шифрование

Существует много положительных факторов ведения личного дневника, но не всегда там написано то, что нужно читать всем желающим, поэтому я решил сделать велосипед личный дневник (записную книжку) на php и mysql с использованием шифрования данных. Публикация рассчитана для начинающих php-программистов, так как опытные люди это всё знают и сделают всё это ещё лучше меня.

Основное преимущество этой записной книжки — это хранение данных в зашифрованном виде, а ключ знаете только вы. Поэтому, если злоумышленник украдёт вашу базу данных, это максимум что он увидит:



Личный дневник использует два внешних php класса:

XTEA — для шифрования
SafeMySQL — для работы с базой данных

Имеет очень простую структуру базы данных:

CREATE TABLE `notest` (
`id` int(255) NOT NULL,
`text` text NOT NULL,
`date_d` text NOT NULL,
`date_m` text NOT NULL,
`date_t` text NOT NULL,
`date_cat` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Ключ, который использует система хранится в переменной $_SESSION[‘key’]. Проверяем её состояние, спрашиваем у пользователя ключ, если этой переменной нет:

// Если не существует переменная ключа
if (!isset($_SESSION[‘key’])) {
$keyw = ‘<form action="" method="post"><input type="text" name="key" placeholder="Введите ваш ключ"><input type="submit" value="Сохранить"></form>’;
}
// Иначе, если существует
else {
$keyw = ‘<button><a class="nodec_link" href="index.php?action=delkey">Удалить ключ</a></button>’;
}
// Если отправлена форма сохранения ключа
if (isset($_POST["key"])) {
$_SESSION[‘key’] = $_POST["key"];
header("Location: index.php");
exit();
}

Когда пользователь закончил работу, делаем unset:

if ((isset($_GET[‘action’])) && ($_GET[‘action’] == ‘delkey’)) {
unset($_SESSION[‘key’]);
header("Location: index.php");
exit();
}

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

$db = new SafeMySQL($set_bd);
$option = $db->getAll("SELECT DISTINCT `date_cat` FROM `notes`");
foreach ($option as $value) {
$desc_opt = explode("-", $value[‘date_cat’]);
echo ‘<option value="index.php?date=’.$value[‘date_cat’].’">’.num2name($desc_opt[0]).’ ‘.$desc_opt[1].'</option>’;
}

Функция num2name() переводит номер месяца в его название

function num2name($num) {
switch ($num) {
case ’01’:
$name = "январь";
break;
case ’02’:
$name = "февраль";
break;
case ’03’:
$name = "март";
break;
case ’04’:
$name = "апрель";
break;
case ’05’:
$name = "май";
break;
case ’06’:
$name = "июнь";
break;
case ’07’:
$name = "июль";
break;
case ’08’:
$name = "август";
break;
case ’09’:
$name = "сентябрь";
break;
case ’10’:
$name = "октябрь";
break;
case ’11’:
$name = "ноябрь";
break;
case ’12’:
$name = "декабрь";
break;

default:
$name = "";
break;
}
return $name;
}

В зависимости от наличия запроса на вывод записей определённого месяца, выводим записи дневника:

// Если отправлен запрос на конкретный месяц
if (isset($_GET["date"])) {
$cur_date = $_GET["date"];
$db = new SafeMySQL($set_bd);
$data = $db->getAll("SELECT * FROM `notes` WHERE `date_cat` = ?s ORDER BY `id` DESC", $cur_date);
foreach ($data as $key) {
// Декодируем текст
$xtea = new XTEA($_SESSION[‘key’]);
$text_decode = $xtea->Decrypt($key["text"]);
// Выводим записи
echo ‘<tr>
<td class="date">
<div class="date_m">’.num2name($key["date_m"]).'</div>
<div class="date_d">’.$key["date_d"].'</div>
<div class="date_t">’.$key["date_t"].'</div>
</td>
<td class="note_text" valign="top">’.$text_decode.'</td>
</tr>’;
}
}
// Если нет запроса на определённый месяц, выводим последние $set_col записей
else {
$db = new SafeMySQL($set_bd);
$data = $db->getAll("SELECT * FROM `notes` ORDER BY `id` DESC LIMIT ?i", $set_col);
foreach ($data as $key) {
// Декодируем текст
$xtea = new XTEA($_SESSION[‘key’]);
$text_decode = $xtea->Decrypt($key["text"]);
// Выводим записи
echo ‘<tr>
<td class="date">
<div class="date_m">’.num2name($key["date_m"]).'</div>
<div class="date_d">’.$key["date_d"].'</div>
<div class="date_t">’.$key["date_t"].'</div>
</td>
<td class="note_text" valign="top">’.$text_decode.'</td>
</tr>’;
}
}

Добавление новой записи происходит через простую форму:

<form method="post" action="">
<textarea rows="10" name="text" placeholder="Текст новой записи"></textarea>
<input type="submit" value="Добавить">
</form>

Которая обрабатывается таким кодом:

if (isset($_POST["text"])) {
// Получение данных в переменные
$text = nl2br($_POST["text"]);
// Шифрование текста
$xtea = new XTEA($_SESSION[‘key’]);
$text = $xtea->Encrypt($text);

$date_d = date("d");
$date_m = date("m");
$date_t = date("Y");
$date_cat = date("n-Y");
// Отправка в базу данных
$db = new SafeMySQL($set_bd);
$sql = "INSERT INTO `notes` (`text`, `date_d`, `date_m`, `date_t`, `date_cat`) VALUES (?s, ?s, ?s, ?s, ?s)";
$db->query($sql, $text, $date_d, $date_m, $date_t, $date_cat);
}

Для использования на открытом сервере необходимо закрывать доступ к каталогу со скриптом, так как он написан для домашнего закрытого сервера и у него нет встроенной авторизации.

Полный код основного файла

<?php
// Отображение ошибок на странице
ini_set(‘error_reporting’, E_ALL);
ini_set(‘display_errors’, 1);
ini_set(‘display_startup_errors’, 1);

include "config.php";
include "db.php";
include ‘xtea.php’;

header(‘Content-Type: text/html; charset=utf-8’);
session_start();

// Если не существует переменная ключа
if (!isset($_SESSION[‘key’])) {
$keyw = ‘<form action="" method="post"><input type="text" name="key" placeholder="Введите ваш ключ"><input type="submit" value="Сохранить"></form>’;
}
// Иначе, если существует
else {
$keyw = ‘<button><a class="nodec_link" href="index.php?action=delkey">Удалить ключ</a></button>’;
}
// Если отправлена форма сохранения ключа
if (isset($_POST["key"])) {
$_SESSION[‘key’] = $_POST["key"];
header("Location: index.php");
exit();
}
if ((isset($_GET[‘action’])) && ($_GET[‘action’] == ‘delkey’)) {
unset($_SESSION[‘key’]);
header("Location: index.php");
exit();
}
// если отправлена форма добавления записи
if (isset($_POST["text"])) {
$text = nl2br($_POST["text"]);

$xtea = new XTEA($_SESSION[‘key’]);
$text = $xtea->Encrypt($text);

$date_d = date("d");
$date_m = date("m");
$date_t = date("Y");
$date_cat = date("n-Y");

$db = new SafeMySQL($set_bd);
$sql = "INSERT INTO `notes` (`text`, `date_d`, `date_m`, `date_t`, `date_cat`) VALUES (?s, ?s, ?s, ?s, ?s)";
$db->query($sql, $text, $date_d, $date_m, $date_t, $date_cat);
}

// Функия перевода номера месяца в его название
function num2name($num) {
switch ($num) {
case ’01’:
$name = "январь";
break;
case ’02’:
$name = "февраль";
break;
case ’03’:
$name = "март";
break;
case ’04’:
$name = "апрель";
break;
case ’05’:
$name = "май";
break;
case ’06’:
$name = "июнь";
break;
case ’07’:
$name = "июль";
break;
case ’08’:
$name = "август";
break;
case ’09’:
$name = "сентябрь";
break;
case ’10’:
$name = "октябрь";
break;
case ’11’:
$name = "ноябрь";
break;
case ’12’:
$name = "декабрь";
break;

default:
$name = "";
break;
}
return $name;
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href=’https://fonts.googleapis.com/css?family=Open+Sans&subset=latin,cyrillic’ rel=’stylesheet’ type=’text/css’>
<title><?php echo $set_title; ?></title>
<link href="style.css" rel="stylesheet">
</head>
<body>
<div class="main">
<table>
<tr>
<select onchange="location.href=this.options[this.selectedIndex].value;">
<option value="index.php">Выбор месяца и года</option>
<option value="index.php">Последине <?php echo $set_col; ?> записей</option>
<?php
$db = new SafeMySQL($set_bd);
$option = $db->getAll("SELECT DISTINCT `date_cat` FROM `notes`");
foreach ($option as $value) {
$desc_opt = explode("-", $value[‘date_cat’]);
echo ‘<option value="index.php?date=’.$value[‘date_cat’].’">’.num2name($desc_opt[0]).’ ‘.$desc_opt[1].'</option>’;
}
?>
</select>
</tr>
<tr>
<?php echo $keyw; ?>
</tr>
</table>
<h1><?php echo $set_title; ?></h1>
<div class="addform">
<form method="post" action="">
<textarea rows="10" name="text" placeholder="Текст новой записи"></textarea>
<input type="submit" value="Добавить">
</form>
</div>
<div class="notes">
<table class="table">
<tbody>
<?php
// Если отправлен запрос на конкретный месяц
if (isset($_GET["date"])) {
$cur_date = $_GET["date"];
$db = new SafeMySQL($set_bd);
$data = $db->getAll("SELECT * FROM `notes` WHERE `date_cat` = ?s ORDER BY `id` DESC", $cur_date);
foreach ($data as $key) {
// Декодируем текст
$xtea = new XTEA($_SESSION[‘key’]);
$text_decode = $xtea->Decrypt($key["text"]);
// Выводим записи
echo ‘<tr>
<td class="date">
<div class="date_m">’.num2name($key["date_m"]).'</div>
<div class="date_d">’.$key["date_d"].'</div>
<div class="date_t">’.$key["date_t"].'</div>
</td>
<td class="note_text" valign="top">’.$text_decode.'</td>
</tr>’;
}
}
// Если нет запроса на определённый месяц, выводим последние $set_col записей
else {
$db = new SafeMySQL($set_bd);
$data = $db->getAll("SELECT * FROM `notes` ORDER BY `id` DESC LIMIT ?i", $set_col);
foreach ($data as $key) {
// Декодируем текст
$xtea = new XTEA($_SESSION[‘key’]);
$text_decode = $xtea->Decrypt($key["text"]);
// Выводим записи
echo ‘<tr>
<td class="date">
<div class="date_m">’.num2name($key["date_m"]).'</div>
<div class="date_d">’.$key["date_d"].'</div>
<div class="date_t">’.$key["date_t"].'</div>
</td>
<td class="note_text" valign="top">’.$text_decode.'</td>
</tr>’;
}
}
?>
</tbody>
</table>
</div>

</div>
</body>
</html>

Ссылки:

Исходный код в репозитории
SafeMySQL
Алгоритм XTEA