T09 · Insecure Skill Coding Practices
Warning
- Location
- assets/beijing-jifen-wizard.html:1518
- Finding
- Automatic Plaintext Persistence of Sensitive Personal Data in Browser Storage<![CDATA[ ## Vulnerability Details **File Location**: `assets/beijing-jifen-wizard.html`, lines 1518-1553 **Vulnerability Type**: Persistent plaintext storage of sensitive personal information **Risk Level**: Medium ### Vulnerable Code ```javascript function collectFormState(){ var data = { version: 4, form: {}, extras: {}, socialRows: [], resRows: [], birth: "", degreeDeduct: "" }; data.plan = JSON.parse(JSON.stringify(planState)); data.radios = collectRadioState(); try { var form = document.querySelector("#wizardForm"); new FormData(form).forEach(function(v, k){ data.form[k] = String(v); }); } catch (e) {} ["#areaStart1", "#areaStart2", "#taxStart1"].forEach(function(sel){ var el = $(sel); if (el) data.extras[sel] = ymValueFrom(el); }); ["#innovate1", "#innovate2", "#innovate3"].forEach(function(sel){ var el = $(sel); if (el) data.extras[sel] = el.value; }); $$("#socialSegs .seg-row").forEach(function(row){ var p = row.querySelectorAll(".ym-pair"); data.socialRows.push({ start: ymReadSafe(p[0]), end: ymReadSafe(p[1]) }); }); $$("#resSegs .seg-row").forEach(function(row){ var p = row.querySelectorAll(".ym-pair"); var t = row.querySelector(".seg-res-type"); data.resRows.push({ start: ymReadSafe(p[0]), end: ymReadSafe(p[1]), type: t ? t.value : "1" }); }); var birth = $("#birthYM .ym-pair"); data.birth = ymReadSafe(birth); var dd = $("#degreeDeductSeg"), dp = dd ? dd.querySelectorAll(".ym-pair") : []; if (dp.length >= 2) data.degreeDeduct = ymReadSafe(dp[0]) + "|" + ymReadSafe(dp[1]); return data; } function saveForm(){ if (restoringForm) return; try { localStorage.setItem(FORM_CACHE_KEY, JSON.stringify(collectFormState())); } catch (e) {} } ``` ### Technical Analysis The application automatically serializes nearly the entire calculator form and stores it as plaintext JSON in `localStorage`. The retained information can include birth month, employment and social-insurance periods, r ...[truncated 2395 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Disable persistent storage by default and require an explicit opt-in before saving form data. 2. Clearly identify every category of data that will be retained, the purpose of retention, and how long it will remain. 3. Minimize retained data. In particular, avoid persisting birth information, tax status, criminal-record eligibility, administrative penalties, and other sensitive fields unless strictly necessary. 4. Provide a session-only mode using in-memory state or `sessionStorage`, and make it the default. 5. Add an expiration timestamp and automatically delete stale records after a short, documented retention period. 6. Automatically clear sensitive state when the user completes the workflow, unless the user explicitly chooses to retain it. 7. Add a visible persistent-storage status indicator and an accessible control to disable future saving. 8. Consider field-level redaction or selective persistence so that only low-sensitivity UI preferences are retained. 9. Do not rely solely on client-side encryption when the encryption key is stored in the same browser context, as that does not protect against same-origin script access. 10. Retain the existing manual clearing function, but supplement it with automatic expiry and opt-in consent rather than treating manual deletion as the only lifecycle control. ]]>
