auth.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <?php
  2. declare(strict_types=1);
  3. // Admin session, password check and CSRF for the management UI. Merges the two
  4. // near-identical login halves of the PSA update-server/manage.php and
  5. // backup-server/manage.php into one implementation.
  6. require_once __DIR__ . "/bootstrap.php";
  7. function manageStartSession(): void
  8. {
  9. if (session_status() !== PHP_SESSION_NONE) {
  10. return;
  11. }
  12. $isHttps =
  13. (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off") ||
  14. (isset($_SERVER["SERVER_PORT"]) && (int) $_SERVER["SERVER_PORT"] === 443) ||
  15. strtolower((string) ($_SERVER["HTTP_X_FORWARDED_PROTO"] ?? "")) === "https";
  16. ini_set("session.use_strict_mode", "1");
  17. ini_set("session.cookie_httponly", "1");
  18. ini_set("session.cookie_secure", $isHttps ? "1" : "0");
  19. ini_set("session.cookie_samesite", "Lax");
  20. session_name("manage_session");
  21. session_start();
  22. }
  23. function managePasswordConfigured(): bool
  24. {
  25. if (defined("MANAGE_ADMIN_PASSWORD_HASH")) {
  26. $hash = trim((string) MANAGE_ADMIN_PASSWORD_HASH);
  27. if ($hash !== "" && !str_contains($hash, "replace-this")) {
  28. return true;
  29. }
  30. }
  31. return defined("MANAGE_ADMIN_PASSWORD") && trim((string) MANAGE_ADMIN_PASSWORD) !== "";
  32. }
  33. function managePasswordMatches(string $password): bool
  34. {
  35. if (defined("MANAGE_ADMIN_PASSWORD_HASH")) {
  36. $hash = trim((string) MANAGE_ADMIN_PASSWORD_HASH);
  37. if ($hash !== "" && !str_contains($hash, "replace-this")) {
  38. return password_verify($password, $hash);
  39. }
  40. }
  41. if (defined("MANAGE_ADMIN_PASSWORD")) {
  42. return hash_equals((string) MANAGE_ADMIN_PASSWORD, $password);
  43. }
  44. return false;
  45. }
  46. function manageIsLoggedIn(): bool
  47. {
  48. return !empty($_SESSION["manage_logged_in"]);
  49. }
  50. function manageLogin(): void
  51. {
  52. session_regenerate_id(true);
  53. $_SESSION["manage_logged_in"] = true;
  54. $_SESSION["manage_logged_in_at"] = time();
  55. }
  56. function manageLogout(): void
  57. {
  58. unset(
  59. $_SESSION["manage_logged_in"],
  60. $_SESSION["manage_logged_in_at"],
  61. $_SESSION["manage_csrf_token"],
  62. );
  63. }
  64. // Redirects to the login page instead of rendering. Every admin page calls this
  65. // as its first statement after the includes.
  66. function manageRequireLogin(): void
  67. {
  68. manageStartSession();
  69. if (manageIsLoggedIn()) {
  70. return;
  71. }
  72. $target = basename((string) ($_SERVER["SCRIPT_NAME"] ?? ""));
  73. header("Location: login.php?next=" . rawurlencode($target));
  74. exit;
  75. }
  76. function manageCsrfToken(): string
  77. {
  78. if (empty($_SESSION["manage_csrf_token"])) {
  79. $_SESSION["manage_csrf_token"] = bin2hex(random_bytes(32));
  80. }
  81. return (string) $_SESSION["manage_csrf_token"];
  82. }
  83. function manageCsrfIsValid(string $token): bool
  84. {
  85. return !empty($_SESSION["manage_csrf_token"]) &&
  86. hash_equals((string) $_SESSION["manage_csrf_token"], $token);
  87. }
  88. function manageCsrfField(): string
  89. {
  90. return '<input type="hidden" name="csrf_token" value="' .
  91. manageEscape(manageCsrfToken()) .
  92. '">';
  93. }
  94. // Guard for every mutating POST in the admin UI.
  95. function manageRequireCsrf(): void
  96. {
  97. if (!manageCsrfIsValid((string) ($_POST["csrf_token"] ?? ""))) {
  98. throw new RuntimeException(
  99. "Ungültiges Sicherheitstoken. Bitte die Seite neu laden und erneut versuchen.",
  100. );
  101. }
  102. }