mirror of
https://git.hexahost.dev/smueller/HexaHost-Frontend.git
synced 2026-06-02 12:18:43 +00:00
Enhance security and configuration of contact form: Added Content Security Policy and Strict-Transport-Security headers in .htaccess for improved security. Updated error handling to use a single 404.php for various error codes. Removed deprecated config.php and composer.json files, and implemented IP address detection for better security. Added honeypot field for bot protection in contact form and improved session security settings in functions.php.
This commit is contained in:
@@ -8,6 +8,12 @@
|
|||||||
Header always set X-XSS-Protection "1; mode=block"
|
Header always set X-XSS-Protection "1; mode=block"
|
||||||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
|
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
|
||||||
|
|
||||||
|
# Content Security Policy - Schutz vor XSS und Code-Injection
|
||||||
|
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://cdn.hexahost.de data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
|
||||||
|
|
||||||
|
# Strict-Transport-Security (HSTS) - Erzwingt HTTPS
|
||||||
|
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# HTTPS erzwingen (falls SSL verfügbar)
|
# HTTPS erzwingen (falls SSL verfügbar)
|
||||||
@@ -48,6 +54,16 @@
|
|||||||
Deny from all
|
Deny from all
|
||||||
</Files>
|
</Files>
|
||||||
|
|
||||||
|
# Config-Verzeichnis schützen
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteRule ^config/ - [F,L]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Includes-Verzeichnis schützen (direkter Zugriff verhindern)
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteRule ^includes/ - [F,L]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
# Logs-Verzeichnis schützen
|
# Logs-Verzeichnis schützen
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteRule ^logs/ - [F,L]
|
RewriteRule ^logs/ - [F,L]
|
||||||
@@ -95,14 +111,25 @@
|
|||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# Fehlerbehandlung
|
# Fehlerbehandlung
|
||||||
ErrorDocument 404 /404.html
|
ErrorDocument 400 /404.php
|
||||||
ErrorDocument 500 /500.html
|
ErrorDocument 401 /404.php
|
||||||
|
ErrorDocument 403 /404.php
|
||||||
|
ErrorDocument 404 /404.php
|
||||||
|
ErrorDocument 500 /500.php
|
||||||
|
ErrorDocument 502 /500.php
|
||||||
|
ErrorDocument 503 /500.php
|
||||||
|
|
||||||
# Verzeichnis-Listing deaktivieren
|
# Verzeichnis-Listing deaktivieren
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
|
|
||||||
# Datei-Zugriff beschränken
|
# Datei-Zugriff beschränken
|
||||||
<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|inc|bak)$">
|
<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|inc|bak|sql|env|yml|yaml|json|xml|md)$">
|
||||||
Order Allow,Deny
|
Order Allow,Deny
|
||||||
Deny from all
|
Deny from all
|
||||||
</FilesMatch>
|
</FilesMatch>
|
||||||
|
|
||||||
|
# Spezifische Ausnahmen für benötigte XML-Dateien
|
||||||
|
<FilesMatch "^(sitemap\.xml|robots\.txt)$">
|
||||||
|
Order Allow,Deny
|
||||||
|
Allow from all
|
||||||
|
</FilesMatch>
|
||||||
73
public/404.php
Normal file
73
public/404.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
// Page configuration
|
||||||
|
$page_title = '404 - Seite nicht gefunden | HexaHost.de';
|
||||||
|
$page_description = 'Die angeforderte Seite wurde nicht gefunden.';
|
||||||
|
$current_page = '404';
|
||||||
|
|
||||||
|
// Set 404 header
|
||||||
|
http_response_code(404);
|
||||||
|
|
||||||
|
// Include header
|
||||||
|
includeHeader($page_title, $page_description, $current_page);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="error-page">
|
||||||
|
<div class="container">
|
||||||
|
<div class="error-content glass-card">
|
||||||
|
<div class="error-code">404</div>
|
||||||
|
<h1>Seite nicht gefunden</h1>
|
||||||
|
<p>Die angeforderte Seite existiert leider nicht oder wurde verschoben.</p>
|
||||||
|
<div class="error-actions">
|
||||||
|
<a href="index.php" class="btn btn-primary">Zur Startseite</a>
|
||||||
|
<a href="contact.php" class="btn btn-secondary">Kontakt aufnehmen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.error-page {
|
||||||
|
min-height: 60vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4rem 0;
|
||||||
|
}
|
||||||
|
.error-content {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
.error-code {
|
||||||
|
font-size: 6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
background: linear-gradient(135deg, #ff51f9, #a348ff);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.error-content h1 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.error-content p {
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.error-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
includeFooter();
|
||||||
|
?>
|
||||||
73
public/500.php
Normal file
73
public/500.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
// Page configuration
|
||||||
|
$page_title = '500 - Serverfehler | HexaHost.de';
|
||||||
|
$page_description = 'Ein interner Serverfehler ist aufgetreten.';
|
||||||
|
$current_page = '500';
|
||||||
|
|
||||||
|
// Set 500 header
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
// Include header
|
||||||
|
includeHeader($page_title, $page_description, $current_page);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="error-page">
|
||||||
|
<div class="container">
|
||||||
|
<div class="error-content glass-card">
|
||||||
|
<div class="error-code">500</div>
|
||||||
|
<h1>Interner Serverfehler</h1>
|
||||||
|
<p>Es ist ein unerwarteter Fehler aufgetreten. Wir arbeiten bereits an der Lösung.</p>
|
||||||
|
<div class="error-actions">
|
||||||
|
<a href="index.php" class="btn btn-primary">Zur Startseite</a>
|
||||||
|
<a href="contact.php" class="btn btn-secondary">Support kontaktieren</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.error-page {
|
||||||
|
min-height: 60vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4rem 0;
|
||||||
|
}
|
||||||
|
.error-content {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
.error-code {
|
||||||
|
font-size: 6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
background: linear-gradient(135deg, #ff51f9, #a348ff);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.error-content h1 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.error-content p {
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.error-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
includeFooter();
|
||||||
|
?>
|
||||||
@@ -51,9 +51,6 @@
|
|||||||
// Get form data
|
// Get form data
|
||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
// Add honeypot field (hidden field for bot protection)
|
|
||||||
formData.append('website', ''); // Honeypot field
|
|
||||||
|
|
||||||
// Basic validation
|
// Basic validation
|
||||||
const data = {};
|
const data = {};
|
||||||
for (let [key, value] of formData.entries()) {
|
for (let [key, value] of formData.entries()) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Konfiguration laden
|
// Konfiguration laden
|
||||||
require_once 'config.php';
|
require_once 'config/config.php';
|
||||||
|
|
||||||
// Konfiguration verwenden
|
// Konfiguration verwenden
|
||||||
$config = getHexaHostConfig();
|
$config = getHexaHostConfig();
|
||||||
@@ -113,6 +113,32 @@ function sanitizeInput($input) {
|
|||||||
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
|
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sichere IP-Adressen-Erkennung (auch hinter Proxies/Cloudflare)
|
||||||
|
function getClientIP() {
|
||||||
|
$ip_keys = [
|
||||||
|
'HTTP_CF_CONNECTING_IP', // Cloudflare
|
||||||
|
'HTTP_X_FORWARDED_FOR', // Proxy
|
||||||
|
'HTTP_X_REAL_IP', // Nginx Proxy
|
||||||
|
'REMOTE_ADDR' // Standard
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($ip_keys as $key) {
|
||||||
|
if (!empty($_SERVER[$key])) {
|
||||||
|
// Bei X-Forwarded-For kann eine Liste von IPs kommen
|
||||||
|
$ip = explode(',', $_SERVER[$key])[0];
|
||||||
|
$ip = trim($ip);
|
||||||
|
|
||||||
|
// Validiere IP-Format
|
||||||
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||||
|
return $ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback auf REMOTE_ADDR (auch private IPs für lokale Entwicklung)
|
||||||
|
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
// SMTP E-Mail-Versand mit PHPMailer
|
// SMTP E-Mail-Versand mit PHPMailer
|
||||||
function sendEmail($data) {
|
function sendEmail($data) {
|
||||||
global $config;
|
global $config;
|
||||||
@@ -268,7 +294,7 @@ function generateEmailHTML($data) {
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="label">IP-Adresse:</div>
|
<div class="label">IP-Adresse:</div>
|
||||||
<div class="value">' . $_SERVER['REMOTE_ADDR'] . '</div>
|
<div class="value">' . htmlspecialchars(getClientIP()) . '</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -311,7 +337,7 @@ function generateEmailText($data) {
|
|||||||
$text .= "----------\n";
|
$text .= "----------\n";
|
||||||
$text .= $data['message'] . "\n\n";
|
$text .= $data['message'] . "\n\n";
|
||||||
|
|
||||||
$text .= "IP-Adresse: " . $_SERVER['REMOTE_ADDR'] . "\n";
|
$text .= "IP-Adresse: " . getClientIP() . "\n";
|
||||||
$text .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n\n";
|
$text .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n\n";
|
||||||
|
|
||||||
$text .= "---\n";
|
$text .= "---\n";
|
||||||
@@ -334,7 +360,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rate Limiting Check
|
// Rate Limiting Check
|
||||||
$client_ip = $_SERVER['REMOTE_ADDR'];
|
$client_ip = getClientIP();
|
||||||
if (!checkRateLimit($client_ip)) {
|
if (!checkRateLimit($client_ip)) {
|
||||||
http_response_code(429);
|
http_response_code(429);
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
|
|||||||
</div>
|
</div>
|
||||||
<form class="contact-form glass-card" id="contactForm" action="contact-handler.php" method="POST">
|
<form class="contact-form glass-card" id="contactForm" action="contact-handler.php" method="POST">
|
||||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||||
|
<!-- Honeypot-Feld für Bot-Schutz (versteckt via CSS) -->
|
||||||
|
<div style="position: absolute; left: -9999px;" aria-hidden="true">
|
||||||
|
<input type="text" name="website" tabindex="-1" autocomplete="off">
|
||||||
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="firstName">Vorname *</label>
|
<label for="firstName">Vorname *</label>
|
||||||
@@ -143,7 +147,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
|
|||||||
<div class="form-group checkbox-group">
|
<div class="form-group checkbox-group">
|
||||||
<label class="checkbox-label">
|
<label class="checkbox-label">
|
||||||
<input type="checkbox" id="privacy" name="privacy" required>
|
<input type="checkbox" id="privacy" name="privacy" required>
|
||||||
Ich habe die <a href="#" target="_blank">Datenschutzerklärung</a> gelesen und stimme der Verarbeitung meiner Daten zu. *
|
Ich habe die <a href="datenschutz.php" target="_blank">Datenschutzerklärung</a> gelesen und stimme der Verarbeitung meiner Daten zu. *
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
|
|||||||
@@ -3,9 +3,30 @@
|
|||||||
* Helper functions for HexaHost.de
|
* Helper functions for HexaHost.de
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Start session for CSRF token
|
// Sichere Session-Konfiguration
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
// Session-Cookie-Sicherheit
|
||||||
|
ini_set('session.cookie_httponly', 1);
|
||||||
|
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? 1 : 0);
|
||||||
|
ini_set('session.cookie_samesite', 'Strict');
|
||||||
|
ini_set('session.use_strict_mode', 1);
|
||||||
|
ini_set('session.use_only_cookies', 1);
|
||||||
|
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
|
// Session-ID regenerieren bei Login/wichtigen Aktionen (Schutz vor Session Fixation)
|
||||||
|
if (!isset($_SESSION['initiated'])) {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['initiated'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHP Error Display in Produktion deaktivieren
|
||||||
|
if (!defined('DEBUG_MODE') || !DEBUG_MODE) {
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
ini_set('display_startup_errors', 0);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user