2025-04-13 22:35:48
#ok #simple-chat
% cat index.php
<?php
// Начинаем сессию с усиленными настройками безопасности
session_start([
'cookie_httponly' => true, // Защита от JavaScript-доступа к куки
'cookie_secure' => isset($_SERVER['HTTPS']), // Куки передаются только по HTTPS
'use_strict_mode' => true // Защита от подмены ID сессии
]);
// Генерация нового CSRF-токена, если его еще нет
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Файл для хранения сообщений
$messageFile = 'messages.txt';
// Если имя пользователя не установлено в сессии, используем "Guest"
if (!isset($_SESSION['username'])) {
$_SESSION['username'] = 'Guest';
}
// Получаем IP-адрес пользователя
$userIp = $_SERVER['REMOTE_ADDR'];
// Ограничение частоты отправки сообщений (минимальный интервал - 5 секунд)
if (isset($_SESSION['last_message_time'])) {
$time_diff = time() - $_SESSION['last_message_time'];
if ($time_diff < 1) { // Минимальный интервал между сообщениями (5 секунд)
die('Please wait before sending another message.');
}
}
// Проверяем, отправлена ли форма с новым сообщением
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Проверка CSRF-токена
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('Invalid CSRF token');
}
// Обновляем имя пользователя, если оно передано
if (!empty($_POST['username'])) {
$_SESSION['username'] = htmlspecialchars($_POST['username']);
}
// Если сообщение не пустое, добавляем его в файл
if (!empty($_POST['message'])) {
$newMessage = htmlspecialchars($_POST['message']); // Защита от XSS
$username = $_SESSION['username']; // Имя пользователя из сессии
$timestamp = date('Y-m-d H:i:s'); // Полная дата и время
// Формируем строку сообщения в формате "Username@IP: message"
$displayName = $username . '@' . $userIp;
$messageLine = "[$timestamp]\n $displayName: $newMessage" . PHP_EOL;
// Добавляем сообщение в файл
file_put_contents($messageFile, $messageLine, FILE_APPEND | LOCK_EX);
// Сохраняем время последнего сообщения
$_SESSION['last_message_time'] = time();
}
}
// Читаем все сообщения из файла
$messages = file_exists($messageFile) ? file($messageFile, FILE_IGNORE_NEW_LINES) : [];
// Ограничиваем количество сообщений до 10 последних
$messages = array_slice($messages, -15);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Chat</title>
<!-- Link to styles -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Simple Chat</h1>
<!-- Form for sending a new message -->
<form method="POST" action="" style="margin-bottom: 20px;">
<!-- CSRF-токен -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<!-- label for="username">Name:</label -->
<input type="text" id="username" name="username" value="<?php echo htmlspecialchars($_SESSION['username']); ?>" placeholder="Enter your name" required>
<!-- label for="message">Message:</label -->
<input type="text" id="message" name="message" placeholder="Enter your message" autofocus >
<button type="submit">Send / Refresh</button>
</form>
<!-- Display messages -->
<?php if (!empty($messages)): ?>
<pre><?php echo htmlspecialchars(implode(PHP_EOL, $messages)); ?></pre>
<?php else: ?>
<p>No messages yet. Be the first to write one!</p>
<?php endif; ?>
<a href="http://ur4uqu.com/chat/messages.txt" target="_blank">message-log</a>
</body>
</html>
####################
% cat styles.css
/* Общие стили */
body {
font-family: monospace;
line-height: 1.6;
margin: 0 auto;
max-width: 90%;
padding: 15px;
background-color: #2c2c2c;
color: #f0f0f0;
}
h1 {
color: #ffffff;
text-align: center;
font-weight: 700;
}
/* Стиль для блока с сообщениями */
.chat-messages {
font-family: monospace;
white-space: pre-wrap; /* Перенос строк с сохранением пробелов */
word-wrap: break-word; /* Разрыв длинных слов */
background-color: #3a3a3a;
color: #f0f0f0;
padding: 10px;
border-radius: 5px;
overflow-y: hidden; /* Убираем скроллинг */
line-height: 1.4; /* Убираем через-строчность */
}
code, pre {
font-family: monospace;
font-size: 15px;
line-height: 1.5;
letter-spacing: 0.01em;
color: #c9c9c9; /* Более светлый цвет текста */
background-color: #333333; /* Темный фон */
padding: 8px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); /* Небольшая тень */
text-align: left;
white-space: pre-wrap;
word-break: keep-all;
}
/* Стиль для полей ввода */
input[type="text"] {
font-family: monospace;
font-size: 18px;
line-height: 1.6;
color: #f0f0f0; /* Цвет текста, как и у body */
background-color: #333333; /* Темный фон */
border: 1px solid #555; /* Более темная граница */
border-radius: 8px;
padding: 10px;
margin-bottom: 15px;
width: 100%; /* Занимает всю ширину контейнера */
box-sizing: border-box; /* Учитывает padding и border в ширине */
transition: border-color 0.3s; /* Плавное изменение цвета границы */
}
input[type="text"]:focus {
outline: none; /* Убираем стандартное выделение при фокусе */
border-color: #1e90ff; /* Подсвечиваем границу при фокусе */
box-shadow: 0 0 5px rgba(30, 144, 255, 0.5); /* Добавляем небольшую тень */
}
input[type="text"]::placeholder {
color: #b0b0b0; /* Более светлый цвет для placeholder */
}
/* Стиль для кнопки */
button {
background: linear-gradient(90deg, #4e73df 0%, #1c7ed6 100%);
color: white;
border: none;
border-radius: 5px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: linear-gradient(90deg, #1c7ed6 0%, #4e73df 100%);
}
/* Стиль для ссылок */
a {
color: #ffa07a; /* Оранжевый цвет */
text-decoration: none; /* Удаление подчеркивания */
}
a:hover {
color: #ff9900; /* Темно-оранжевый цвет при наведении */
}
Back to list