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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java b/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
index b59028b8611a3fe368f5dfa9578d06e61687d41c..27aa246e14f8089023cd75ef066b1eff1ef2a575 100644
--- a/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
@@ -10,6 +10,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.AsyncTask;
+import android.os.Handler;
import org.json.JSONArray;
import org.json.JSONException;
@@ -24,6 +25,7 @@ import org.chromium.mojo.system.MojoException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Android implementation of the InstalledAppProvider service defined in
@@ -76,18 +78,26 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
}
final URI frameUrl = mFrameUrlDelegate.getUrl();
+ final AtomicInteger delayMillis = new AtomicInteger();
// Use an AsyncTask to execute the installed/related checks on a background thread (so as
// not to block the UI thread).
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.
@Override
protected RelatedApplication[] doInBackground(Void... unused) {
- return filterInstalledAppsOnBackgroundThread(relatedApps, frameUrl);
+ return filterInstalledAppsOnBackgroundThread(relatedApps, frameUrl, delayMillis);
}
@Override
- protected void onPostExecute(RelatedApplication[] installedApps) {
- callback.call(installedApps);
+ protected void onPostExecute(final RelatedApplication[] installedApps) {
+ // Before calling the callback, delay for the amount of time that has been
+ // calculated in |delayMillis|.
+ delayThenRun(new Runnable() {
+ @Override
+ public void run() {
+ callback.call(installedApps);
+ }
+ }, delayMillis.get());
}
}
.execute();
@@ -106,10 +116,13 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
*
* @param relatedApps A list of applications to be filtered.
* @param frameUrl The URL of the frame this operation was called from.
+ * @param delayMillis (Output) Number that will be incremented by the total amount of time in ms
+ * that should be delayed before returning to the user, to mask the installed
+ * state of the requested apps.
* @return A subsequence of applications that meet the criteria.
*/
private RelatedApplication[] filterInstalledAppsOnBackgroundThread(
- RelatedApplication[] relatedApps, URI frameUrl) {
+ RelatedApplication[] relatedApps, URI frameUrl, AtomicInteger delayMillis) {
ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedApplication>();
PackageManager pm = mContext.getPackageManager();
for (RelatedApplication app : relatedApps) {
@@ -119,9 +132,11 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
// between the app not being installed and the origin not being associated with the app
// (otherwise, arbitrary websites would be able to test whether un-associated apps are
// installed on the user's device).
- if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != null
- && isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, pm)) {
- installedApps.add(app);
+ if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != null) {
+ delayMillis.addAndGet(calculateDelayForPackageMs(app.id));
+ if (isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, pm)) {
+ installedApps.add(app);
+ }
}
}
@@ -131,6 +146,23 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
}
/**
+ * Determines how long to artifically delay for, for a particular package name.
+ */
+ private int calculateDelayForPackageMs(String packageName) {
+ // Important timing-attack prevention measure: delay by a pseudo-random amount of time, to
+ // add significant noise to the time taken to check whether this app is installed and
+ // related. Otherwise, it would be possible to tell whether a non-related app is installed,
+ // based on the time this operation takes.
+ //
+ // Generate a 16-bit hash based on a unique device ID + the package name.
+ short hash = PackageHash.hashForPackage(packageName);
+
+ // The time delay is the low 10 bits of the hash in 100ths of a ms (between 0 and 10ms).
+ int delayHundredthsOfMs = hash & 0x3ff;
+ return delayHundredthsOfMs / 100;
+ }
+
+ /**
* Determines whether a particular app is installed and matches the origin.
*
* @param packageName Name of the Android package to check if installed. Returns false if the
@@ -138,7 +170,7 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
* @param frameUrl Returns false if the Android package does not declare association with the
* origin of this URL. Can be null.
*/
- private static boolean isAppInstalledAndAssociatedWithOrigin(
+ private boolean isAppInstalledAndAssociatedWithOrigin(
String packageName, URI frameUrl, PackageManager pm) {
if (frameUrl == null) return false;
@@ -276,4 +308,17 @@ public class InstalledAppProviderImpl implements InstalledAppProvider {
return assetUrl.getScheme().equals(frameUrl.getScheme())
&& assetUrl.getAuthority().equals(frameUrl.getAuthority());
}
+
+ /**
+ * Runs a Runnable task after a given delay.
+ *
+ * Protected and non-static for testing.
+ *
+ * @param r The Runnable that will be executed.
+ * @param delayMillis The delay (in ms) until the Runnable will be executed.
+ * @return True if the Runnable was successfully placed into the message queue.
+ */
+ protected boolean delayThenRun(Runnable r, long delayMillis) {
+ 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.
+ }
}

Powered by Google App Engine
This is Rietveld 408576698