Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java

Issue 2802603002: getInstalledRelatedApps: Introduce random delay to stop timing attacks. (Closed)
Patch Set: Use Handler.postDelay instead of Thread.sleep, to avoid choking any thread. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser.installedapp; 5 package org.chromium.content.browser.installedapp;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.content.pm.ApplicationInfo; 8 import android.content.pm.ApplicationInfo;
9 import android.content.pm.PackageManager; 9 import android.content.pm.PackageManager;
10 import android.content.pm.PackageManager.NameNotFoundException; 10 import android.content.pm.PackageManager.NameNotFoundException;
11 import android.content.res.Resources; 11 import android.content.res.Resources;
12 import android.os.AsyncTask; 12 import android.os.AsyncTask;
13 import android.os.Handler;
13 14
14 import org.json.JSONArray; 15 import org.json.JSONArray;
15 import org.json.JSONException; 16 import org.json.JSONException;
16 import org.json.JSONObject; 17 import org.json.JSONObject;
17 18
18 import org.chromium.base.Log; 19 import org.chromium.base.Log;
19 import org.chromium.base.VisibleForTesting; 20 import org.chromium.base.VisibleForTesting;
20 import org.chromium.installedapp.mojom.InstalledAppProvider; 21 import org.chromium.installedapp.mojom.InstalledAppProvider;
21 import org.chromium.installedapp.mojom.RelatedApplication; 22 import org.chromium.installedapp.mojom.RelatedApplication;
22 import org.chromium.mojo.system.MojoException; 23 import org.chromium.mojo.system.MojoException;
23 24
24 import java.net.URI; 25 import java.net.URI;
25 import java.net.URISyntaxException; 26 import java.net.URISyntaxException;
26 import java.util.ArrayList; 27 import java.util.ArrayList;
28 import java.util.concurrent.atomic.AtomicInteger;
27 29
28 /** 30 /**
29 * Android implementation of the InstalledAppProvider service defined in 31 * Android implementation of the InstalledAppProvider service defined in
30 * installed_app_provider.mojom 32 * installed_app_provider.mojom
31 */ 33 */
32 public class InstalledAppProviderImpl implements InstalledAppProvider { 34 public class InstalledAppProviderImpl implements InstalledAppProvider {
33 @VisibleForTesting 35 @VisibleForTesting
34 public static final String ASSET_STATEMENTS_KEY = "asset_statements"; 36 public static final String ASSET_STATEMENTS_KEY = "asset_statements";
35 private static final String ASSET_STATEMENT_FIELD_TARGET = "target"; 37 private static final String ASSET_STATEMENT_FIELD_TARGET = "target";
36 private static final String ASSET_STATEMENT_FIELD_NAMESPACE = "namespace"; 38 private static final String ASSET_STATEMENT_FIELD_NAMESPACE = "namespace";
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 71
70 @Override 72 @Override
71 public void filterInstalledApps( 73 public void filterInstalledApps(
72 final RelatedApplication[] relatedApps, final FilterInstalledAppsRes ponse callback) { 74 final RelatedApplication[] relatedApps, final FilterInstalledAppsRes ponse callback) {
73 if (mFrameUrlDelegate.isIncognito()) { 75 if (mFrameUrlDelegate.isIncognito()) {
74 callback.call(new RelatedApplication[0]); 76 callback.call(new RelatedApplication[0]);
75 return; 77 return;
76 } 78 }
77 79
78 final URI frameUrl = mFrameUrlDelegate.getUrl(); 80 final URI frameUrl = mFrameUrlDelegate.getUrl();
81 final AtomicInteger delayMillis = new AtomicInteger();
79 82
80 // Use an AsyncTask to execute the installed/related checks on a backgro und thread (so as 83 // Use an AsyncTask to execute the installed/related checks on a backgro und thread (so as
81 // not to block the UI thread). 84 // not to block the UI thread).
82 new AsyncTask<Void, Void, RelatedApplication[]>() { 85 new AsyncTask<Void, Void, RelatedApplication[]>() {
Ted C 2017/04/12 15:53:50 I "think" it would be slightly cleaner to change t
Matt Giuca 2017/04/13 00:29:55 Done. Thanks, a good suggestion.
83 @Override 86 @Override
84 protected RelatedApplication[] doInBackground(Void... unused) { 87 protected RelatedApplication[] doInBackground(Void... unused) {
85 return filterInstalledAppsOnBackgroundThread(relatedApps, frameU rl); 88 return filterInstalledAppsOnBackgroundThread(relatedApps, frameU rl, delayMillis);
86 } 89 }
87 90
88 @Override 91 @Override
89 protected void onPostExecute(RelatedApplication[] installedApps) { 92 protected void onPostExecute(final RelatedApplication[] installedApp s) {
90 callback.call(installedApps); 93 // Before calling the callback, delay for the amount of time tha t has been
94 // calculated in |delayMillis|.
95 delayThenRun(new Runnable() {
96 @Override
97 public void run() {
98 callback.call(installedApps);
99 }
100 }, delayMillis.get());
91 } 101 }
92 } 102 }
93 .execute(); 103 .execute();
94 } 104 }
95 105
96 @Override 106 @Override
97 public void close() {} 107 public void close() {}
98 108
99 @Override 109 @Override
100 public void onConnectionError(MojoException e) {} 110 public void onConnectionError(MojoException e) {}
101 111
102 /** 112 /**
103 * Filters a list of apps, returning those that are both installed and match the origin. 113 * Filters a list of apps, returning those that are both installed and match the origin.
104 * 114 *
105 * This method is expected to be called on a background thread (not the main UI thread). 115 * This method is expected to be called on a background thread (not the main UI thread).
106 * 116 *
107 * @param relatedApps A list of applications to be filtered. 117 * @param relatedApps A list of applications to be filtered.
108 * @param frameUrl The URL of the frame this operation was called from. 118 * @param frameUrl The URL of the frame this operation was called from.
119 * @param delayMillis (Output) Number that will be incremented by the total amount of time in ms
120 * that should be delayed before returning to the user, t o mask the installed
121 * state of the requested apps.
109 * @return A subsequence of applications that meet the criteria. 122 * @return A subsequence of applications that meet the criteria.
110 */ 123 */
111 private RelatedApplication[] filterInstalledAppsOnBackgroundThread( 124 private RelatedApplication[] filterInstalledAppsOnBackgroundThread(
112 RelatedApplication[] relatedApps, URI frameUrl) { 125 RelatedApplication[] relatedApps, URI frameUrl, AtomicInteger delayM illis) {
113 ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedAppli cation>(); 126 ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedAppli cation>();
114 PackageManager pm = mContext.getPackageManager(); 127 PackageManager pm = mContext.getPackageManager();
115 for (RelatedApplication app : relatedApps) { 128 for (RelatedApplication app : relatedApps) {
116 // If the package is of type "play", it is installed, and the origin is associated with 129 // If the package is of type "play", it is installed, and the origin is associated with
117 // package, add the package to the list of valid packages. 130 // package, add the package to the list of valid packages.
118 // NOTE: For security, it must not be possible to distinguish (from the response) 131 // NOTE: For security, it must not be possible to distinguish (from the response)
119 // between the app not being installed and the origin not being asso ciated with the app 132 // between the app not being installed and the origin not being asso ciated with the app
120 // (otherwise, arbitrary websites would be able to test whether un-a ssociated apps are 133 // (otherwise, arbitrary websites would be able to test whether un-a ssociated apps are
121 // installed on the user's device). 134 // installed on the user's device).
122 if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != n ull 135 if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != n ull) {
123 && isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, p m)) { 136 delayMillis.addAndGet(calculateDelayForPackageMs(app.id));
124 installedApps.add(app); 137 if (isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, pm)) {
138 installedApps.add(app);
139 }
125 } 140 }
126 } 141 }
127 142
128 RelatedApplication[] installedAppsArray = new RelatedApplication[install edApps.size()]; 143 RelatedApplication[] installedAppsArray = new RelatedApplication[install edApps.size()];
129 installedApps.toArray(installedAppsArray); 144 installedApps.toArray(installedAppsArray);
130 return installedAppsArray; 145 return installedAppsArray;
131 } 146 }
132 147
133 /** 148 /**
149 * Determines how long to artifically delay for, for a particular package na me.
150 */
151 private int calculateDelayForPackageMs(String packageName) {
152 // Important timing-attack prevention measure: delay by a pseudo-random amount of time, to
153 // add significant noise to the time taken to check whether this app is installed and
154 // related. Otherwise, it would be possible to tell whether a non-relate d app is installed,
155 // based on the time this operation takes.
156 //
157 // Generate a 16-bit hash based on a unique device ID + the package name .
158 short hash = PackageHash.hashForPackage(packageName);
159
160 // The time delay is the low 10 bits of the hash in 100ths of a ms (betw een 0 and 10ms).
161 int delayHundredthsOfMs = hash & 0x3ff;
162 return delayHundredthsOfMs / 100;
163 }
164
165 /**
134 * Determines whether a particular app is installed and matches the origin. 166 * Determines whether a particular app is installed and matches the origin.
135 * 167 *
136 * @param packageName Name of the Android package to check if installed. Ret urns false if the 168 * @param packageName Name of the Android package to check if installed. Ret urns false if the
137 * app is not installed. 169 * app is not installed.
138 * @param frameUrl Returns false if the Android package does not declare ass ociation with the 170 * @param frameUrl Returns false if the Android package does not declare ass ociation with the
139 * origin of this URL. Can be null. 171 * origin of this URL. Can be null.
140 */ 172 */
141 private static boolean isAppInstalledAndAssociatedWithOrigin( 173 private boolean isAppInstalledAndAssociatedWithOrigin(
142 String packageName, URI frameUrl, PackageManager pm) { 174 String packageName, URI frameUrl, PackageManager pm) {
143 if (frameUrl == null) return false; 175 if (frameUrl == null) return false;
144 176
145 // Early-exit if the Android app is not installed. 177 // Early-exit if the Android app is not installed.
146 JSONArray statements; 178 JSONArray statements;
147 try { 179 try {
148 statements = getAssetStatements(packageName, pm); 180 statements = getAssetStatements(packageName, pm);
149 } catch (NameNotFoundException e) { 181 } catch (NameNotFoundException e) {
150 return false; 182 return false;
151 } 183 }
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 } 301 }
270 302
271 private static boolean statementTargetMatches(URI frameUrl, URI assetUrl) { 303 private static boolean statementTargetMatches(URI frameUrl, URI assetUrl) {
272 if (assetUrl.getScheme() == null || assetUrl.getAuthority() == null) { 304 if (assetUrl.getScheme() == null || assetUrl.getAuthority() == null) {
273 return false; 305 return false;
274 } 306 }
275 307
276 return assetUrl.getScheme().equals(frameUrl.getScheme()) 308 return assetUrl.getScheme().equals(frameUrl.getScheme())
277 && assetUrl.getAuthority().equals(frameUrl.getAuthority()); 309 && assetUrl.getAuthority().equals(frameUrl.getAuthority());
278 } 310 }
311
312 /**
313 * Runs a Runnable task after a given delay.
314 *
315 * Protected and non-static for testing.
316 *
317 * @param r The Runnable that will be executed.
318 * @param delayMillis The delay (in ms) until the Runnable will be executed.
319 * @return True if the Runnable was successfully placed into the message que ue.
320 */
321 protected boolean delayThenRun(Runnable r, long delayMillis) {
322 return new Handler().postDelayed(r, delayMillis);
Ted C 2017/04/12 15:53:50 Use can use ThreadUtils.postOnUiThreadDelayed here
Matt Giuca 2017/04/13 00:29:55 Done.
323 }
279 } 324 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698