Store secret key in DB + autorotate it

This commit is contained in:
Miraty 2022-12-21 00:14:55 +01:00
parent 75e7caed88
commit 57dfb02a40
8 changed files with 16 additions and 40 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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