Основное преимущество этой записной книжки — это хранение данных в зашифрованном виде, а ключ знаете только вы. Поэтому, если злоумышленник украдёт вашу базу данных, это максимум что он увидит:
Личный дневник использует два внешних php класса:
Имеет очень простую структуру базы данных:
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>';
}
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
Комментарии (9)
SerafimArts
21.12.2015 12:25+1Очень слабый код, уровня продвинутого начинающего. Почему:
1) Смешение логики и представления
2) Процедурный код (т.е. недоступен автолоад как минимум)
3) Шифрование уже встроено в php (openssl, mcrypt), эти решения быстрее и надёжнее
4) Посмотрите самый последний пример, он даже не отформатирован
5) Тема довольно проста и очевидна, как следствие — в интернете тысяча и один аналогичный пример
6) Многие вещи можно в разы упростить (например использовать готовый объект даты времени http://php.net/manual/ru/class.datetime.php вместо date)
Отсюда и минусы. Рекомендую скрыть статью и доработать, учитывая приведённую ссылку комментарием выше, как минимум.
antness
21.12.2015 12:41+1Добавлю:
- В view не предусмотрено экранирование, как следствие, имеем XSS
- Само шифрование/дешифрование в данной задаче логичнее реализовать на стороне клиента, чтобы избежать перехвата как ключа шифрования, так и самого защищаемого текста
ZapevalovAnton
21.12.2015 12:54Мы все конечно понимаем, что это статья в целях обучения и т.д. и т.п. Но конфигурационные файлы не нужно хранить в системе контроля версий. Тем более что там сейчас лежат реальные данные. Для этого обычно создают файлы вида config.php.example, и там описывают каркас конфига без реальных данных.
d7s2di
21.12.2015 13:42+1Вместо этих костылей, проще и логичнее использовать шифрованный gpg текстовичок.
Delphinum
21.12.2015 14:43+1Вот я и нашел куда внести свою лепту:
note.sh#!/bin/bash vim -x "`date +%d-%m-%y_$1.txt`"
d7s2di
21.12.2015 15:22У меня для этих целей набор инструкций в ~/.vim/ftdetect/gpg.vim:
gpg.vimaugroup encrypted au! autocmd BufReadPre,FileReadPre *.gpg set viminfo= autocmd BufReadPre,FileReadPre *.gpg set noswapfile autocmd BufReadPre,FileReadPre *.gpg set bin autocmd BufReadPre,FileReadPre *.gpg let ch_save = &ch|set ch=2 autocmd BufReadPre,FileReadPre *.gpg let shsave=&sh autocmd BufReadPre,FileReadPre *.gpg let &sh='sh' autocmd BufReadPre,FileReadPre *.gpg let ch_save = &ch|set ch=2 autocmd BufReadPost,FileReadPost *.gpg '[,']!gpg --decrypt --default-recipient-self 2> /dev/null autocmd BufReadPost,FileReadPost *.gpg let &sh=shsave autocmd BufReadPost,FileReadPost *.gpg set nobin autocmd BufReadPost,FileReadPost *.gpg let &ch = ch_save|unlet ch_save autocmd BufReadPost,FileReadPost *.gpg execute ":doautocmd BufReadPost " . expand("%:r") autocmd BufWritePre,FileWritePre *.gpg set bin autocmd BufWritePre,FileWritePre *.gpg let shsave=&sh autocmd BufWritePre,FileWritePre *.gpg let &sh='sh' autocmd BufWritePre,FileWritePre *.gpg '[,']!gpg --encrypt --default-recipient-self 2>/dev/null autocmd BufWritePre,FileWritePre *.gpg let &sh=shsave autocmd BufWritePost,FileWritePost *.gpg silent u autocmd BufWritePost,FileWritePost *.gpg set nobin augroup END
andrewnester
дорогой автор, Вам будет полезно ознакомиться www.phptherightway.com