Selaa lähdekoodia

Use Argon2id instead of Bcrypt for password storage (and other things about passwords)

Miraty 3 vuotta sitten
vanhempi
commit
ae7bf5ef4e
9 muutettua tiedostoa jossa 81 lisäystä ja 18 poistoa
  1. 1 1
      auth/index.php
  2. 6 1
      auth/login.php
  3. 3 1
      auth/register.php
  4. 0 13
      inc/all.inc.php
  5. 59 0
      inc/auth.inc.php
  6. 8 0
      inc/const.inc.php
  7. 1 1
      inc/pages.inc.php
  8. 2 1
      less/form.less
  9. 1 0
      top.inc.php

+ 1 - 1
auth/index.php

@@ -4,7 +4,7 @@
 
 <a class="authButton" href="logout">Se déconnecter</a>
 <br>
-<a class="authButton" href="password">Changer le mot de passe</a>
+<a class="authButton" href="password">Changer la clé de passe</a>
 
 <?php } else { ?>
   Vous devez être authentifié·e pour utiliser Niver

+ 6 - 1
auth/login.php

@@ -6,7 +6,7 @@
   <br>
 
   <label for="password">Clé de passe</label><br>
-  <input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="password" name="password" type="password" placeholder="************">
+  <input required="" autocomplete="current-password" minlength="8" maxlength="1024" pattern="<?= PASSWORD_REGEX ?>" id="password" name="password" type="password" placeholder="************************">
   <br>
 
   <input type="submit">
@@ -25,8 +25,13 @@ if (isset($_POST['username']) AND isset($_POST['password'])) {
     exit("Le format du nom du compte n'est pas valide !");
 
   if (checkPassword($_POST['username'], $_POST['password'])) {
+
     $_SESSION['username'] = htmlspecialchars($_POST['username']);
     $_SESSION['sftp_enabled'] = sftpStatus($_SESSION['username']);
+
+    if (outdatedPasswordHash($_SESSION['username']))
+      changePassword($_SESSION['username'], $_POST['password']);
+
     if (isset($_GET['redir'])) {
       if (preg_match("/^[0-9a-z\/-]+$/", $_GET['redir']))
         header('Location: ' . PREFIX . "/" . $_GET['redir']);

+ 3 - 1
auth/register.php

@@ -14,7 +14,7 @@ if (isset($_POST['username']) AND isset($_POST['password'])) {
   $userExist = userExist($username);
   if (!$userExist) {
 
-    $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
+    $password = hashPassword($_POST['password']);
 
     $db = new PDO('sqlite:' . DB_PATH);
 
@@ -54,6 +54,8 @@ if (isset($_POST['username']) AND isset($_POST['password'])) {
   <label for="password">
     <details>
       <summary>Clé de passe</summary>
+      Une clé de passe sécurisée est trop compliquée à deviner pour une attaque qui testerais automatiquement plein de clés de passe tout en connaissant d'autres informations et secrets sur vous.
+      <br>
       Minimum 8 caractères si elle contient minuscule, majuscule et chiffre, ou minimum 10 caractères sinon.
     </details>
 

+ 0 - 13
inc/all.inc.php

@@ -19,19 +19,6 @@ function checkAction($action) {
     exit("ERROR: wrong value for action");
 }
 
-function checkPassword($username, $password) {
-  $username2[0] = $username;
-
-  $db = new PDO('sqlite:' . DB_PATH);
-
-  $op = $db->prepare('SELECT username, password FROM users WHERE username = ?');
-  $op->execute($username2);
-
-  $dbPassword = $op->fetch()['password'];
-
-  return password_verify($password, $dbPassword);
-}
-
 function userExist($username) {
   $usernameArray[0] = $username;
 

+ 59 - 0
inc/auth.inc.php

@@ -0,0 +1,59 @@
+<?php
+if (strpos($_SERVER['PHP_SELF'], "inc.php") !== false)
+  exit("This file is meant to be included.");
+
+function hashPassword($password) {
+  return password_hash($password, ALGO_PASSWORD, OPTIONS_PASSWORD);
+}
+
+function checkPassword($username, $password) {
+  $username2[0] = $username;
+
+  $db = new PDO('sqlite:' . DB_PATH);
+
+  $op = $db->prepare('SELECT username, password FROM users WHERE username = ?');
+  $op->execute($username2);
+
+  $dbPassword = $op->fetch()['password'];
+
+  return password_verify($password, $dbPassword);
+}
+
+function outdatedPasswordHash($username) {
+  $username2[0] = $username;
+
+  $db = new PDO('sqlite:' . DB_PATH);
+
+  $op = $db->prepare('SELECT username, password FROM users WHERE username = ?');
+  $op->execute($username2);
+
+  $dbPassword = $op->fetch()['password'];
+
+  return password_needs_rehash($dbPassword, ALGO_PASSWORD, OPTIONS_PASSWORD);
+}
+
+function changePassword($username, $password) {
+  $password = hashPassword($password);
+
+  $db = new PDO('sqlite:' . DB_PATH);
+
+  $stmt = $db->prepare("UPDATE users SET password = :password WHERE username = :username");
+
+  $stmt->bindParam(':username', $username);
+  $stmt->bindParam(':password', $password);
+
+  $stmt->execute();
+}
+
+function antiCSRF() {
+
+  if (!isset($_SERVER['HTTP_SEC_FETCH_SITE']) AND !isset($_SERVER['HTTP_ORIGIN']))
+    exit("ERROR: Browser sent neither Sec-Fetch-Site nor Origin HTTP headers, so anti-CSRS verification can't be done.");
+
+  if (isset($_SERVER['HTTP_ORIGIN']) AND $_SERVER['HTTP_ORIGIN'] !== "https://niver.4.niv.re")
+    exit("ERROR: Anti-CSRF verification failed");
+
+  if (isset($_SERVER['HTTP_SEC_FETCH_SITE']) AND $_SERVER['HTTP_SEC_FETCH_SITE'] !== "same-origin")
+    exit("ERROR: Anti-CSRF verification failed");
+
+}

+ 8 - 0
inc/const.inc.php

@@ -40,6 +40,14 @@ define("USERNAME_REGEX", "^[a-z]{4,32}$");
 define("PASSWORD_REGEX", "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]{8,1024}|.{10,1024}$");
 define("SUBDOMAIN_REGEX", "^[a-z]{4,63}$");
 
+// Password storage security
+define("ALGO_PASSWORD", PASSWORD_ARGON2ID);
+define("OPTIONS_PASSWORD", array(
+  "memory_cost" => 65536,
+  "time_cost" => 24,
+  "threads" => 64,
+));
+
 // Color scheme
 define("THEME", array(
   // Displayed on light theme

+ 1 - 1
inc/pages.inc.php

@@ -77,7 +77,7 @@ switch (SERVICE) {
         $page['title'] = "Créer un compte";
       break;
       case "password":
-        $page['title'] = "Changer le mot de passe";
+        $page['title'] = "Changer la clé de passe";
       break;
       case "logout":
         $page['title'] = "Déconnexion";

+ 2 - 1
less/form.less

@@ -52,7 +52,8 @@ input#subdomain, input#ttl-value {
 }
 
 #password {
-  width: 24ch;
+  width: 32ch;
+  text-align: center;
 }
 
 #username {

+ 1 - 0
top.inc.php

@@ -10,6 +10,7 @@ require "inc/format.inc.php";
 require "inc/ht.inc.php";
 require "inc/ns.inc.php";
 require "inc/reg.inc.php";
+require "inc/auth.inc.php";
 // Page titles definition
 require "inc/pages.inc.php";