improve user experience with autofill security alert:

* remember trusted links
 * allow to make an app trusted
 * improve message by displaying application label instead of package name (show package name once for security)
This commit is contained in:
Philipp Crocoll
2020-07-27 11:19:29 +02:00
parent 02073c01b6
commit d3ced0cdf3
4 changed files with 92 additions and 25 deletions

View File

@@ -1232,8 +1232,8 @@ Initial public release
<string name="AutofillWarning_title">Security Alert: Unrecognized domain/package link</string>
<string name="AutofillWarning_Intro">You are about to insert credentials for domain "%1$s" into the app with package name "%2$s".</string>
<string name="AutofillWarning_Intro">You are about to insert credentials for domain "%1$s" into the app "%2$s".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">If you trust "%2$s" to belong to "%1$s" or you trust the app "%2$s" not to misuse the credentials (e.g. because it is a trusted browser app), it is ok to continue. If not, please cancel.</string>
<string name="AutofillWarning_continue">Security Alert: Unrecognized domain/package link</string>
<string name="AutofillWarning_trustAsBrowser">Accept always in "%1$s"</string>
</resources>

View File

@@ -10,13 +10,13 @@ using Android.Views.Autofill;
using Android.Widget;
using Java.Util;
using keepass2android.services.AutofillBase.model;
using System.Collections.Generic;
using System.Linq;
using Android.Content.PM;
using Com.Dropbox.Core.V2.Teamlog;
using AlertDialog = Android.App.AlertDialog;
namespace keepass2android.services.AutofillBase
{
public abstract class ChooseForAutofillActivityBase : AndroidX.AppCompat.App.AppCompatActivity
{
protected Intent ReplyIntent;
@@ -51,6 +51,7 @@ namespace keepass2android.services.AutofillBase
RestartApp();
return;
}
if (Intent.HasExtra(ExtraDisplayWarning))
{
AutofillServiceBase.DisplayWarning warning =
@@ -60,22 +61,56 @@ namespace keepass2android.services.AutofillBase
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetTitle(this.GetString(Resource.String.AutofillWarning_title));
string appName = Intent.GetStringExtra(ExtraQueryPackageString);
string appNameWithPackage = appName;
try
{
var appInfo = PackageManager.GetApplicationInfo(appName, 0);
if (appInfo != null)
{
appName = PackageManager.GetApplicationLabel(appInfo);
appNameWithPackage = appName + " (" + Intent.GetStringExtra(ExtraQueryPackageString) + ")";
}
}
catch (Exception)
{
// ignored
}
builder.SetMessage(
GetString(Resource.String.AutofillWarning_Intro, new Java.Lang.Object[] { Intent.GetStringExtra(ExtraQueryDomainString), Intent.GetStringExtra(ExtraQueryPackageString) })
GetString(Resource.String.AutofillWarning_Intro, new Java.Lang.Object[]
{
Intent.GetStringExtra(ExtraQueryDomainString), appNameWithPackage
})
+ " " +
this.GetString(Resource.String.AutofillWarning_FillDomainInUntrustedApp, new Java.Lang.Object[] { Intent.GetStringExtra(ExtraQueryDomainString), Intent.GetStringExtra(ExtraQueryPackageString) }));
this.GetString(Resource.String.AutofillWarning_FillDomainInUntrustedApp, new Java.Lang.Object[]
{
Intent.GetStringExtra(ExtraQueryDomainString), appName
}));
builder.SetPositiveButton(this.GetString(Resource.String.Continue),
(dlgSender, dlgEvt) =>
{
new Kp2aDigitalAssetLinksDataSource(this).RememberTrustedLink(Intent.GetStringExtra(ExtraQueryDomainString),
Intent.GetStringExtra(ExtraQueryPackageString));
Proceed();
});
builder.SetNeutralButton(this.GetString(Resource.String.AutofillWarning_trustAsBrowser, new Java.Lang.Object[]
{appName}),
(sender, args) =>
{
new Kp2aDigitalAssetLinksDataSource(this).RememberAsTrustedApp(Intent.GetStringExtra(ExtraQueryPackageString));
Proceed();
});
builder.SetNegativeButton(this.GetString(Resource.String.cancel), (dlgSender, dlgEvt) =>
{
Finish();
});
Dialog dialog = builder.Create();
dialog.Show();
@@ -86,6 +121,7 @@ namespace keepass2android.services.AutofillBase
Proceed();
}
private void Proceed()
{
string requestedUrl = Intent.GetStringExtra(ExtraQueryString);

View File

@@ -1,33 +1,65 @@
using System.Collections.Generic;
using System.Linq;
using Android.Content;
using Android.Preferences;
namespace keepass2android.services.AutofillBase
{
internal class Kp2aDigitalAssetLinksDataSource
{
private static Kp2aDigitalAssetLinksDataSource instance;
private Kp2aDigitalAssetLinksDataSource() { }
private const string Autofilltrustedapps = "AutoFillTrustedApps";
private readonly Context _ctx;
public static Kp2aDigitalAssetLinksDataSource Instance
public Kp2aDigitalAssetLinksDataSource(Context ctx)
{
get
{
if (instance == null)
{
instance = new Kp2aDigitalAssetLinksDataSource();
}
return instance;
}
_ctx = ctx;
}
public bool IsValid(Context context, string webDomain, string packageName)
public bool IsTrustedApp(string packageName)
{
return (IsTrustedBrowser(packageName));
if (_trustedBrowsers.Contains(packageName))
return true;
var prefs = PreferenceManager.GetDefaultSharedPreferences(_ctx);
var trustedApps = prefs.GetStringSet(Autofilltrustedapps, new List<string>()).ToHashSet();
return trustedApps.Contains(packageName);
}
public bool IsTrustedLink(string domain, string targetPackage)
{
//we can fill everything into trusted apps (aka browsers)
if (IsTrustedApp(targetPackage))
return true;
//see if the user explicitly allows to fill credentials for domain into targetPackage:
var prefs = PreferenceManager.GetDefaultSharedPreferences(_ctx);
var trustedLinks = prefs.GetStringSet("AutoFillTrustedLinks", new List<string>()).ToHashSet();
return trustedLinks.Contains(BuildLink(domain, targetPackage));
}
public void RememberAsTrustedApp(string packageName)
{
var prefs = PreferenceManager.GetDefaultSharedPreferences(_ctx);
var trustedApps = prefs.GetStringSet(Autofilltrustedapps, new List<string>()).ToHashSet();
trustedApps.Add(packageName);
prefs.Edit().PutStringSet(Autofilltrustedapps, trustedApps).Commit();
}
public void RememberTrustedLink(string domain, string package)
{
var prefs = PreferenceManager.GetDefaultSharedPreferences(_ctx);
var trustedLinks = prefs.GetStringSet("AutoFillTrustedLinks", new List<string>()).ToHashSet();
trustedLinks.Add(BuildLink(domain, package));
prefs.Edit().PutStringSet("AutoFillTrustedLinks", trustedLinks).Commit();
}
private static string BuildLink(string domain, string package)
{
return domain + " + " + package;
}
static readonly HashSet<string> _trustedBrowsers = new HashSet<string>
{
"org.mozilla.firefox","org.mozilla.firefox_beta","org.mozilla.klar","org.mozilla.focus",
@@ -41,9 +73,5 @@ namespace keepass2android.services.AutofillBase
"acr.browser.lightning", "acr.browser.barebones", "jp.hazuki.yuzubrowser"
};
private bool IsTrustedBrowser(string packageName)
{
return _trustedBrowsers.Contains(packageName);
}
}
}

View File

@@ -31,7 +31,8 @@ namespace keepass2android.services.AutofillBase
public string PackageId { get; set; }
public StructureParser(Context context, AssistStructure structure)
{
{
kp2aDigitalAssetLinksDataSource = new Kp2aDigitalAssetLinksDataSource(context);
mContext = context;
Structure = structure;
AutofillFields = new AutofillFieldMetadataCollection();
@@ -176,7 +177,7 @@ namespace keepass2android.services.AutofillBase
result.PackageName = Structure.ActivityComponent.PackageName;
if (!string.IsNullOrEmpty(webDomain))
{
result.IncompatiblePackageAndDomain = !Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, result.PackageName);
result.IncompatiblePackageAndDomain = !kp2aDigitalAssetLinksDataSource.IsTrustedLink(webDomain, result.PackageName);
if (result.IncompatiblePackageAndDomain)
{
CommonUtil.loge($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
@@ -196,6 +197,8 @@ namespace keepass2android.services.AutofillBase
}
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email","e-mail","username" };
private Kp2aDigitalAssetLinksDataSource kp2aDigitalAssetLinksDataSource;
private static bool HasUsernameHint(AssistStructure.ViewNode f)
{
return ContainsAny(f.IdEntry, _usernameHints) ||