submit.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <?php
  2. require_once "../env.inc.php";
  3. require_once "../conf.inc.php";
  4. require_once DIR_PHP_LAYOUTS . "header.php";
  5. secureSession::start();
  6. require_once "../access.inc.php";
  7. require_once DIR_PHP_LAYOUTS . "cms.session.php";
  8. // Validation CSRF pour les formulaires POST (sauf exceptions)
  9. if ($_SERVER['REQUEST_METHOD'] === 'POST' && core::ifPost("from")) {
  10. $from = core::getPost("from");
  11. // ========================================
  12. // PROTECTION LOGIN : Rate Limiting + Honeypot
  13. // ========================================
  14. $loginActions = ['login', 'authenticator'];
  15. if (in_array($from, $loginActions)) {
  16. $email = core::getPost('email') ?? '';
  17. // 1. Vérification du honeypot (anti-bot)
  18. if (!loginSecurity::checkHoneypot('website')) {
  19. error_log("[SECURITY] Bot detected via honeypot | IP: " . loginSecurity::getClientIP());
  20. http_response_code(403);
  21. header('Content-Type: application/json');
  22. echo json_encode(['error' => 'Requête invalide', 'blocked' => true]);
  23. exit();
  24. }
  25. // 2. Vérification du rate limiting
  26. $blockStatus = loginSecurity::checkBlocked($email);
  27. if ($blockStatus['blocked']) {
  28. error_log("[SECURITY] Blocked login attempt | IP: " . loginSecurity::getClientIP() . " | Email: $email");
  29. http_response_code(429); // Too Many Requests
  30. header('Content-Type: application/json');
  31. echo json_encode([
  32. 'success' => false,
  33. 'blocked' => true,
  34. 'remaining_time' => $blockStatus['remaining_time'],
  35. 'message' => $blockStatus['message']
  36. ]);
  37. exit();
  38. }
  39. // 3. Appliquer le throttling (délai progressif)
  40. loginSecurity::applyThrottle($email);
  41. }
  42. // Liste des actions nécessitant une protection CSRF (avec consommation du token)
  43. $csrfProtectedActions = [
  44. 'login',
  45. 'user-edit',
  46. 'user-password',
  47. 'parametres-clients',
  48. 'evenement-fiche',
  49. 'lottery-fiche',
  50. 'alertes-emails',
  51. 'parametres-teams-edit',
  52. 'rh-import',
  53. 'restore'
  54. ];
  55. // Actions qui valident le token SANS le consommer (pour les pré-vérifications AJAX)
  56. $csrfCheckOnlyActions = [
  57. 'authenticator' // Vérification Google Auth avant login - ne consomme pas le token
  58. ];
  59. // Si l'action nécessite une protection CSRF
  60. if (in_array($from, $csrfProtectedActions)) {
  61. $isValid = false;
  62. // Tentative validation via header (AJAX)
  63. if (!empty($_SERVER['HTTP_X_CSRF_TOKEN'])) {
  64. $isValid = csrf::validateHeader('cms-ajax', 'X-CSRF-Token');
  65. if (!$isValid) {
  66. $isValid = csrf::validateHeader('login-ajax', 'X-CSRF-Token');
  67. }
  68. }
  69. // Tentative validation via POST (formulaire) - consomme le token
  70. if (!$isValid && isset($_POST['csrf_token'])) {
  71. $isValid = csrf::validatePost('login-form', 'csrf_token', 3600, true);
  72. }
  73. // Si le token est invalide
  74. if (!$isValid) {
  75. error_log("CSRF validation failed for action: $from from IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
  76. if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
  77. http_response_code(403);
  78. header('Content-Type: application/json');
  79. echo json_encode([
  80. 'success' => false,
  81. 'error' => 'csrf_failed',
  82. 'message' => 'Token de sécurité invalide ou expiré. Veuillez recharger la page.'
  83. ]);
  84. exit();
  85. } else {
  86. alert::recError("Erreur de sécurité. Veuillez réessayer.");
  87. header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '/'));
  88. exit();
  89. }
  90. }
  91. }
  92. // Si l'action est une pré-vérification (valide sans consommer)
  93. if (in_array($from, $csrfCheckOnlyActions)) {
  94. $isValid = false;
  95. // Validation via POST sans consommer le token
  96. if (isset($_POST['csrf_token'])) {
  97. $isValid = csrf::validatePost('login-form', 'csrf_token', 3600, false); // false = ne pas consommer
  98. }
  99. if (!$isValid) {
  100. error_log("CSRF check failed for action: $from from IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
  101. http_response_code(403);
  102. header('Content-Type: application/json');
  103. echo json_encode(['error' => 'Token invalide']);
  104. exit();
  105. }
  106. }
  107. }
  108. if (debug::isFile("submit") and (core::ifPost() or core::ifFiles())) {
  109. core::ifPost() ? debug::logSession(core::getPost()) : NULL;
  110. core::ifFiles() ? debug::logSession(core::getFiles()) : NULL;
  111. }
  112. get::submit();