Sign-up guard: honeypot is read-only until a trusted focus and ignores browser autofill (own email); masked value logging
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -108,7 +108,7 @@ function head(o) {
|
|||||||
+ '</head><body><div class="wrap bl">';
|
+ '</head><body><div class="wrap bl">';
|
||||||
}
|
}
|
||||||
function tail() {
|
function tail() {
|
||||||
return '</div><script src="/assets/common.js?v=20260912e"></script><script src="/assets/blog-page.js?v=20260912a"></script></body></html>';
|
return '</div><script src="/assets/common.js?v=20260913a"></script><script src="/assets/blog-page.js?v=20260912a"></script></body></html>';
|
||||||
}
|
}
|
||||||
const authorBox = () => '<div class="author"><img src="/logo-icon.png" alt="InstantAdPay"><div><b>' + AUTHOR + '</b><span>Founder of InstantAdPay and the Crypto Team Build Network. Twenty-plus years of internet marketing, and a habit of writing down what actually worked.</span></div></div>';
|
const authorBox = () => '<div class="author"><img src="/logo-icon.png" alt="InstantAdPay"><div><b>' + AUTHOR + '</b><span>Founder of InstantAdPay and the Crypto Team Build Network. Twenty-plus years of internet marketing, and a habit of writing down what actually worked.</span></div></div>';
|
||||||
const cardHtml = p => '<a class="post-card" href="/blog/' + esc(p.slug) + '">' + (p.cover ? '<img src="' + esc(p.cover) + '" alt="" loading="lazy" style="width:100%;border-radius:10px;margin:0 0 12px">' : '') + '<h2>' + esc(p.title) + '</h2><p>' + esc(p.excerpt) + '</p><p class="meta">' + fmtDate(p.publishedAt || p.created) + ' · ' + readMinutes(p.body) + ' min read' + (p.tags.length ? ' · ' + p.tags.map(esc).join(', ') : '') + '</p></a>';
|
const cardHtml = p => '<a class="post-card" href="/blog/' + esc(p.slug) + '">' + (p.cover ? '<img src="' + esc(p.cover) + '" alt="" loading="lazy" style="width:100%;border-radius:10px;margin:0 0 12px">' : '') + '<h2>' + esc(p.title) + '</h2><p>' + esc(p.excerpt) + '</p><p class="meta">' + fmtDate(p.publishedAt || p.created) + ' · ' + readMinutes(p.body) + ' min read' + (p.tags.length ? ' · ' + p.tags.map(esc).join(', ') : '') + '</p></a>';
|
||||||
|
|||||||
+1
-1
@@ -396,7 +396,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/admin.js?v=20260912c"></script>
|
<script src="/assets/admin.js?v=20260912c"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-1
@@ -154,12 +154,23 @@ window.IAP = (function () {
|
|||||||
host.querySelectorAll('.ic-btn').forEach(b => b.addEventListener('click', () => { host.innerHTML = ''; host.hidden = true; resolve(b.textContent); }, { once: true }));
|
host.querySelectorAll('.ic-btn').forEach(b => b.addEventListener('click', () => { host.innerHTML = ''; host.hidden = true; resolve(b.textContent); }, { once: true }));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// honeypot fields (join + sign-in): read-only until a trusted focus, so browser autofill and
|
||||||
|
// password managers leave them alone; a value that appeared without a trusted event is ignored
|
||||||
|
function armHoneypots() {
|
||||||
|
document.querySelectorAll('.hp-field').forEach(el => {
|
||||||
|
if (el.dataset.armed) return; el.dataset.armed = '1'; el.readOnly = true;
|
||||||
|
const touch = e => { if (e.isTrusted) { el.readOnly = false; el.dataset.touched = '1'; } };
|
||||||
|
el.addEventListener('focus', touch); el.addEventListener('input', touch); el.addEventListener('keydown', touch);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
armHoneypots(); document.addEventListener('DOMContentLoaded', armHoneypots);
|
||||||
|
const hpValue = el => (el && el.dataset.touched === '1') ? (el.value || '') : '';
|
||||||
async function requestCode(email, opts) {
|
async function requestCode(email, opts) {
|
||||||
const o = opts || {};
|
const o = opts || {};
|
||||||
let pick = null;
|
let pick = null;
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
const r = await (await fetch('/api/auth/email/start', { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
const r = await (await fetch('/api/auth/email/start', { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ email, fts: FORM_TS, hp_field_x9: (o.honeypot && o.honeypot.value) || '', pick }) })).json();
|
body: JSON.stringify({ email, fts: FORM_TS, hp_field_x9: hpValue(o.honeypot), pick }) })).json();
|
||||||
if (r.challenge && o.host) { pick = await iconCheck(o.host, r.challenge, r.error); continue; }
|
if (r.challenge && o.host) { pick = await iconCheck(o.host, r.challenge, r.error); continue; }
|
||||||
if (r.error) throw new Error(r.error);
|
if (r.error) throw new Error(r.error);
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/contract.js?v=20260908p"></script>
|
<script src="/assets/contract.js?v=20260908p"></script>
|
||||||
<script src="/assets/chat.js?v=20260906m"></script>
|
<script src="/assets/chat.js?v=20260906m"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<p class="muted small">You decide whether, and how much, to spend. Never spend more than you can afford to lose.</p>
|
<p class="muted small">You decide whether, and how much, to spend. Never spend more than you can afford to lose.</p>
|
||||||
</div>
|
</div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/legal.js?v=20260908a"></script>
|
<script src="/assets/legal.js?v=20260908a"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -67,6 +67,6 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -461,7 +461,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/wallet.js?v=20260909c"></script>
|
<script src="/assets/wallet.js?v=20260909c"></script>
|
||||||
<script src="/assets/home.js?v=20260912a"></script>
|
<script src="/assets/home.js?v=20260912a"></script>
|
||||||
<script src="/assets/chat.js?v=20260906m"></script>
|
<script src="/assets/chat.js?v=20260906m"></script>
|
||||||
|
|||||||
+1
-1
@@ -156,7 +156,7 @@
|
|||||||
InstantAdPay · <a href="/contract">Contract</a> · <a href="/terms">Terms</a> · <a href="/privacy">Privacy</a> · <a href="/disclaimer">Disclaimer</a>
|
InstantAdPay · <a href="/contract">Contract</a> · <a href="/terms">Terms</a> · <a href="/privacy">Privacy</a> · <a href="/disclaimer">Disclaimer</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/join.js?v=20260912d"></script>
|
<script src="/assets/join.js?v=20260912d"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -85,7 +85,7 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/launch.js?v=20260911b"></script>
|
<script src="/assets/launch.js?v=20260911b"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@
|
|||||||
<div>InstantAdPay · <a href="/">how it works</a> · <a id="contractLink" href="#" target="_blank" rel="noopener">contract source ↗</a></div>
|
<div>InstantAdPay · <a href="/">how it works</a> · <a id="contractLink" href="#" target="_blank" rel="noopener">contract source ↗</a></div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/ledger.js?v=20260906m"></script>
|
<script src="/assets/ledger.js?v=20260906m"></script>
|
||||||
<script src="/assets/chat.js?v=20260906m"></script>
|
<script src="/assets/chat.js?v=20260906m"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+1
-1
@@ -912,7 +912,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/wallet.js?v=20260911a"></script>
|
<script src="/assets/wallet.js?v=20260911a"></script>
|
||||||
<script src="/assets/promo.js?v=20260911a"></script>
|
<script src="/assets/promo.js?v=20260911a"></script>
|
||||||
<script src="/assets/my.js?v=20260912i"></script>
|
<script src="/assets/my.js?v=20260912i"></script>
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford to lose.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford to lose.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/partners.js?v=20260912a"></script>
|
<script src="/assets/partners.js?v=20260912a"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -192,7 +192,7 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/plays.js?v=20260910b"></script>
|
<script src="/assets/plays.js?v=20260910b"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
<p class="muted small">We use reasonable safeguards, but no system is perfectly secure. Protect your email and your wallet.</p>
|
<p class="muted small">We use reasonable safeguards, but no system is perfectly secure. Protect your email and your wallet.</p>
|
||||||
</div>
|
</div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/legal.js?v=20260908a"></script>
|
<script src="/assets/legal.js?v=20260908a"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@
|
|||||||
<p class="muted small" style="margin-top:18px">See also the <a href="/disclaimer">Disclaimer</a> and <a href="/privacy">Privacy Policy</a>.</p>
|
<p class="muted small" style="margin-top:18px">See also the <a href="/disclaimer">Disclaimer</a> and <a href="/privacy">Privacy Policy</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/legal.js?v=20260908a"></script>
|
<script src="/assets/legal.js?v=20260908a"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
<p><a href="/ledger">← Back to the live ledger</a> · <a href="/contract">Read the contract review</a></p>
|
<p><a href="/ledger">← Back to the live ledger</a> · <a href="/contract">Read the contract review</a></p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/tx.js?v=20260906m"></script>
|
<script src="/assets/tx.js?v=20260906m"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/wall.js?v=20260911v"></script>
|
<script src="/assets/wall.js?v=20260911v"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@
|
|||||||
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never share your recovery phrase.</div>
|
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never share your recovery phrase.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="/assets/common.js?v=20260912e"></script>
|
<script src="/assets/common.js?v=20260913a"></script>
|
||||||
<script src="/assets/wallets.js?v=20260911b"></script>
|
<script src="/assets/wallets.js?v=20260911b"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -148,7 +148,10 @@ function codeGuard(req, b) {
|
|||||||
// honeypot: bots fill it, humans never see it. Some form-filler extensions fill every field,
|
// honeypot: bots fill it, humans never see it. Some form-filler extensions fill every field,
|
||||||
// hidden or not (seen 2026-09-11), so a filled honeypot is a CHALLENGE, never a silent drop:
|
// hidden or not (seen 2026-09-11), so a filled honeypot is a CHALLENGE, never a silent drop:
|
||||||
// a person passes the icon check and gets the code, a bot cannot.
|
// a person passes the icon check and gets the code, a bot cannot.
|
||||||
const hp = !!b.hp_field_x9;
|
const hpRaw = String(b.hp_field_x9 || '').trim();
|
||||||
|
const hpIsEmail = !!hpRaw && hpRaw.toLowerCase() === String(b.email || '').trim().toLowerCase();
|
||||||
|
const hp = !!hpRaw && !hpIsEmail; // 2026-09-13: phones were autofilling the hidden field; own email = autofill, not a bot
|
||||||
|
if (hpRaw) console.log('signup-guard honeypot-value', clientIp(req), hpIsEmail ? 'own-email' : (/^https?:/i.test(hpRaw) ? 'url' : /@/.test(hpRaw) ? 'other-email' : /^\+?[\d\s()-]{6,}$/.test(hpRaw) ? 'phone' : 'text'), 'len=' + hpRaw.length, hpIsEmail ? '' : hpRaw.slice(0, 2) + '***');
|
||||||
const fts = Number(b.fts) || 0;
|
const fts = Number(b.fts) || 0;
|
||||||
if (!fts || now - fts < CODE_LIMITS.minFormMs) { guardLog(req, 'form-age', b); return { status: 400, body: { error: 'Give the page a second, then tap again.' } }; }
|
if (!fts || now - fts < CODE_LIMITS.minFormMs) { guardLog(req, 'form-age', b); return { status: 400, body: { error: 'Give the page a second, then tap again.' } }; }
|
||||||
if (now - fts > 12 * 3600 * 1000) { guardLog(req, 'form-stale', b); return { status: 400, body: { error: 'This page has been open a long time. Refresh it, then tap again.' } }; }
|
if (now - fts > 12 * 3600 * 1000) { guardLog(req, 'form-stale', b); return { status: 400, body: { error: 'This page has been open a long time. Refresh it, then tap again.' } }; }
|
||||||
|
|||||||
Reference in New Issue
Block a user