Complete mock secure web application with:
- User registration and login with CSRF protection - SQL injection prevention and XSS protection - Real-time form validation - Password strength requirements - Show/hide password toggle - Modern dark theme UI - Routes for /login, /register, /home, /logout - API endpoints for CRUD operations - Prettier and ESLint configure
This commit is contained in:
136
config/database.php
Normal file
136
config/database.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
class Database {
|
||||
private $host;
|
||||
private $db;
|
||||
private $user;
|
||||
private $pass;
|
||||
private $port;
|
||||
private $charset;
|
||||
private $pdo;
|
||||
|
||||
public function __construct() {
|
||||
$this->host = getenv('DB_HOST') ?: 'localhost';
|
||||
$this->db = getenv('DB_NAME') ?: 'securecode';
|
||||
$this->user = getenv('DB_USER') ?: 'carlos';
|
||||
$this->pass = getenv('DB_PASSWORD') ?: '';
|
||||
$this->port = getenv('DB_PORT') ?: '5432';
|
||||
$this->charset = 'utf8';
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
if ($this->pdo !== null) {
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
$dsn = "pgsql:host={$this->host};port={$this->port};dbname={$this->db};options='--client_encoding={$this->charset}'";
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
try {
|
||||
$this->pdo = new PDO($dsn, $this->user, $this->pass, $options);
|
||||
} catch (\PDOException $e) {
|
||||
error_log("Database connection error: " . $e->getMessage());
|
||||
throw new \Exception("Database connection failed");
|
||||
}
|
||||
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
public function query($sql, $params = []) {
|
||||
$pdo = $this->getConnection();
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeInput($data) {
|
||||
if (is_array($data)) {
|
||||
return array_map('sanitizeInput', $data);
|
||||
}
|
||||
return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function escapeHtml($data) {
|
||||
if (is_array($data)) {
|
||||
return array_map('escapeHtml', $data);
|
||||
}
|
||||
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function validateEmail($email) {
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
|
||||
}
|
||||
|
||||
function validateString($data, $maxLength = 1000) {
|
||||
if (is_array($data)) {
|
||||
return array_map(fn($item) => validateString($item, $maxLength), $data);
|
||||
}
|
||||
$data = trim($data);
|
||||
if (strlen($data) > $maxLength) {
|
||||
return false;
|
||||
}
|
||||
return preg_match('/^[\p{L}\p{N}\s\-_.,!?@]+$/u', $data) === 1 || empty($data);
|
||||
}
|
||||
|
||||
function sanitizeForDatabase($data) {
|
||||
if (is_array($data)) {
|
||||
return array_map('sanitizeForDatabase', $data);
|
||||
}
|
||||
return preg_replace('/[^\p{L}\p{N}\s\-_.,!?@]/u', '', $data);
|
||||
}
|
||||
|
||||
function validatePassword($password) {
|
||||
$errors = [];
|
||||
|
||||
if (strlen($password) < 8) {
|
||||
$errors[] = 'Password must be at least 8 characters';
|
||||
}
|
||||
|
||||
if (strlen($password) > 128) {
|
||||
$errors[] = 'Password must be less than 128 characters';
|
||||
}
|
||||
|
||||
if (!preg_match('/[a-z]/', $password)) {
|
||||
$errors[] = 'Password must contain at least one lowercase letter';
|
||||
}
|
||||
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
$errors[] = 'Password must contain at least one uppercase letter';
|
||||
}
|
||||
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
$errors[] = 'Password must contain at least one number';
|
||||
}
|
||||
|
||||
if (!preg_match('/[!@#$%^&*(),.?":{}|<>]/', $password)) {
|
||||
$errors[] = 'Password must contain at least one special character';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
function generateToken() {
|
||||
if (empty($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
function verifyToken($token) {
|
||||
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
function hashPassword($password) {
|
||||
return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
|
||||
}
|
||||
|
||||
function verifyPassword($password, $hash) {
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
22
config/schema.sql
Normal file
22
config/schema.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
-- PostgreSQL schema for secure application
|
||||
|
||||
-- Create users table
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
|
||||
-- Create user_data table
|
||||
CREATE TABLE IF NOT EXISTS user_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
data TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_data_user_id ON user_data(user_id);
|
||||
Reference in New Issue
Block a user