Add HTTPS warning for vault and guard crypto availability
This commit is contained in:
@@ -27,6 +27,10 @@ Instruções rápidas para clonar o repositório e subir o ambiente via Docker.
|
||||
- Adminer (banco): http://localhost:8081 — host `db`, usuário `telseg`, senha `telseg`, base `telseg`
|
||||
- Postgres: porta 5432 exposta localmente (opcional)
|
||||
|
||||
## Sobre o Cofre de Senhas (HTTPS)
|
||||
- O cofre usa WebCrypto do navegador (PBKDF2 + AES-GCM). Para funcionar, o navegador exige contexto seguro (HTTPS ou localhost).
|
||||
- Se acessar via IP/HTTP (ex.: `http://192.168.x.x:4242`), o botão de desbloquear pode não responder. Use HTTPS (mesmo com certificado self-signed) ou acesse via `https://` atrás de um proxy reverso.
|
||||
|
||||
## Parar os serviços
|
||||
- `docker compose down` para parar
|
||||
- `docker compose down -v` se também quiser descartar os volumes do Postgres (dados serão perdidos)
|
||||
|
||||
@@ -263,6 +263,17 @@ function hideModal(id) { const el = document.getElementById(id); el?.classList.a
|
||||
function escapeHTML(s) { return (s ?? "").replace(/[&<>"]/g, c => ({"&":"&","<":"<",">":">","\"":"""}[c])); }
|
||||
function escapeAttr(s) { return escapeHTML(s).replace(/'/g, "'"); }
|
||||
function formatDate(s) { try { const [y,m,d] = (s||"").split("-"); return d && m && y ? `${d}/${m}/${y}` : s; } catch { return s; } }
|
||||
|
||||
// WebCrypto guard (requer HTTPS/localhost)
|
||||
const CRYPTO_REQUIRE_MSG = "O cofre precisa de HTTPS (ou localhost) porque o navegador bloqueia a criptografia em origens inseguras.";
|
||||
let cryptoWarned = false;
|
||||
function hasCrypto() { return !!(globalThis.crypto && globalThis.crypto.subtle); }
|
||||
function ensureCrypto(msgTarget) {
|
||||
if (hasCrypto()) return true;
|
||||
if (msgTarget) msgTarget.textContent = CRYPTO_REQUIRE_MSG;
|
||||
if (!cryptoWarned) { alert(CRYPTO_REQUIRE_MSG); cryptoWarned = true; }
|
||||
return false;
|
||||
}
|
||||
// ------- Cofre de Senhas (WebCrypto) -------
|
||||
async function refreshCreds() {
|
||||
if (state.useApi) {
|
||||
@@ -421,6 +432,7 @@ function b642ab(b64) {
|
||||
}
|
||||
|
||||
async function deriveKey(password, saltB64, iterations) {
|
||||
if (!hasCrypto()) throw new Error('crypto_unavailable');
|
||||
const salt = b642ab(saltB64);
|
||||
const keyMaterial = await crypto.subtle.importKey(
|
||||
"raw", textEnc.encode(password), { name: "PBKDF2" }, false, ["deriveKey"]
|
||||
@@ -456,6 +468,11 @@ function renderVaultGate() {
|
||||
const status = $("#vaultStatus");
|
||||
$("#vaultLocked").classList.remove("hidden");
|
||||
$("#vaultUnlocked").classList.add("hidden");
|
||||
if (!ensureCrypto(status)) {
|
||||
setForm.classList.add("hidden");
|
||||
unlockForm.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
if (!meta) {
|
||||
status.textContent = "Proteja suas senhas com uma senha mestra.";
|
||||
setForm.classList.remove("hidden");
|
||||
@@ -468,6 +485,7 @@ function renderVaultGate() {
|
||||
}
|
||||
|
||||
async function unlockVault(password) {
|
||||
if (!ensureCrypto($("#vaultStatus"))) return;
|
||||
const meta = getVault();
|
||||
if (!meta) throw new Error("Cofre não configurado");
|
||||
const key = await deriveKey(password, meta.salt, meta.iterations || VAULT_ITERS);
|
||||
@@ -805,6 +823,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
$("#setVaultForm").addEventListener("submit", async (ev) => {
|
||||
ev.preventDefault();
|
||||
if (!ensureCrypto($("#vaultStatus"))) return;
|
||||
const p1 = $("#vaultPass1").value;
|
||||
const p2 = $("#vaultPass2").value;
|
||||
if (!p1 || p1 !== p2) { alert("Senhas não conferem."); return; }
|
||||
|
||||
Reference in New Issue
Block a user