aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.ts.sample17
-rw-r--r--routes/email.ts2
-rw-r--r--routes/payments.ts2
-rw-r--r--routes/users.ts9
4 files changed, 22 insertions, 8 deletions
diff --git a/config.ts.sample b/config.ts.sample
index 1becf362..80162f5f 100644
--- a/config.ts.sample
+++ b/config.ts.sample
@@ -12,7 +12,10 @@ export default {
"wss://relay.nostr.bg",
"wss://nostr.orangepill.dev"
],
- jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b",
+ // SECURITY: generate a fresh 32-byte secret before deploying —
+ // `openssl rand -hex 32`. This also keys @fastify/secure-session, so a
+ // published value lets anyone forge session JWTs and read the session cookie.
+ jwt: "0000000000000000000000000000000000000000000000000000000000000000",
bitcoin: {
host: "bc",
wallet: "coinos",
@@ -33,10 +36,16 @@ export default {
lightning: "/home/bun/app/data/lightning/regtest/lightning-rpc",
lightningb: "/home/bun/app/data/lightningb/regtest/lightning-rpc",
fee: 0.001,
- adminpass: "pwa",
+ // SECURITY: the admin login path is disabled when this is empty (fails
+ // closed). Set a strong, unique value only if you need admin impersonation;
+ // never ship the sample value.
+ adminpass: "",
support: "[email protected]",
- nostrKey: "nsec1msk3dqy2pzqy2murmm5s5gejfgkjkm2zesnh266gud2wcvhcunzqlu0h90",
- nostrKey2: "nsec17ugnsk0qrnakf7xaxzckya4xvx58q063k6q4vq2l577k4uyv9r0qmdcgm5",
+ // SECURITY: generate fresh keys — never deploy these sample values. These are
+ // the server NWC wallet-service key and the zap-receipt signer.
+ // node -e "import('nostr-tools').then(n=>console.log(n.nip19.nsecEncode(n.generateSecretKey())))"
+ nostrKey: "nsec1replacewithafreshlygeneratedkeydonotusethissamplevalue0",
+ nostrKey2: "nsec1replacewithafreshlygeneratedkeydonotusethissamplevalue1",
mintUrl: "http://mint:3338",
square: {
environment: SquareEnvironment.Sandbox,
diff --git a/routes/email.ts b/routes/email.ts
index 1b8dd9f0..eb256f1a 100644
--- a/routes/email.ts
+++ b/routes/email.ts
@@ -22,7 +22,7 @@ export default {
})
.json();
- if (success || response === config.adminpass) {
+ if (success || (!!config.adminpass && response === config.adminpass)) {
body.token = undefined;
warn("support request from", email);
diff --git a/routes/payments.ts b/routes/payments.ts
index 9751b421..4f0e7f6e 100644
--- a/routes/payments.ts
+++ b/routes/payments.ts
@@ -625,7 +625,7 @@ export default {
body: { secret },
} = req;
try {
- if (secret !== config.adminpass) fail("unauthorized");
+ if (!config.adminpass || secret !== config.adminpass) fail("unauthorized");
await s("freeze", true);
res.send("ok");
} catch (e) {
diff --git a/routes/users.ts b/routes/users.ts
index 63699777..eafcd74a 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -70,7 +70,7 @@ const verifyRecaptcha = async (response, req?) => {
},
})
.json();
- return success || response === config.adminpass;
+ return success || (!!config.adminpass && response === config.adminpass);
} catch {
return false;
}
@@ -383,7 +383,12 @@ export default {
if (ipCount === 1) await db.expire(ipKey, 10);
if (ipCount > 30) return res.code(429).send({});
- const isAdmin = password === config?.adminpass;
+ // Fail closed: an unset adminpass must never authenticate, and an omitted
+ // password field must never coincide with an unset value (undefined ===
+ // undefined). Both the configured value and the supplied one must be
+ // non-empty and match exactly.
+ const isAdmin =
+ !!config?.adminpass && !!password && password === config.adminpass;
if (!isAdmin) {
const recaptchaOk = await verifyRecaptcha(recaptcha, req);