openCACAO / cocoa-issues

接触確認アプリ COCOA に関するIssues用レポジトリです
Creative Commons Zero v1.0 Universal
8 stars 0 forks source link

userUuid を利用している場所のコード解析 #5

Open moonmile opened 4 years ago

moonmile commented 4 years ago

元ネタで6月頃に userUuid と secret の件で揉めていました。この userUuid は、結局のところ ver.1.1.2 でも使われていると思うので、念のため userUuid が何処で使われているのかを明確にしておきます。 話が少しややこしいのですが、要するに

という問題です。ちなみに、陽性の場合 HER-SYS から割り振られる番号を「陽性登録」してサーバーに送るので、個人が特定される のは確かなのですが、問題はそれ以前にサーバー側から個人を特定できるだろうか?という点です。

userUuid の取得

userUuid は、スマホでアプリをインストールしたときの「利用規約」のページ(TutorialPage3View)で、サーバーから割り振られます。

https://github.com/openCACAO/Covid19Radar/blob/2d860dae1abbcdbfe4ec48a8bbaef1e6053547b7/Covid19Radar/Covid19Radar/ViewModels/Tutorial/TutorialPage3ViewModel.cs#L27-L45

        public Command OnClickAgree => new Command(async () =>
        {

            UserDialogs.Instance.ShowLoading(Resources.AppResources.LoadingTextRegistering);
            if (!userDataService.IsExistUserData)
            {
                userData = await userDataService.RegisterUserAsync(); // ★ここを呼び出す
                if (userData == null)
                {
                    UserDialogs.Instance.HideLoading();
                    await UserDialogs.Instance.AlertAsync(Resources.AppResources.DialogNetworkConnectionError, Resources.AppResources.DialogNetworkConnectionErrorTitle, Resources.AppResources.ButtonOk);
                    return;
                }
            }
            userData.IsOptined = true;
            await userDataService.SetAsync(userData);
            UserDialogs.Instance.HideLoading();
            await NavigationService.NavigateAsync(nameof(PrivacyPolicyPage));
        });

これが、UserDataService::RegisterUserAsync を呼び出し、

https://github.com/openCACAO/Covid19Radar/blob/2d860dae1abbcdbfe4ec48a8bbaef1e6053547b7/Covid19Radar/Covid19Radar/Services/UserDataService.cs#L28-L43

        public async Task<UserDataModel> RegisterUserAsync()
        {
            var userData = await httpDataService.PostRegisterUserAsync(); // ★ここを呼び出す
            if (userData == null)
            {
                return null;
            }
            userData.StartDateTime = DateTime.UtcNow;
            userData.IsExposureNotificationEnabled = false;
            userData.IsNotificationEnabled = false;
            userData.IsOptined = false;
            userData.IsPolicyAccepted = false;
            userData.IsPositived = false;
            await SetAsync(userData);
            return userData;
        }

HttpDataService::PostRegisterUserAsync に辿り着き、サーバー呼び出しをして、userData.UserUuid = registerResult.UserUuid として保存します。

https://github.com/openCACAO/Covid19Radar/blob/2d860dae1abbcdbfe4ec48a8bbaef1e6053547b7/Covid19Radar/Covid19Radar/Services/HttpDataService.cs#L43-L67

        public async Task<UserDataModel> PostRegisterUserAsync()
        {
            try
            {
                string url = AppSettings.Instance.ApiUrlBase + "/register";
                var content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
                var result = await PostAsync(url, content);
                if (result != null)
                {
                    var registerResult = Utils.DeserializeFromJson<RegisterResultModel>(result);

                    UserDataModel userData = new UserDataModel();
                    userData.Secret = registerResult.Secret;
                    userData.UserUuid = registerResult.UserUuid; // ★ここで保存
                    userData.JumpConsistentSeed = registerResult.JumpConsistentSeed;
                    userData.IsOptined = true;
                    await SecureStorage.SetAsync(AppConstants.StorageKey.Secret, registerResult.Secret);
                    SetSecret();
                    return userData;
                }
            }
            catch (HttpRequestException) { }

            return null;
        }

スマホユーザーを特定できる userUuid は、UserDataModel::UserUuid に保存される訳ですが、これを使っているところは、陽性登録を行うときの UploadSelfExposureKeysToServerAsync だけです。

陽性登録で userUuid を送信

ユーザーが陽性登録を行おうとしたとき ExposureNotificationHandler::UploadSelfExposureKeysToServerAsync が EN api からコールバックで呼び出されます。

https://github.com/openCACAO/Covid19Radar/blob/2d860dae1abbcdbfe4ec48a8bbaef1e6053547b7/Covid19Radar/Covid19Radar/Services/ExposureNotificationHandler.cs#L223-L235

        public async Task UploadSelfExposureKeysToServerAsync(IEnumerable<TemporaryExposureKey> temporaryExposureKeys)
        {
            var latestDiagnosis = userData.LatestDiagnosis;

            if (latestDiagnosis == null || string.IsNullOrEmpty(latestDiagnosis.DiagnosisUid))
            {
                throw new InvalidOperationException();
            }

            var selfDiag = await CreateSubmissionAsync(temporaryExposureKeys, latestDiagnosis);  // ★CreateSubmissionAsync が返す DiagnosisSubmissionParameter に userUuid が入っている

            HttpStatusCode httpStatusCode = await httpDataService.PutSelfExposureKeysAsync(selfDiag); // ★ここでアップロードする
            if (httpStatusCode == HttpStatusCode.NotAcceptable)

userUuid 入りの selfDiag を PutSelfExposureKeysAsync を使いサーバーに送ります。

https://github.com/openCACAO/Covid19Radar/blob/2d860dae1abbcdbfe4ec48a8bbaef1e6053547b7/Covid19Radar/Covid19Radar/Services/HttpDataService.cs#L69-L75

        public async Task<HttpStatusCode> PutSelfExposureKeysAsync(DiagnosisSubmissionParameter request)
        {
            var url = $"{AppSettings.Instance.ApiUrlBase.TrimEnd('/')}/diagnosis";
            var content = new StringContent(Utils.SerializeToJson(request), Encoding.UTF8, "application/json");
            HttpStatusCode status = await PutAsync(url, content);
            return status;
        }

結論としては、陽性者登録以外では userUuid を送っていない

ひとまず、現在の ver.1.1.2 でもこのコードのままだとすると、陽性者登録のときにしか userUuid を送りません。

利用規約
+ TutorialPage3ViewModel::OnClickAgree
  + UserDataService::RegisterUserAsync
    + HttpDataService::PostRegisterUserAsync

陽性者登録
+ ExposureNotificationHandler
  + UploadSelfExposureKeysToServerAsync
    + HttpDataService::PutSelfExposureKeysAsync

例えば、サーバーから TEK をダウンロードするときには GET メソッドなので、クライアントから userUuid を送りません。更に言えば、ダウンロード用の HttpClient とアップロード用の HttpClient が異なる作りとなっているため通常の状態では secret も送っていません。

所感

ただし、私見を言えば、個人が特定できそうな誤解の多い userUuid のコードは消しておくのがベターだろうと思います。先に書いた通り、陽性者登録するときは HER-SYS のコードと一緒に TEK が送られるため、サーバーから個人を特定することは非常に容易です。現状では陽性者の TEK をサーバーに送るのは 陽性者自身が陽性登録をした ときに限られるため、サーバーから陽性者の TEK を消すユースケースは、陽性登録をした後に自分の TEK を消したい、というケースに限られます。これは非常に稀な現象であるし、陽性者自身が自分の TEK を登録後に消す作業の考慮は、userUuid とは違うパターンで実装したほうがよいと思われます。

kvaluation commented 4 years ago

オプトアウトのためだけにユーザー識別情報を得ておくのではなく、ユーザー識別情報自体を得ないのが最適と思われます。

個人的に、私は、2020年8月19日以降 export.bin ({3桁数字}.bin) を毎晩みるようにしています。

処理番号による陽性登録が、例えば8/20にあると、まとめて、8/21 0時0分(24時間、日本時間)に通知サーバーに格納されます。 すると、自動通知のユーザー宛には8/21 1時から3時などに通知、自動照合されています。アプリ起動によるダウンロードの方は、深夜0時をすぎればダウンロード・照合できます。

そして、このアプリに期待される役割としては、接触日からできるだけ早い通知・照合が望まれます。

仕様・運用面の問題かも知れませんが、陽性登録後に自分のTEKを消したいという機能を実装するために、照合・通知が送れてしまうのでは、デメリットの方が大きいと考えます。

TEKを消したくなる可能性があれば、そもそも陽性登録しないことをユーザーに表示していく方向性が一案と思われます。