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:
@@ -1232,8 +1232,8 @@ Initial public release
|
|||||||
|
|
||||||
|
|
||||||
<string name="AutofillWarning_title">Security Alert: Unrecognized domain/package link</string>
|
<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_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>
|
</resources>
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ using Android.Views.Autofill;
|
|||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Java.Util;
|
using Java.Util;
|
||||||
using keepass2android.services.AutofillBase.model;
|
using keepass2android.services.AutofillBase.model;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Android.Content.PM;
|
||||||
|
using Com.Dropbox.Core.V2.Teamlog;
|
||||||
using AlertDialog = Android.App.AlertDialog;
|
using AlertDialog = Android.App.AlertDialog;
|
||||||
|
|
||||||
namespace keepass2android.services.AutofillBase
|
namespace keepass2android.services.AutofillBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public abstract class ChooseForAutofillActivityBase : AndroidX.AppCompat.App.AppCompatActivity
|
public abstract class ChooseForAutofillActivityBase : AndroidX.AppCompat.App.AppCompatActivity
|
||||||
{
|
{
|
||||||
protected Intent ReplyIntent;
|
protected Intent ReplyIntent;
|
||||||
@@ -51,6 +51,7 @@ namespace keepass2android.services.AutofillBase
|
|||||||
RestartApp();
|
RestartApp();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Intent.HasExtra(ExtraDisplayWarning))
|
if (Intent.HasExtra(ExtraDisplayWarning))
|
||||||
{
|
{
|
||||||
AutofillServiceBase.DisplayWarning warning =
|
AutofillServiceBase.DisplayWarning warning =
|
||||||
@@ -60,22 +61,56 @@ namespace keepass2android.services.AutofillBase
|
|||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.SetTitle(this.GetString(Resource.String.AutofillWarning_title));
|
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(
|
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),
|
builder.SetPositiveButton(this.GetString(Resource.String.Continue),
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
|
new Kp2aDigitalAssetLinksDataSource(this).RememberTrustedLink(Intent.GetStringExtra(ExtraQueryDomainString),
|
||||||
|
Intent.GetStringExtra(ExtraQueryPackageString));
|
||||||
Proceed();
|
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) =>
|
builder.SetNegativeButton(this.GetString(Resource.String.cancel), (dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
Finish();
|
Finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Dialog dialog = builder.Create();
|
Dialog dialog = builder.Create();
|
||||||
dialog.Show();
|
dialog.Show();
|
||||||
@@ -86,6 +121,7 @@ namespace keepass2android.services.AutofillBase
|
|||||||
Proceed();
|
Proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Proceed()
|
private void Proceed()
|
||||||
{
|
{
|
||||||
string requestedUrl = Intent.GetStringExtra(ExtraQueryString);
|
string requestedUrl = Intent.GetStringExtra(ExtraQueryString);
|
||||||
|
|||||||
@@ -1,33 +1,65 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
using Android.Preferences;
|
||||||
|
|
||||||
namespace keepass2android.services.AutofillBase
|
namespace keepass2android.services.AutofillBase
|
||||||
{
|
{
|
||||||
|
|
||||||
internal class Kp2aDigitalAssetLinksDataSource
|
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
|
_ctx = ctx;
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
{
|
|
||||||
instance = new Kp2aDigitalAssetLinksDataSource();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
static readonly HashSet<string> _trustedBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
"org.mozilla.firefox","org.mozilla.firefox_beta","org.mozilla.klar","org.mozilla.focus",
|
"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"
|
"acr.browser.lightning", "acr.browser.barebones", "jp.hazuki.yuzubrowser"
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool IsTrustedBrowser(string packageName)
|
|
||||||
{
|
|
||||||
return _trustedBrowsers.Contains(packageName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ namespace keepass2android.services.AutofillBase
|
|||||||
public string PackageId { get; set; }
|
public string PackageId { get; set; }
|
||||||
|
|
||||||
public StructureParser(Context context, AssistStructure structure)
|
public StructureParser(Context context, AssistStructure structure)
|
||||||
{
|
{
|
||||||
|
kp2aDigitalAssetLinksDataSource = new Kp2aDigitalAssetLinksDataSource(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
Structure = structure;
|
Structure = structure;
|
||||||
AutofillFields = new AutofillFieldMetadataCollection();
|
AutofillFields = new AutofillFieldMetadataCollection();
|
||||||
@@ -176,7 +177,7 @@ namespace keepass2android.services.AutofillBase
|
|||||||
result.PackageName = Structure.ActivityComponent.PackageName;
|
result.PackageName = Structure.ActivityComponent.PackageName;
|
||||||
if (!string.IsNullOrEmpty(webDomain))
|
if (!string.IsNullOrEmpty(webDomain))
|
||||||
{
|
{
|
||||||
result.IncompatiblePackageAndDomain = !Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, result.PackageName);
|
result.IncompatiblePackageAndDomain = !kp2aDigitalAssetLinksDataSource.IsTrustedLink(webDomain, result.PackageName);
|
||||||
if (result.IncompatiblePackageAndDomain)
|
if (result.IncompatiblePackageAndDomain)
|
||||||
{
|
{
|
||||||
CommonUtil.loge($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
|
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 static readonly HashSet<string> _usernameHints = new HashSet<string> { "email","e-mail","username" };
|
||||||
|
private Kp2aDigitalAssetLinksDataSource kp2aDigitalAssetLinksDataSource;
|
||||||
|
|
||||||
private static bool HasUsernameHint(AssistStructure.ViewNode f)
|
private static bool HasUsernameHint(AssistStructure.ViewNode f)
|
||||||
{
|
{
|
||||||
return ContainsAny(f.IdEntry, _usernameHints) ||
|
return ContainsAny(f.IdEntry, _usernameHints) ||
|
||||||
|
|||||||
Reference in New Issue
Block a user