T09 · Insecure Skill Coding Practices
- Location
scripts/portal-bridge.js:20- Finding
One-Time Authentication Token Exposed in URL Query String
- Content
View full analysis
&type=magiclink ``` ```js const u = new URL('https://site/portal/'); u.searchParams.set('token_hash', gen.hashed_token); u.searchParams.set('type', gen.verification_type || 'magiclink'); const magicLink = u.toString(); ``` The portal reads the token from the query string and does not remove it until the asynchronous verification request has settled: ```js var q = new URLSearchParams(window.location.search); // (3) error first — short-circuit and don't try to verify anything. var err = q.get('error_description') || q.get('error') || q.get('error_code'); if (err) { setTimeout(function () { alert('הקישור פג או כבר נוצל. בקש מהסוכן לשלוח קישור חדש.\n\n(' + decodeURIComponent(err) + ')'); }, 300); history.replaceState(null, '', window.location.pathname); return; } // (1) token_hash flow — explicit verifyOtp. var tokenHash = q.get('token_hash'); var type = q.get('type'); if (tokenHash && type) { sb.auth.verifyOtp({ token_hash: tokenHash, type: type }) .then(function (res) { if (res.error) { console.warn('verifyOtp failed:', res.error.message); setTimeout(function () { alert('הקישור פג או כבר נוצל. בקש מהסוכן לשלוח קישור חדש.'); }, 300); } }) .finally(function () { // Strip the one-time token from the URL so a refresh doesn't replay. history.replaceState(null, '', window.location.pathname); }); } ``` ### Technical Analysis Although named `token_hash`, this value is a bearer-like one-time credential accepted by Supabase's `verifyOtp` operation. Possession ...[truncated 2951 chars]- Remediation
View remediation
