Browse Source

Store secret key in DB + autorotate it

Miraty 2 years ago
parent
commit
57dfb02a40

+ 2 - 0
db/schema.sql

@@ -6,6 +6,8 @@ CREATE TABLE IF NOT EXISTS "params" (
 );
 INSERT INTO "params"("name", "value") VALUES("instance_bucket_tokens", "0");
 INSERT INTO "params"("name", "value") VALUES("instance_bucket_last_update", "0");
+INSERT INTO "params"("name", "value") VALUES("secret_key", "0");
+INSERT INTO "params"("name", "value") VALUES("secret_key_last_change", "0");
 INSERT INTO "params"("name", "value") VALUES("username_salt", "00000000000000000000000000000000"); -- Should be unique and secret ; generate one using `openssl rand -hex 16` ; can't be changed without breaking current accounts login
 CREATE TABLE IF NOT EXISTS "users" (
 	"id"                 TEXT    NOT NULL UNIQUE,

+ 6 - 6
fn/common.php

@@ -113,13 +113,13 @@ function linkToDocs($ref, $title) {
 	- the user's id
 	- that a same user used a token multiple times (by using a unique salt for each token)
 */
-define('SECRET_KEY_FILE', sys_get_temp_dir() . '/Niver.key');
-if (!file_exists(SECRET_KEY_FILE)) {
-	$original_umask = umask(0077);
-	file_put_contents(SECRET_KEY_FILE, random_bytes(32));
-	umask($original_umask);
+if (time() - query('select', 'params', ['name' => 'secret_key_last_change'], 'value')[0] >= 86400 * 20) {
+	DB->prepare("UPDATE params SET value = :secret_key WHERE name = 'secret_key';")
+	->execute([':secret_key' => bin2hex(random_bytes(32))]);
+	DB->prepare("UPDATE params SET value = :last_change WHERE name = 'secret_key_last_change';")
+	->execute([':last_change' => time()]);
 }
-define('SECRET_KEY', file_get_contents(SECRET_KEY_FILE));
+define('SECRET_KEY', hex2bin(query('select', 'params', ['name' => 'secret_key'], 'value')[0]));
 function getAuthToken() {
 	$salt = bin2hex(random_bytes(4));
 	$hash = hash_hmac('sha256', $salt . ($_SESSION['id'] ?? ''), SECRET_KEY);

+ 2 - 10
pg-view/ht/add-http-dns.php

@@ -1,11 +1,3 @@
-<?php
-
-$dirsStatuses = dirsStatuses('dns', 'http');
-
-$proof = getAuthToken();
-
-?>
-
 <p>
 	Ajouter sur un dossier de site un accès <?= linkToDocs('http', 'HTTP') ?> par <?= linkToDocs('dns', 'DNS') ?> et <?= linkToDocs('tls', 'TLS') ?> <?= linkToDocs('ca', 'authentifié par <em>Let\'s Encrypt</em>') ?>.
 </p>
@@ -25,7 +17,7 @@ $proof = getAuthToken();
 	</dd>
 	<dt><code>TXT</code></dt>
 	<dd>
-		<code><?= SERVER_NAME ?>_domain-verification=<?= $proof ?></code>
+		<code><?= SERVER_NAME ?>_domain-verification=<?= getAuthToken() ?></code>
 	</dd>
 </dl>
 
@@ -36,7 +28,7 @@ $proof = getAuthToken();
 	<select required="" name="dir" id="dir">
 		<option value="" disabled="" selected="">---</option>
 <?php
-foreach ($dirsStatuses as $dir => $alreadyEnabled)
+foreach (dirsStatuses('dns', 'http') as $dir => $alreadyEnabled)
 	echo '		<option' . ($alreadyEnabled ? ' disabled=""' : '') . ' value="' . $dir . '">' . $dir . '</option>' . LF;
 ?>
 	</select>

+ 1 - 7
pg-view/ht/add-http-onion.php

@@ -1,9 +1,3 @@
-<?php
-
-$dirsStatuses = dirsStatuses('onion', 'http');
-
-?>
-
 <p>
 	Ajouter un accès en .onion sur un dossier
 </p>
@@ -13,7 +7,7 @@ $dirsStatuses = dirsStatuses('onion', 'http');
 	<select required="" name="dir" id="dir">
 		<option value="" disabled="" selected="">---</option>
 <?php
-foreach ($dirsStatuses as $dir => $alreadyEnabled)
+foreach (dirsStatuses('onion', 'http') as $dir => $alreadyEnabled)
 	echo '		<option' . ($alreadyEnabled ? ' disabled=""' : '') . ' value="' . $dir . '">' . $dir . '</option>' . LF;
 ?>
 	</select>

+ 1 - 7
pg-view/ht/del-http-dns.php

@@ -1,9 +1,3 @@
-<?php
-
-$dirsStatuses = dirsStatuses('dns', 'http');
-
-?>
-
 <p>
 	Retirer un accès DNS et TLS d'un dossier
 </p>
@@ -13,7 +7,7 @@ $dirsStatuses = dirsStatuses('dns', 'http');
 	<select required="" name="dir" id="dir">
 		<option value="" disabled="" selected="">---</option>
 <?php
-foreach ($dirsStatuses as $dir => $alreadyEnabled)
+foreach (dirsStatuses('dns', 'http') as $dir => $alreadyEnabled)
 	echo '		<option' . ($alreadyEnabled ? '' : ' disabled=""') . ' value="' . $dir . '">' . $dir . '</option>' . LF;
 ?>
 	</select>

+ 1 - 7
pg-view/ht/del-http-onion.php

@@ -1,9 +1,3 @@
-<?php
-
-$dirsStatuses = dirsStatuses('onion', 'http');
-
-?>
-
 <p>
 	Retirer un accès Onion d'un dossier
 </p>
@@ -13,7 +7,7 @@ $dirsStatuses = dirsStatuses('onion', 'http');
 	<select required="" name="dir" id="dir">
 		<option value="" disabled="" selected="">---</option>
 <?php
-foreach ($dirsStatuses as $dir => $alreadyEnabled)
+foreach (dirsStatuses('onion', 'http') as $dir => $alreadyEnabled)
 	echo '		<option' . ($alreadyEnabled ? '' : ' disabled=""') . ' value="' . $dir . '">' . $dir . '</option>' . LF;
 ?>
 	</select>

+ 1 - 1
pg-view/ns/zone-add.php

@@ -1,5 +1,5 @@
 <p>
-	Pour prouver que vous possédez bien ce domaine, il doit posséder un <?= linkToDocs('ns-record', 'enregistrement NS') ?> égal à <code><?= $proof ?>._domain-verification.<?= SERVER_NAME ?>.</code> lors du traitement de ce formulaire.
+	Pour prouver que vous possédez bien ce domaine, il doit posséder un <?= linkToDocs('ns-record', 'enregistrement NS') ?> égal à <code><?= getAuthToken() ?>._domain-verification.<?= SERVER_NAME ?>.</code> lors du traitement de ce formulaire.
 </p>
 
 <p>

+ 2 - 2
router.php

@@ -2,12 +2,12 @@
 define('TIME', hrtime(true));
 define('CONF', parse_ini_file(__DIR__ . '/config.ini', true, INI_SCANNER_TYPED));
 
+define('DB', new PDO('sqlite:' . CONF['common']['root_path'] . '/db/niver.db'));
+
 foreach (array_diff(scandir(CONF['common']['root_path'] . '/fn'), array('..', '.')) as $file)
 	require CONF['common']['root_path'] . '/fn/' . $file;
 require 'pages.php';
 
-define('DB', new PDO('sqlite:' . CONF['common']['root_path'] . '/db/niver.db'));
-
 const LF = "\n";
 
 const PLACEHOLDER_DOMAIN = 'example'; // From RFC2606: Reserved Top Level DNS Names > 2. TLDs for Testing, & Documentation Examples