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

в 9:07, , рубрики: php, записная книжка, информационная безопасность, шифрование на php

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

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

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

Личный дневник использует два внешних 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>';
                }
              } 

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

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

<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

Автор: pingvi69

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js