diff --git a/application/basilisk/app/profile/basilisk.js b/application/basilisk/app/profile/basilisk.js
index 79d767851d..24f1c582cc 100644
--- a/application/basilisk/app/profile/basilisk.js
+++ b/application/basilisk/app/profile/basilisk.js
@@ -1438,3 +1438,11 @@ pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);
// controlling validation are located in /services/sync/services-sync.js
pref("services.sync.validation.enabled", true);
#endif
+
+// When a user cancels this number of authentication dialogs coming from
+// a single web page (eTLD+1) in a row, all following authentication dialogs
+// will be blocked (automatically canceled) for that page.
+// This counter is per-tab and per-domain to minimize false positives.
+// The counter resets when the page is reloaded from the UI
+// (content-reloads do NOT clear this to mitigate reloading tricks).
+pref("prompts.authentication_dialog_abuse_limit", 3);
diff --git a/application/basilisk/base/content/browser.js b/application/basilisk/base/content/browser.js
index 893934166c..af2545fcd5 100644
--- a/application/basilisk/base/content/browser.js
+++ b/application/basilisk/base/content/browser.js
@@ -3099,6 +3099,11 @@ function getWebNavigation()
}
function BrowserReloadWithFlags(reloadFlags) {
+
+ // Reset DOS mitigation for auth prompts when user initiates a reload.
+ let browser = gBrowser.selectedBrowser;
+ delete browser.authPromptCounter;
+
let url = gBrowser.currentURI.spec;
if (gBrowser.updateBrowserRemotenessByURL(gBrowser.selectedBrowser, url)) {
// If the remoteness has changed, the new browser doesn't have any
diff --git a/application/basilisk/base/content/browser.xul b/application/basilisk/base/content/browser.xul
index 6b7a10dc7f..c732c3263a 100644
--- a/application/basilisk/base/content/browser.xul
+++ b/application/basilisk/base/content/browser.xul
@@ -999,7 +999,8 @@
contentcontextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
selectmenulist="ContentSelectDropdown"
- datetimepicker="DateTimePickerPanel"/>
+ datetimepicker="DateTimePickerPanel"
+ authdosprotected="true" />
diff --git a/application/basilisk/base/content/tabbrowser.xml b/application/basilisk/base/content/tabbrowser.xml
index 0408350018..6ac6d54c75 100644
--- a/application/basilisk/base/content/tabbrowser.xml
+++ b/application/basilisk/base/content/tabbrowser.xml
@@ -25,7 +25,7 @@
+ xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker,authdosprotected"/>
@@ -1952,6 +1952,10 @@
if (this.hasAttribute("datetimepicker")) {
b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
}
+
+ if (this.hasAttribute("authdosprotected")) {
+ b.setAttribute("authdosprotected", this.getAttribute("authdosprotected"));
+ }
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
@@ -3007,7 +3011,10 @@
diff --git a/application/basilisk/base/content/urlbarBindings.xml b/application/basilisk/base/content/urlbarBindings.xml
index eb31505812..4c010bffb4 100644
--- a/application/basilisk/base/content/urlbarBindings.xml
+++ b/application/basilisk/base/content/urlbarBindings.xml
@@ -554,6 +554,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
Cu.reportError(ex);
}
+ // Reset DOS mitigations for the basic auth prompt.
+ delete browser.authPromptCounter;
+
let params = {
postData,
allowThirdPartyFixup: true,
diff --git a/application/palemoon/app/profile/palemoon.js b/application/palemoon/app/profile/palemoon.js
index 43f020f9a1..3df5d71944 100644
--- a/application/palemoon/app/profile/palemoon.js
+++ b/application/palemoon/app/profile/palemoon.js
@@ -1160,6 +1160,14 @@ pref("toolkit.pageThumbs.minHeight", 180);
pref("ui.key.menuAccessKeyFocuses", true);
#endif
+// When a user cancels this number of authentication dialogs coming from
+// a single web page (eTLD+1) in a row, all following authentication dialogs
+// will be blocked (automatically canceled) for that page.
+// This counter is per-tab and per-domain to minimize false positives.
+// The counter resets when the page is reloaded from the UI
+// (content-reloads do NOT clear this to mitigate reloading tricks).
+pref("prompts.authentication_dialog_abuse_limit", 3);
+
// ****************** s4e prefs ******************
pref("status4evar.addonbar.borderStyle", false);
pref("status4evar.addonbar.closeButton", false);
diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js
index 3feeef9b6c..4e753d4228 100644
--- a/application/palemoon/base/content/browser.js
+++ b/application/palemoon/base/content/browser.js
@@ -2661,6 +2661,11 @@ function getWebNavigation()
}
function BrowserReloadWithFlags(reloadFlags) {
+
+ // Reset DOS mitigation for auth prompts when user initiates a reload.
+ let browser = gBrowser.selectedBrowser;
+ delete browser.authPromptCounter;
+
/* First, we'll try to use the session history object to reload so
* that framesets are handled properly. If we're in a special
* window (such as view-source) that has no session history, fall
diff --git a/application/palemoon/base/content/browser.xul b/application/palemoon/base/content/browser.xul
index ce2a7c5a8f..ddc305a7b8 100644
--- a/application/palemoon/base/content/browser.xul
+++ b/application/palemoon/base/content/browser.xul
@@ -965,7 +965,8 @@
tabcontainer="tabbrowser-tabs"
contentcontextmenu="contentAreaContextMenu"
autocompletepopup="PopupAutoComplete"
- datetimepicker="DateTimePickerPanel"/>
+ datetimepicker="DateTimePickerPanel"
+ authdosprotected="true"/>
diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml
index 988cae55c4..cbe029af03 100644
--- a/application/palemoon/base/content/tabbrowser.xml
+++ b/application/palemoon/base/content/tabbrowser.xml
@@ -30,7 +30,7 @@
+ xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,datetimepicker,authdosprotected"/>
@@ -1588,6 +1588,10 @@
if (this.hasAttribute("datetimepicker")) {
b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
}
+
+ if (this.hasAttribute("authdosprotected")) {
+ b.setAttribute("authdosprotected", this.getAttribute("authdosprotected"));
+ }
// Create the browserStack container
var stack = document.createElementNS(NS_XUL, "stack");
@@ -2458,7 +2462,10 @@
diff --git a/application/palemoon/base/content/urlbarBindings.xml b/application/palemoon/base/content/urlbarBindings.xml
index d188e66585..d2d9cc7204 100644
--- a/application/palemoon/base/content/urlbarBindings.xml
+++ b/application/palemoon/base/content/urlbarBindings.xml
@@ -302,6 +302,10 @@
// but don't let that interfere with the loading of the url.
Cu.reportError(ex);
}
+
+ // Reset DOS mitigations for the basic auth prompt.
+ let browser = gBrowser.selectedBrowser;
+ delete browser.authPromptCounter;
function loadCurrent() {
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 7156b2a0af..91a9a1bfca 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5459,8 +5459,9 @@ pref("dom.storageManager.enabled", true);
pref("dom.storageManager.enabled", false);
#endif
-// When a user cancels this number of authentication dialogs coming from
-// a single web page in a row, all following authentication dialogs will
-// be blocked (automatically canceled) for that page. The counter resets
-// when the page is reloaded. To turn this feature off, just set the limit to 0.
-pref("prompts.authentication_dialog_abuse_limit", 3);
+// DoS protection for HTTP Auth prompt spawning.
+// -1 = completely disable HTTP Auth prompting. (careful!)
+// 0 = disable this DoS protection
+// >0 = suppress further prompts after the user has canceled the dialog n times
+// See application preferences for appropriate defaults.
+pref("prompts.authentication_dialog_abuse_limit", 0);
diff --git a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
index 720e80446b..c4be39e318 100644
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -97,17 +97,25 @@ LoginManagerPromptFactory.prototype = {
return;
}
- // Allow only a limited number of authentication dialogs when they are all
- // canceled by the user.
- var cancelationCounter = (prompter._browser && prompter._browser.canceledAuthenticationPromptCounter) || { count: 0, id: 0 };
- if (prompt.channel) {
- var httpChannel = prompt.channel.QueryInterface(Ci.nsIHttpChannel);
- if (httpChannel) {
- var windowId = httpChannel.topLevelContentWindowId;
- if (windowId != cancelationCounter.id) {
- // window has been reloaded or navigated, reset the counter
- cancelationCounter = { count: 0, id: windowId };
- }
+ // Set up a counter for ensuring that the basic auth prompt can not
+ // be abused for DOS-style attacks. With this counter, each eTLD+1
+ // per browser will get a limited number of times a user can
+ // cancel the prompt until we stop showing it.
+ let browser = prompter._browser;
+ let baseDomain = null;
+ if (browser && browser.isAuthDOSProtected) {
+ try {
+ baseDomain = Services.eTLD.getBaseDomainFromHost(hostname);
+ } catch (e) {
+ baseDomain = hostname;
+ }
+
+ if (!browser.authPromptCounter) {
+ browser.authPromptCounter = {};
+ }
+
+ if (!browser.authPromptCounter[baseDomain]) {
+ browser.authPromptCounter[baseDomain] = 0;
}
}
@@ -137,13 +145,14 @@ LoginManagerPromptFactory.prototype = {
prompt.inProgress = false;
self._asyncPromptInProgress = false;
- if (ok) {
- cancelationCounter.count = 0;
- } else {
- cancelationCounter.count++;
- }
- if (prompter._browser) {
- prompter._browser.canceledAuthenticationPromptCounter = cancelationCounter;
+ if (browser && browser.isAuthDOSProtected) {
+ // Reset the counter state if the user replied to a prompt and actually
+ // tried to login (vs. simply clicking any button to get out).
+ if (ok && (prompt.authInfo.username || prompt.authInfo.password)) {
+ browser.authPromptCounter[baseDomain] = 0;
+ } else {
+ browser.authPromptCounter[baseDomain] += 1;
+ }
}
}
@@ -168,14 +177,27 @@ LoginManagerPromptFactory.prototype = {
var cancelDialogLimit = Services.prefs.getIntPref("prompts.authentication_dialog_abuse_limit");
- this.log("cancelationCounter =", cancelationCounter);
- if (cancelDialogLimit && cancelationCounter.count >= cancelDialogLimit) {
- this.log("Blocking auth dialog, due to exceeding dialog bloat limit");
- delete this._asyncPrompts[hashKey];
+ // Block the auth prompt if:
+ // - There is an attached browser element
+ // - The browser element has opted-in to DOS protection
+ // - The dialog cancellation limit is not 0 (= feature disabled)
+ // - The amount of cancellations >= the set abuse limit
+ if (browser && browser.isAuthDOSProtected) {
+ let cancelationCounter = browser.authPromptCounter[baseDomain];
+ this.log("cancelationCounter =", cancelationCounter);
- // just make the runnable cancel all consumers
- runnable.cancel = true;
+ if (cancelDialogLimit && cancelationCounter >= cancelDialogLimit) {
+ this.log("Blocking auth dialog, due to exceeding dialog bloat limit");
+ delete this._asyncPrompts[hashKey];
+
+ // just make the runnable cancel all consumers
+ runnable.cancel = true;
+ } else {
+ this._asyncPromptInProgress = true;
+ prompt.inProgress = true;
+ }
} else {
+ // No DOS protection: prompt
this._asyncPromptInProgress = true;
prompt.inProgress = true;
}
diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml
index a30ff1c43c..5a0a99bf86 100644
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -899,6 +899,10 @@
null
+
+
null