Sécurité et qualité : headers HTTP, permissions .env, lint PHPStan + PHP-CS-Fixer, réorganisation dossiers, scripts de déploiement
This commit is contained in:
+28
-10
@@ -8,18 +8,27 @@ use App\Http\Csrf;
|
||||
|
||||
// --- Helpers AVANT tout usage ---
|
||||
if (!function_exists('env')) {
|
||||
function env(string $key, ?string $default = null): ?string {
|
||||
if (array_key_exists($key, $_ENV) && $_ENV[$key] !== '') return (string)$_ENV[$key];
|
||||
function env(string $key, ?string $default = null): ?string
|
||||
{
|
||||
if (array_key_exists($key, $_ENV) && $_ENV[$key] !== '') {
|
||||
return (string)$_ENV[$key];
|
||||
}
|
||||
$v = getenv($key);
|
||||
if ($v !== false && $v !== '') return (string)$v;
|
||||
if ($v !== false && $v !== '') {
|
||||
return (string)$v;
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
if (!function_exists('db')) {
|
||||
function db(): \PDO { return \App\Infrastructure\Database::get(); }
|
||||
function db(): \PDO
|
||||
{
|
||||
return \App\Infrastructure\Database::get();
|
||||
}
|
||||
}
|
||||
if (!function_exists('url')) {
|
||||
function url(string $path = '/'): string {
|
||||
function url(string $path = '/'): string
|
||||
{
|
||||
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
return $scheme . '://' . $host . $path;
|
||||
@@ -40,7 +49,9 @@ $maxPerWin = (int) env('MAGIC_MAX_PER_WINDOW', '5');
|
||||
$defaultReturn = '/';
|
||||
$sanitize = static function (string $url) use ($defaultReturn): string {
|
||||
$url = trim($url);
|
||||
if ($url === '' || !str_starts_with($url, '/')) return $defaultReturn;
|
||||
if ($url === '' || !str_starts_with($url, '/')) {
|
||||
return $defaultReturn;
|
||||
}
|
||||
return $url;
|
||||
};
|
||||
$returnTo = $sanitize((string)($_GET['return_to'] ?? ($_SERVER['HTTP_REFERER'] ?? $defaultReturn)));
|
||||
@@ -49,7 +60,10 @@ $returnTo = $sanitize((string)($_GET['return_to'] ?? ($_SERVER['HTTP_REFERER'] ?
|
||||
$oidcEnabled = (bool) (env('OIDC_ISSUER') && env('OIDC_CLIENT_ID'));
|
||||
$oidcLoginUrl = '/login/oidc' . ($returnTo ? ('?return_to=' . urlencode($returnTo)) : '');
|
||||
$oidcAuto = (isset($_GET['sso']) && $_GET['sso'] === '1') || (env('OIDC_AUTO', '0') === '1');
|
||||
if ($oidcEnabled && $oidcAuto) { header('Location: ' . $oidcLoginUrl, true, 302); exit; }
|
||||
if ($oidcEnabled && $oidcAuto) {
|
||||
header('Location: ' . $oidcLoginUrl, true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- form: demande de lien magique ---
|
||||
$errors = [];
|
||||
@@ -65,13 +79,15 @@ if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
||||
} else {
|
||||
// rate limit simple par email et IP
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
|
||||
if (strpos($ip, ',') !== false) $ip = trim(explode(',', $ip, 2)[0]);
|
||||
if (strpos($ip, ',') !== false) {
|
||||
$ip = trim(explode(',', $ip, 2)[0]);
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
// purge expirés / consommés
|
||||
$pdo->prepare("DELETE FROM auth_magic_links WHERE email = :e AND (expires_at < NOW() OR consumed_at IS NOT NULL)")
|
||||
$pdo->prepare('DELETE FROM auth_magic_links WHERE email = :e AND (expires_at < NOW() OR consumed_at IS NOT NULL)')
|
||||
->execute([':e' => $email]);
|
||||
|
||||
// 1) cooldown: refuser si un envoi récent < coolMin
|
||||
@@ -130,7 +146,9 @@ if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
||||
$okMsg = "Un lien vient d'être envoyé. Vérifiez votre boîte de réception et le dossier spam/indésirables.";
|
||||
|
||||
} catch (\Throwable $ex) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$errors[] = $ex->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user