mirror of
https://git.sr.ht/~iwakuralain/text0Nly
synced 2025-07-27 15:36:11 +00:00
yet another FVKING security update
This commit is contained in:
parent
b160348533
commit
44ad71a116
11
main/db.sql
11
main/db.sql
@ -4,7 +4,8 @@ CREATE TABLE messages (
|
|||||||
message TEXT NOT NULL,
|
message TEXT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
signature TEXT,
|
signature TEXT,
|
||||||
is_encrypted BOOLEAN DEFAULT FALSE
|
is_encrypted BOOLEAN DEFAULT FALSE,
|
||||||
|
INDEX idx_created_at (created_at)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
@ -13,11 +14,15 @@ CREATE TABLE users (
|
|||||||
password VARCHAR(255) NOT NULL,
|
password VARCHAR(255) NOT NULL,
|
||||||
pgp_key TEXT,
|
pgp_key TEXT,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
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 (
|
CREATE TABLE registrations (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
ip VARCHAR(45) NOT NULL,
|
ip VARCHAR(45) NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_ip_created (ip, created_at)
|
||||||
);
|
);
|
@ -1,9 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
|
ini_set('session.cookie_httponly', 1);
|
||||||
|
ini_set('session.cookie_secure', 1);
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
header('X-Frame-Options: DENY');
|
header('X-Frame-Options: DENY');
|
||||||
header('X-XSS-Protection: 1; mode=block');
|
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';
|
$config = require 'config.php';
|
||||||
$db = new PDO(
|
$db = new PDO(
|
||||||
@ -16,17 +19,32 @@ $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|||||||
$error = '';
|
$error = '';
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
|
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
|
||||||
$password = $_POST['password'] ?? '';
|
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
if ($username && $password) {
|
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]);
|
$stmt->execute([$username]);
|
||||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if ($user && password_verify($password, $user['password'])) {
|
|
||||||
$_SESSION['user_id'] = $user['id'];
|
if ($user) {
|
||||||
$_SESSION['username'] = $username;
|
if ($user['login_attempts'] >= 5 && strtotime($user['last_attempt']) > strtotime('-15 minutes')) {
|
||||||
$_SESSION['is_moderator'] = $user['is_moderator'];
|
$error = 'Too many login attempts. Please try again later.';
|
||||||
header('Location: index.php');
|
} else if (password_verify($password, $user['password'])) {
|
||||||
exit;
|
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 {
|
} else {
|
||||||
$error = 'Invalid username or password';
|
$error = 'Invalid username or password';
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
|
ini_set('session.cookie_httponly', 1);
|
||||||
|
ini_set('session.cookie_secure', 1);
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
header('X-Frame-Options: DENY');
|
header('X-Frame-Options: DENY');
|
||||||
header('X-XSS-Protection: 1; mode=block');
|
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';
|
$config = require 'config.php';
|
||||||
$db = new PDO(
|
$db = new PDO(
|
||||||
@ -18,13 +21,15 @@ $success = '';
|
|||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
|
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
|
||||||
$password = $_POST['password'] ?? '';
|
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);
|
||||||
$pgp_key = $_POST['pgp_key'] ?? '';
|
$pgp_key = filter_input(INPUT_POST, 'pgp_key', FILTER_SANITIZE_STRING);
|
||||||
$ip = $_SERVER['REMOTE_ADDR'];
|
$ip = $_SERVER['REMOTE_ADDR'];
|
||||||
|
|
||||||
if ($username && $password) {
|
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';
|
$error = 'Invalid data';
|
||||||
|
} else if (strlen($pgp_key) > 4096) {
|
||||||
|
$error = 'PGP key is too long';
|
||||||
} else {
|
} else {
|
||||||
$stmt = $db->prepare('SELECT COUNT(*) FROM registrations WHERE ip = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)');
|
$stmt = $db->prepare('SELECT COUNT(*) FROM registrations WHERE ip = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)');
|
||||||
$stmt->execute([$ip]);
|
$stmt->execute([$ip]);
|
||||||
@ -34,8 +39,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$error = 'Registration limit exceeded for your IP';
|
$error = 'Registration limit exceeded for your IP';
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$stmt = $db->prepare('INSERT INTO users (username, password, pgp_key) VALUES (?, ?, ?)');
|
$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), $pgp_key]);
|
$stmt->execute([
|
||||||
|
$username,
|
||||||
|
password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]),
|
||||||
|
$pgp_key
|
||||||
|
]);
|
||||||
|
|
||||||
$stmt = $db->prepare('INSERT INTO registrations (ip) VALUES (?)');
|
$stmt = $db->prepare('INSERT INTO registrations (ip) VALUES (?)');
|
||||||
$stmt->execute([$ip]);
|
$stmt->execute([$ip]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user