Security hardening: CSRF, rate limiting, session/password policy, audit log
Fixes critical pre-existing issues found during review: bulk_action.php had no auth check at all (unauthenticated download/delete of any qrcode) and built a table name from unwhitelisted user input (SQL injection); the QR generator classes wrote files from unvalidated filename/format, allowing path traversal and arbitrary file writes. Also pins chillerlan/php-qrcode to 5.0.5 since master now requires PHP 8.4, breaking the PHP 8.3 build. - CSRF tokens on all POST forms and the bulk_action.php JSON endpoint - Login rate limiting (5 attempts / 15 min) via new login_attempts table - Hardened sessions: httponly/samesite cookies, 30 min idle timeout, session regeneration on login - Forced password change for the default superadmin/superadmin account - Server-side validation in Users/DynamicQrcode/Qrcode classes - Audit log table for auth, user, and qrcode actions - Checked-in db schema (db/init.sql, migrations/) instead of relying on an opaque prebuilt db image - Production docker-compose with Nginx + php-fpm instead of the PHP dev server Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` int(25) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(50) NOT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`series_id` varchar(60) DEFAULT NULL,
|
||||
`remember_token` varchar(255) DEFAULT NULL,
|
||||
`expires` datetime DEFAULT NULL,
|
||||
`type` varchar(10) NOT NULL,
|
||||
`must_change_password` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`password_changed_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
|
||||
|
||||
-- Default super admin account. Credentials: superadmin / superadmin
|
||||
-- must_change_password=1 forces a password change on first login (see Fase 1 hardening).
|
||||
INSERT INTO `users` (`id`, `username`, `password`, `series_id`, `remember_token`, `expires`, `type`, `must_change_password`, `password_changed_at`) VALUES
|
||||
(1, 'superadmin', '$2y$10$xpZc5KC.aU2XHkcqhuZGFuAnqmtL4Unt8MysOyylceq.19XIyoZpG', NULL, NULL, NULL, 'super', 1, NULL);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dynamic_qrcodes` (
|
||||
`id` int(10) NOT NULL AUTO_INCREMENT,
|
||||
`id_owner` int(25) NULL DEFAULT NULL,
|
||||
`filename` varchar(45) NOT NULL,
|
||||
`format` varchar(45) DEFAULT NULL,
|
||||
`identifier` longtext,
|
||||
`link` varchar(500) DEFAULT NULL,
|
||||
`qrcode` varchar(60) DEFAULT NULL,
|
||||
`scan` int(11) NOT NULL DEFAULT '0',
|
||||
`state` varchar(20) NOT NULL DEFAULT 'enable',
|
||||
`created_by` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_by` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `static_qrcodes` (
|
||||
`id` int(10) NOT NULL AUTO_INCREMENT,
|
||||
`id_owner` int(25) NULL DEFAULT NULL,
|
||||
`filename` varchar(45) CHARACTER SET utf8 NOT NULL,
|
||||
`format` varchar(45) DEFAULT NULL,
|
||||
`type` varchar(45) CHARACTER SET utf8 DEFAULT NULL,
|
||||
`content` mediumtext CHARACTER SET utf8,
|
||||
`qrcode` varchar(60) CHARACTER SET utf8 DEFAULT NULL,
|
||||
`state` varchar(20) CHARACTER SET utf8 NOT NULL DEFAULT 'enable',
|
||||
`created_by` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_by` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ;
|
||||
|
||||
-- Security hardening (Fase 1): rate limiting op login pogingen
|
||||
CREATE TABLE IF NOT EXISTS `login_attempts` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(50) NOT NULL,
|
||||
`ip_address` varchar(45) NOT NULL,
|
||||
`success` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`attempted_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `username_attempted_at` (`username`, `attempted_at`),
|
||||
KEY `ip_attempted_at` (`ip_address`, `attempted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Security hardening (Fase 1): audit log van gevoelige acties
|
||||
CREATE TABLE IF NOT EXISTS `audit_log` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(25) DEFAULT NULL,
|
||||
`username` varchar(50) DEFAULT NULL,
|
||||
`action` varchar(50) NOT NULL,
|
||||
`target_type` varchar(30) DEFAULT NULL,
|
||||
`target_id` varchar(50) DEFAULT NULL,
|
||||
`ip_address` varchar(45) DEFAULT NULL,
|
||||
`user_agent` varchar(255) DEFAULT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `created_at` (`created_at`),
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
Reference in New Issue
Block a user