實戰(zhàn)教程(十二):安全地交換雙方微信號)
“AI 軟件開發(fā)實戰(zhàn)教程”系列第 12 篇讓候選中的一方主動確認后雙方同時獲得對方微信號同時保證確認前不泄露、重復(fù)點擊不重復(fù)、資料修改不改寫歷史。系統(tǒng)找到“可能同路”的人以后真正的溝通仍要回到微信。鄰行不做站內(nèi)聊天因此聯(lián)系方式交換是第一條核心流程中風險最高的一步。如果只把微信號顯示在候選詳情里會出現(xiàn)三個問題用戶沒有明確同意把自己的聯(lián)系方式給對方頁面權(quán)限的一處疏漏就可能批量泄露用戶修改微信號后過去已經(jīng)交換的結(jié)果會悄悄變化。K4 先把交換定義成一個獨立、不可覆蓋的業(yè)務(wù)事實?!半p向交換”不是“雙向?qū)徟碑a(chǎn)品規(guī)則是候選雙方中的任何一方都可以發(fā)起。確認頁必須明確告訴發(fā)起者確認后你會看到對方的微信號 你的微信號也會同時提供給對方。 這只是方便雙方回微信溝通 不代表已經(jīng)約定同行。對方不需要再點一次同意。因此頁面不能寫成“雙向確認”否則發(fā)起者會誤以為還要等待。一次操作同時披露是這個產(chǎn)品在小范圍社區(qū)場景中的明確選擇。它減少了雙方異步上線造成的等待但也要求確認文案、權(quán)限復(fù)核和審計事實都足夠清楚。紅燈先固定授權(quán)邊界K4 的第一組測試在導(dǎo)入階段失敗因為交換模型和服務(wù)還不存在。隨后測試逐個固定這些拒絕條件操作者不是候選雙方之一任一方賬戶被限制或不可用候選已經(jīng)失效任一出行信息已經(jīng)關(guān)閉、取消或過期已到雙方較早的預(yù)計出發(fā)時間雙方不再屬于同一個社區(qū)任一方尚未設(shè)置微信號。交換服務(wù)不相信用戶剛剛打開的候選頁面。點擊確認到請求抵達服務(wù)器之間狀態(tài)可能已經(jīng)改變所以服務(wù)會在短事務(wù)中重新鎖定和檢查候選及雙方信息。一個容易混淆的邊界是匹配截止。它只表示停止產(chǎn)生新候選。截止前已經(jīng)形成的候選即使現(xiàn)在已經(jīng)停止匹配只要雙方尚未到較早預(yù)計出發(fā)時間仍然可以交換微信號。專門的注入時鐘測試固定了這條規(guī)則。保存“當時披露的值”交換不能只保存兩個用戶 ID然后每次打開頁面去讀取最新個人資料。假設(shè)乘客交換時的微信號是wx_old后來改成wx_new。過去的車主已經(jīng)拿到wx_old系統(tǒng)卻顯示wx_new歷史事實就不再可信。因此ContactExchange保存候選 發(fā)起者 交換時間 車主當時微信號的加密快照 乘客當時微信號的加密快照 密鑰版本服務(wù)先用個人資料對應(yīng)版本的密鑰解密再用當前活動密鑰重新加密為交換快照。測試在交換后修改乘客資料舊結(jié)果仍必須返回交換時的值。這里保存快照不是為了無限保留。賬戶刪除任務(wù)仍要按產(chǎn)品規(guī)則擦除該用戶在在線資料和交換快照中的敏感值那屬于后續(xù) K8 的數(shù)據(jù)生命周期工作。一對一約束是冪等的最后防線同一個候選只能有一次聯(lián)系方式交換。數(shù)據(jù)模型使用候選的一對一關(guān)系事務(wù)中也先返回已經(jīng)存在的結(jié)果。測試讓車主先發(fā)起再由乘客重復(fù)發(fā)起最終只能得到1 個 ContactExchange 1 個 contact.exchanged 業(yè)務(wù)事件 雙方各 1 條站內(nèi)提醒 雙方各 1 條渠道投遞記錄無論雙擊按鈕、網(wǎng)絡(luò)重試還是雙方幾乎同時操作業(yè)務(wù)語義都不會變成交換兩次。SQLite 上的測試可以證明順序重試和數(shù)據(jù)庫唯一事實。真正的 PostgreSQL 雙連接并發(fā)仍應(yīng)在具備服務(wù)環(huán)境時補跑當前機器沒有 PostgreSQL不能把 SQLite 冒充并發(fā)證據(jù)。不要把秘密交給模板再隱藏一個常見但危險的做法是把車主和乘客的微信號都放入模板上下文然后用頁面條件決定顯示哪一個。隱藏的 DOM、調(diào)試輸出、錯誤頁或以后新增的前端序列化都可能把不該出現(xiàn)的值送到瀏覽器。鄰行的結(jié)果服務(wù)先判斷當前查看者身份查看者是車主 → 只解密乘客快照 查看者是乘客 → 只解密車主快照 其他人 → 拒絕模板上下文只有一個other_wechat_id。HTTP 測試分別斷言確認頁源碼不包含雙方任一微信號交換后頁面包含對方微信號頁面不包含自己的微信號頁面不包含登錄賬號非參與者打開確認和結(jié)果地址都得到 404。安全邊界應(yīng)當盡量發(fā)生在數(shù)據(jù)進入表現(xiàn)層之前而不是依賴 CSS 或前端腳本隱藏。復(fù)制按鈕必須允許失敗微信內(nèi)置瀏覽器、系統(tǒng)權(quán)限和非安全上下文都可能讓 Clipboard API 不可用。一鍵復(fù)制只能是增強能力不能成為唯一出口。結(jié)果頁把對方微信號放在只讀但可以聚焦、長按和選擇的輸入框中。復(fù)制成功顯示“已復(fù)制”失敗時自動選中文本并提示復(fù)制失敗請長按或手動選擇上面的微信號。頁面不會嘗試打開未知的微信協(xié)議也不會自動跳走。下一步文案只告訴用戶返回微信添加對方并確認具體上下車位置。自動瀏覽器可以證明結(jié)構(gòu)和降級路徑已經(jīng)存在但 iOS 和 Android 微信中的長按選擇、復(fù)制權(quán)限與返回操作仍必須由 Gate B 真機驗收不能由桌面 Chromium 最終代替。雙用戶瀏覽器閉環(huán)K3 已有兩個隔離瀏覽器上下文。K4 在同一旅程后繼續(xù)執(zhí)行乘客打開候選 → 點擊“想和對方聯(lián)系” → 確認頁看不到任何微信號 → 閱讀互相披露說明并確認 → 乘客只看到車主微信號 → 車主刷新候選并打開結(jié)果 → 車主只看到乘客微信號 → 雙方都看到返回微信的下一步兩個上下文有獨立 Cookie 和會話。測試不是把同一個客戶端來回切換用戶因此更接近雙方異步操作的真實授權(quán)路徑。本節(jié)點結(jié)果K4 收口時85 個非瀏覽器測試通過分支覆蓋率 90.79%Ruff、格式、mypy strict、遷移和 Django 檢查通過4 條 Chromium 旅程通過其中雙用戶旅程已走到互相披露結(jié)果重復(fù)交換、資料修改、非參與者、受限賬戶、失效狀態(tài)和時間邊界都有自動證據(jù)確認前不含微信號結(jié)果頁每人只收到對方微信號外部提醒實際發(fā)送、PostgreSQL 并發(fā)、WebKit 和微信真機仍未宣稱通過結(jié)果只形成本地提交不推送。驗收矩陣中AC-21 至 AC-23 可以在當前頁面范圍內(nèi)自動通過AC-41 也完成交換前后身份展示閉環(huán)。AC-24 仍是部分通過交換事實與雙方獨立投遞記錄已經(jīng)形成第三方一方失敗不影響另一方要等 K7 故障發(fā)送測試。寫在最后敏感數(shù)據(jù)功能的關(guān)鍵不是“加密了”三個字而是明確誰在什么時刻授權(quán)、事務(wù)中重新檢查什么、保存哪個時間點的值以及秘密最早在哪一層被裁剪。當結(jié)果模板從一開始就只拿到對方微信號后續(xù)頁面改版也更難意外泄露另一份數(shù)據(jù)。好的隱私設(shè)計往往同時讓代碼職責更清晰。下一篇進入 K5雙方回微信溝通后怎樣分別反饋結(jié)果為什么單方確認不能占座以及如何用數(shù)據(jù)庫鎖保證最后一個座位不會同時分給兩位乘客。關(guān)鍵代碼與操作下面的簡化測試從雙方視角讀取同一次交換證明每個人只能得到對方的微信號deftest_one_actor_discloses_both_contacts(candidate,driver,passenger):exchangeexchange_contact(candidate_idcandidate.pk,actor_iddriver.pk,nowtimezone.now(),)assertget_disclosed_contact(exchangeexchange,viewerdriver)passenger-wechatassertget_disclosed_contact(exchangeexchange,viewerpassenger)driver-wechatassertBusinessEvent.objects.filter(event_typecontact.exchanged).count()1驗證命令make bugfix TESTtests/matching/test_contact_exchange.py::test_one_actor_discloses_both_contacts_and_creates_one_event示例只表達權(quán)限方向真實測試還會覆蓋重復(fù)點擊、資料修改、外部成員和過期候選。本篇驗證摘要雙方必須在同一個有效候選中明確同意互相披露系統(tǒng)才創(chuàng)建交換交換保存當時雙方微信號的加密快照之后修改資料不會悄悄改寫歷史結(jié)果唯一約束和冪等服務(wù)保證重復(fù)點擊不會創(chuàng)建第二次交換查詢服務(wù)只向每位參與者返回對方的聯(lián)系方式頁面拿不到自己的解密值移動瀏覽器已驗證雙方確認、查看和復(fù)制流程提醒碼始終不會進入頁面。附錄相關(guān)工具與倉庫gstack倉庫garrytan/gstack地址https://github.com/garrytan/gstackdev-harness倉庫Dev-Wiki/dev-harness地址https://github.com/Dev-Wiki/dev-harnessUI UX Pro Max Skill倉庫nextlevelbuilder/ui-ux-pro-max-skill地址https://github.com/nextlevelbuilder/ui-ux-pro-max-skill