yet another FVKING security update

This commit is contained in:
Lain Iwakura 2025-06-16 01:16:48 +03:00
parent b160348533
commit 44ad71a116
No known key found for this signature in database
GPG Key ID: C7C18257F2ADC6F8
3 changed files with 50 additions and 18 deletions

View File

@ -4,7 +4,8 @@ CREATE TABLE messages (
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
signature TEXT,
is_encrypted BOOLEAN DEFAULT FALSE
is_encrypted BOOLEAN DEFAULT FALSE,
INDEX idx_created_at (created_at)
);
CREATE TABLE users (
@ -13,11 +14,15 @@ CREATE TABLE users (
password VARCHAR(255) NOT NULL,
pgp_key TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_moderator TINYINT(1) NOT NULL DEFAULT 0
is_moderator TINYINT(1) NOT NULL DEFAULT 0,
login_attempts INT NOT NULL DEFAULT 0,
last_attempt TIMESTAMP NULL,
INDEX idx_username (username)
);
CREATE TABLE registrations (
id INT AUTO_INCREMENT PRIMARY KEY,
ip VARCHAR(45) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_ip_created (ip, created_at)
);

View File

@ -1,9 +1,12 @@
<?php
session_start();
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'');
header('Content-Security-Policy: default-src \'self\'; style-src \'self\' \'unsafe-inline\';');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
$config = require 'config.php';
$db = new PDO(
@ -16,17 +19,32 @@ $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$password = $_POST['password'] ?? '';
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);
if ($username && $password) {
$stmt = $db->prepare('SELECT id, password, is_moderator FROM users WHERE username = ?');
$stmt = $db->prepare('SELECT id, password, is_moderator, login_attempts, last_attempt FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $username;
$_SESSION['is_moderator'] = $user['is_moderator'];
header('Location: index.php');
exit;
if ($user) {
if ($user['login_attempts'] >= 5 && strtotime($user['last_attempt']) > strtotime('-15 minutes')) {
$error = 'Too many login attempts. Please try again later.';
} else if (password_verify($password, $user['password'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $username;
$_SESSION['is_moderator'] = $user['is_moderator'];
$stmt = $db->prepare('UPDATE users SET login_attempts = 0, last_attempt = NOW() WHERE id = ?');
$stmt->execute([$user['id']]);
header('Location: index.php');
exit;
} else {
$stmt = $db->prepare('UPDATE users SET login_attempts = login_attempts + 1, last_attempt = NOW() WHERE id = ?');
$stmt->execute([$user['id']]);
$error = 'Invalid username or password';
}
} else {
$error = 'Invalid username or password';
}

View File

@ -1,9 +1,12 @@
<?php
session_start();
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'');
header('Content-Security-Policy: default-src \'self\'; style-src \'self\' \'unsafe-inline\';');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
$config = require 'config.php';
$db = new PDO(
@ -18,13 +21,15 @@ $success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$password = $_POST['password'] ?? '';
$pgp_key = $_POST['pgp_key'] ?? '';
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);
$pgp_key = filter_input(INPUT_POST, 'pgp_key', FILTER_SANITIZE_STRING);
$ip = $_SERVER['REMOTE_ADDR'];
if ($username && $password) {
if (strlen($username) > 50 || strlen($password) < 8) {
if (strlen($username) > 50 || strlen($password) < 8 || !preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
$error = 'Invalid data';
} else if (strlen($pgp_key) > 4096) {
$error = 'PGP key is too long';
} else {
$stmt = $db->prepare('SELECT COUNT(*) FROM registrations WHERE ip = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)');
$stmt->execute([$ip]);
@ -34,8 +39,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$error = 'Registration limit exceeded for your IP';
} else {
try {
$stmt = $db->prepare('INSERT INTO users (username, password, pgp_key) VALUES (?, ?, ?)');
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $pgp_key]);
$stmt = $db->prepare('INSERT INTO users (username, password, pgp_key, login_attempts, last_attempt) VALUES (?, ?, ?, 0, NOW())');
$stmt->execute([
$username,
password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]),
$pgp_key
]);
$stmt = $db->prepare('INSERT INTO registrations (ip) VALUES (?)');
$stmt->execute([$ip]);