承上文,這部份看看下載及通知與感染者接觸的功能,以及相關的問題。
下載組態及資料更新
下載設定及權限
如第一部份所提及,程式使用了 react-native-background-fetch
做背景下載。值得一提的是,其下載設定為
{ minimumFetchInterval: 15, requiredNetworkType: l.default.NETWORK_TYPE_ANY, stopOnTerminate: false, startOnBoot: true, enableHeadless: true }
NETWORK_TYPE_ANY
是指,程式不論是 WiFi 或是流動網絡,只要有連線,均會下載資料作更新。與此相關的是,手動上載的士車牌影像供 OCR 前,亦沒有特別檢查 WiFi 狀態(因為並不預期在的士內可以穩定利用 WiFi [1])。
因此,資科辦解釋「安心出行」權限時 [2] 所指「流動應用程式需要網絡存取、Wi-Fi連線(以節省用戶數據使用)、媒體及檔案相關權限,以保障用戶私隱」,「應用程式需要在背景執行,並使用手機的上網功能,將有關資料定時下載… 。因此,流動應用程式需要網絡存取、Wi-Fi連線、容許程式可以在背景執行及發出通知的相關權限」,具有誤導性:
- 程式根本沒有特別設定,限制只使用 WiFi 上下載,以節省流動數據,
- 上述「Wi-Fi連線相關權限 」在前文後理下,可理解為程式要求的
ACCESS_WIFI_STATE
(查看Wi-Fi連線)權限及CHANGE_WIFI_MULTICAST_STATE
(允許接收Wi-Fi多點傳播封包)。事實是,若單純使用 WiFi 連線,INTERNET 網絡存取權限已足夠。查看 WiFi 連線權限,是用於取得 WiFi 連接有關的額外資訊,在 Android 6 之前更可取得附近 WiFi 網絡(Access Point)識別資料從而推斷用戶位置。從程式功能看,似乎沒有此權限的必要。[3]
觀乎實際程式碼,似乎是開發商使用了一些涵蓋廣泛功能的程式庫,因而附帶要求了相關 WiFi 權限,與當局所指需要 WiFi 連線或節省用戶數據無關。
組態檔
在下載時,如第一部份所述,程式會先下載一個載有目標檔案資料的目錄,附更新時間(updatedAt
),以讓程式自行決定要下載什麼資料(getBatchFile
),內容例子:
[ { "id": 62, "batchSize": 67, "startTs": null, "endTs": null, "filename": "20201127-1606476888700.zip", "updatedAt": 1606476889000 }, ... { "id": 51, "batchSize": 0, "startTs": null, "endTs": null, "filename": "20201116-1605499040680.zip", "updatedAt": 1605470241000 } ]
程式接着再下載組態檔(getConfig
)。組態檔的內容如下:
{ "config": [ { "id": 2, "type": "DEFAULT", "overlapDuration": 60000, "groupOnly": false, "indirectWithin": 0, "indirectDuration": 0, "matchingKeyDays": 14, "dataRetentionDays": 31, "checkOutReminderMs": 3600000, "forceUpdateVersion": "0.0.0", "appStoreIos": "https://apps.apple.com/us/app/leavehomesafe/id1536377801", "playStoreAos": "https://play.google.com/store/apps/details?id=hk.gov.ogcio.leavehomesafe", "appGalleryHms": "https://appgallery.huawei.com/#/app/C103081261", "enableCI": false, "enableFollowUp": false }, { "id": 3, "type": "TAXI", "overlapDuration": 60000, "groupOnly": false, "indirectWithin": 86400000, "indirectDuration": 1000, "matchingKeyDays": 14, "dataRetentionDays": 31, "checkOutReminderMs": 3600000, "forceUpdateVersion": "0.0.0", "appStoreIos": "https://apps.apple.com/us/app/leavehomesafe/id1536377801", "playStoreAos": "https://play.google.com/store/apps/details?id=hk.gov.ogcio.leavehomesafe", "appGalleryHms": "https://appgallery.huawei.com/#/app/C103081261", "enableCI": false, "enableFollowUp": false } ] }
程式內的用戶紀錄及下載感染者紀錄都是按 type
分開處理,上述組態檔中,type = DEFAULT
是指預設不同類型場所紀錄的處理,而 type = TAXI
是指的士車牌的紀錄。
overlapDuration
:60000 (一分鐘),與感染者在同一場所超過一分鐘,會視為直接接觸。groupOnly
:未啟用,若啟用時會收窄接觸的定義,除處於同一場所外,要與感染者紀錄的 group ID 相同,才視為有接觸(目前 group ID 全部設為NO_GROUP
,即 “00000000”)。indirectWithin
:86400000 (一日,僅用於的士)及indirectDuration
:1000(一秒,僅用於的士),若用戶和感染者使用同一的士,即使沒有直接接觸,只須在感染者離開的士後的 24 小時內進入的士,逗留超過一秒,便視為間接接觸。matchingKeyDays
:14(兩星期)程式下載資料後,只會核對自該日零時對上兩星期起至今的紀錄。dataRetentionDays
:31(31日)下載資料後會觸發清除用戶超過31 日的到訪的紀錄。(程式統一使用type = DEFAULT
的設定)checkOutReminderMs
:3600000(一小時)Check in 後一小時會提示用戶 Check out。forceUpdateVersion
: “0.0.0”,若有新版本,或會強制用戶更新,暫時(11月底)未啟用。enableCI
/enableFollowUp
:未啟用,程式內沒有相應編碼。
注意這個組態檔是不會儲存的,每次使用時會重新下載,即是說內容隨時可由當局改變,包括當局掛在口邊,31 日清除到訪的紀錄的設定。此外,
forceUpdateVersion
若啟用,用戶要使用程式或須下載新版本,即使現行版本經檢視未有發現問題,新版本仍是沒有保證。特別是 group 配對 /enableFollowUp
/enableCI
[4] 等功能,目前未有資料,但觀乎名稱,可能日後會如當局所指的「按部就班」,逐步加強追蹤及跟進的功能。
感染者到訪資料檔
根據前述 getBatchFile
所載的目錄資料,程式會下載自安裝或上次更新後,所更新的感染者到訪資料(downloadKeys
)。資料是經 base64 編碼及 zip 壓縮的 Protobuf 檔案格式 [5]。
資料解壓及合併後,成為按 type
(如 TAXI
)分類的感染者到訪資料列表,內容主要是如第二部份所述,經「加密」的 keyData
(地點及時間資料)及可用於解密的 keyInterval
。
而實際下載的有關資料內容,就是政府公布的確診者到訪大廈資料,以下為例子:
inTs: 2020-11-24 00:00:00 outTs: 2020-11-24 23:59:59.999000 metadata: {"name_zh_hk":"置地廣塲","type":"IMPORT","name_zh_cn":"置地廣塲","name_en":"The Landmark"} inTs: 2020-11-24 00:00:00 outTs: 2020-11-24 23:59:59.999000 metadata: {"name_zh_hk":"公爵大廈","type":"NONGOVBUILDINGOFFICE","name_zh_cn":"公爵大廈","name_en":"Edinburgh Tower"} inTs: 2020-11-24 00:00:00 outTs: 2020-11-24 23:59:59.999000 metadata: {"name_zh_hk":"告羅士打大廈","type":"NONGOVBUILDINGOFFICE","name_zh_cn":"告羅士打大廈","name_en":"Gloucester Tower"} inTs: 2020-11-24 00:00:00 outTs: 2020-11-24 23:59:59.999000 metadata:{"name_zh_hk":"約克大廈","type":"NONGOVBUILDINGOFFICE","name_zh_cn":"約克大廈","name_en":"York House"}
注意可能由於資料不是來自程式,出入時間基本涵蓋全日,即只提供日期資料。
程式接下來﹐便是將下載資料按組態檔中的設定核對,以找出是否有與感染者「直接接觸」或「間接接觸」的個案(同一時段及地點同時有「直接接觸」及「間接接觸」,會視為直接接觸,不過在下載資料涵蓋全日看來,這個已沒有太大意義)。有關個案及配對資料會存到 Inbox 內,資料如下:
date
: 目前時間title
: 訊息主題,“Possible COVID-19 Notification”template
: 訊息,分為的士及其他一般場所兩種exposedDate
: 用戶 Check In 場所時間checkOutTs
: 用戶 Check Out 場所時間metaData
: 即上文所指的venue資料,如地方種類,名稱及的士車牌,以 JSON 存放diff
: 直接接觸:與感染者同處一場所合計時間,間接接觸:用戶處於場所(如使用的士)合計時間uuid
: 即時產生的隨機 UUIDconfirmedKeyData
: 下載感染者配對的keyData
contactType
: 間接接觸為 “I”,直接接觸為 “D”isRead
: 是否已讀
若有發現新的接觸個案,會向用戶發通知,更新首頁的通知總數資料。
順帶一提,下載資料 zip 檔案及解壓檔案會即時清除,核對後只會存放成功核對的相關資料到 Inbox。當中由於沒有存放感染者的 keyInterval
,在 Inbox 中的感染者配對的 keyData
只能用來當 hash 用,以確保不會重覆通知,但不能還原資料。
另一方面,程式內雖有清空 Inbox 功能的編碼(deleteInbox
),但似乎沒有使用,讀取後只會劃為已讀。一旦 Inbox 有接觸感染者的訊息,可能除了清除 App 所有資料或重裝外沒有方法消除。
問題是,Inbox 訊息總數是當眼地顯示在程式首頁,如無法清空,而政府場地/私人物業要強制訪客打開此程式,公開地在物業管理員監視下掃描 QR Code,變相是要公開出示這個可以解讀為個人的「感染風險指數」的數字(事實上,程式內稱此數字為 “Exposure Count”)。
可以想像一下,當人人要使用程式,很快便得悉這個數字的「功能」,當「風險高企」者被發現,除了附近人士彈開,同時亦難以排除在監視的物業管理員可能被要求對「風險高企」者有特別對待,如進一步檢查接觸紀錄,否則禁止進入。
這不已經是「健康碼」了嗎?
廣告:不用搶購 3310,Android 2.2 – 4.0.4 不兼容「安心出行」
但可安裝「小鴨幹線」攔截電話
清除舊紀錄
當下載,核對及發出通知完成後,在檢查上次清除紀錄日期不是今天後(每天最多清除一次),程式會清除今天零時對上 31 日(視乎組態檔的 dataRetentionDays
)於 LocationHistory
中的用戶到訪紀錄(clearDataOverRetentionDays
)。由於紀錄作了「加密」,程式要讀取 LocationHistory
所有紀錄,逐一解密及抽取 Check In 時間作比較,才能決定要清除的紀錄。
此外,如上面指出,Inbox 的與感染者接觸資料沒有清除,這技術上已違反了政府「出行記錄亦會在存放手機達31天後自動刪除」的說法,因為 Inbox 也包含了用戶到訪地點及時間詳情。
下文再看看希望用不到的感染者上載資料功能。
[1] 除非… 「司機唔該跟貼前面架巴士,我要用佢 WiFi 上網」
[2] 全文見新聞稿原文
[3] 即使假設程式要偵測是否正使用 WiFi 連線,以啟動下載,ACCESS_NETWORK_STATE (查看網絡連線)權限已經足夠,「小鴨幹線」便是例子。
[4] CI 相信不是軟件開發的 Continuous Integration,又不似統計方面的 Confidence Interval。
[5] 完全是架床疊屋,原本資料(keyData)已經是所謂加密的 data 加上 base 64 encode,加上 Protobuf ,壓縮空間不大,開發者竟想到用多一次 base 64 encode 發大再用 zip。