Javascript 检测浏览器是否使用隐私浏览模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2860879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:14:41  来源:igfitidea点击:

Detecting if a browser is using Private Browsing mode

javascripthtmlsecuritybrowsercookies

提问by Steve

I'm building an extranet for a company paranoid about security. They want to make sure that (among other things) their users are browsing the site with the Private Browsing mode switched on in their web browser so that no cookies or history is kept.

我正在为一家对安全有疑虑的公司建立一个外联网。他们希望确保(除其他外)他们的用户在浏览器的隐私浏览模式下浏览网站,这样就不会保留任何 cookie 或历史记录。

I found only this http://jeremiahgrossman.blogspot.com/2009/03/detecting-private-browsing-mode.htmland https://serverfault.com/questions/18966/force-safari-to-operate-in-private-mode-and-detect-that-state-from-a-webserver

我只发现了这个 http://jeremiahgrossman.blogspot.com/2009/03/detecting-private-browsing-mode.htmlhttps://serverfault.com/questions/18966/force-safari-to-operate-in-私有模式并检测来自网络服务器的状态

The ideal solution would use no or minimal javascript. Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?

理想的解决方案是不使用或最少使用 javascript。尝试为所有浏览器和平台设置唯一的 cookie 是否可行?以前有人做过这个吗?

thanks!

谢谢!



update

更新

http://crypto.stanford.edu/~collinj/research/incognito/uses the CSS visited technique of the browser fingerprinters mentioned by other posters- thanks for the hints.

http://crypto.stanford.edu/~collinj/research/incognito/使用其他海报提到的浏览器指纹识别器的 CSS 访问技术 - 感谢您的提示。

I like it because it is small and elegant, but still want to be able to do it without javascript if possible.

我喜欢它,因为它小巧而优雅,但如果可能的话,仍然希望能够在没有 javascript 的情况下做到这一点。

回答by Dwayne Charrington

Update June 2019

2019 年 6 月更新

Google is removing the abilityto detect Private Browsing Mode permanently in Chrome 76 onwards. So, if you're wanting to detect private browsing it's now impossible (unless you find a way to do it that Google hasn't found). The ability to detect private browsing mode has been acknowledged as a bug and was never intended.

谷歌正在移除在 Chrome 76 及更高版本中永久检测隐私浏览模式的功能。所以,如果你想检测隐私浏览,现在是不可能的(除非你找到了一种谷歌没有找到的方法)。检测隐私浏览模式的能力已被认为是一个错误,并且从未打算这样做。

To anyone else coming across this question, please note as of 2014, there is no reliable or accurate way to detect if someone is browsing in an incognito/private/safe browsing mode through Javascript or CSS. Previous solutions that once worked like the CSS history hack have since been rendered unusable by all browser vendors.

对于遇到此问题的其他人,请注意,截至 2014 年,没有可靠或准确的方法来检测是否有人通过 Javascript 或 CSS 以隐身/隐私/安全浏览模式进行浏览。以前的解决方案曾经像 CSS 历史黑客一样有效,但后来被所有浏览器供应商渲染为无法使用。

There should never be a situation where needing to detect private browsing mode on a normal day-to-day website is ever needed. People are choosing to browse anonymously and or not anonymously for their own reasons.

永远不应该出现需要在正常的日常网站上检测隐私浏览模式的情况。人们出于自己的原因选择匿名浏览或不匿名浏览。

Browsers like Chrome and Firefox do not disable functionality like localStorage any more. They simply namespace it in a temporary location to prevent websites that use it from erroring out. Once you're finished browsing, the namespace is erased and nothing is saved. If you are testing for localStorage support regardless of mode, it will always return true for browsers that support it.

Chrome 和 Firefox 等浏览器不再禁用 localStorage 等功能。他们只是将它命名为一个临时位置,以防止使用它的网站出错。完成浏览后,命名空间将被删除,并且不会保存任何内容。如果您正在测试 localStorage 支持,而不管模式如何,对于支持它的浏览器,它将始终返回 true。

Other means of detecting private mode in Chrome specifically have been completely patched and will no longer work.

其他在 Chrome 中检测私有模式的方法已经完全修补,不再有效。

If it is required internally by a company, you should develop a browser plugin. Chrome and Firefox, in particular, expose internal API's which allow plugins to check if the user is in private browsing/incognito mode and action accordingly. It cannot be done outside of a plugin.

如果公司内部需要,你应该开发一个浏览器插件。特别是 Chrome 和 Firefox,公开了内部 API,允许插件检查用户是否处于隐私浏览/隐身模式并相应地采取行动。它不能在插件之外完成。

回答by Jez D

Here's an easier way to do detect privacy mode. This works in Safari only. I created it because a web app I am developing uses localStorage. LocalStorage is not available in Safari when in privacy mode, thus my app will not work. On page load, run the script below. It shows an alert box if we cannot use localStorage.

这是检测隐私模式的更简单方法。这仅适用于 Safari。我创建它是因为我正在开发的 Web 应用程序使用 localStorage。LocalStorage 在隐私模式下在 Safari 中不可用,因此我的应用程序将无法运行。在页面加载时,运行下面的脚本。如果我们不能使用 localStorage,它会显示一个警告框。

try {
  // try to use localStorage
  localStorage.test = 2;        
} catch (e) {
  // there was an error so...
  alert('You are in Privacy Mode\nPlease deactivate Privacy Mode and then reload the page.');
}

回答by manniL

Current state

当前状态

Google Chrome has developed further and leaves no more space for detection when using incognito mode. Same might apply for other browsers.

谷歌浏览器进一步发展,在使用隐身模式时没有更多的检测空间。同样可能适用于其他浏览器。



Old solutions (might partially work)

旧解决方案(可能部分有效)

It is possibleto detect enabled private browsing modes for the majority of used browsers. This includes Safari, Firefox, IE10, Edge and Google Chrome.

可以为大多数使用的浏览器检测启用的隐私浏览模式。这包括 Safari、Firefox、IE10、Edge 和 Google Chrome。



Firefox

火狐

When the private browsing mode of Firefox is enabled, the IndexedDB throws an InvalidStateError because it is not available in private browsing mode.

开启 Firefox 的隐私浏览模式时,IndexedDB 会抛出 InvalidStateError,因为它在隐私浏览模式下不可用。

To very if that:

非常如果:

var db = indexedDB.open("test");
db.onerror = function(){/*Firefox PB enabled*/};
db.onsuccess =function(){/*Not enabled*/};

Safari

苹果浏览器

For Safari, the key is the local storage service. It is disabled in privacy mode. So try to access it and use a try-catch clause. The following method works on both, OSX and iOS devices. Credits for this method are going to this question and answer

对于 Safari,关键是本地存储服务。它在隐私模式下被禁用。所以尝试访问它并使用 try-catch 子句。以下方法适用于 OSX 和 iOS 设备。此方法的积分将用于此问答

var storage = window.sessionStorage;
try {
    storage.setItem("someKeyHere", "test");
    storage.removeItem("someKeyHere");
} catch (e) {
    if (e.code === DOMException.QUOTA_EXCEEDED_ERR && storage.length === 0) {
        //Private here
    }
}

IE10/Edge

IE10/边缘

Internet Explore is even going to disable the IndexedDB when in privacy mode. So check for existence. But that's not sufficient enough, because older browsers maybe don't even have an IDB. So do another check, e.g. for events that only IE10 and subsequent browser have/trigger. A related question on CodeReview can be found here

Internet Explore 甚至会在隐私模式下禁用 IndexedDB。所以检查是否存在。但这还不够,因为较旧的浏览器可能甚至没有 IDB。所以再做一次检查,例如只有 IE10 和后续浏览器有/触发的事件。可以在此处找到有关 CodeReview 的相关问题

if(!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)){
 //Privacy Mode
}

Chrome

铬合金

Update: This doesn't work since Chrome 76 (thanks to @jLynx)

更新:自 Chrome 76 起这不起作用(感谢 @jLynx)

Chromes Incognito mode can be verified by the file system. A great explanation can be found here on SO

Chromes 隐身模式可以通过文件系统进行验证。一个很好的解释可以在这里找到SO

var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
    console.log("FS check failed..");
    return;
}

fs(window.TEMPORARY, 100, function (fs) {}, function (err) {
//Incognito mode
});

回答by Endless

Here is my take on detecting private mode

这是我对检测私人模式的看法

function detectPrivateMode(cb) {
    var db,
    on = cb.bind(null, true),
    off = cb.bind(null, false)

    function tryls() {
        try {
            localStorage.length ? off() : (localStorage.x = 1, localStorage.removeItem("x"), off());
        } catch (e) {
            // Safari only enables cookie in private mode
            // if cookie is disabled then all client side storage is disabled
            // if all client side storage is disabled, then there is no point
            // in using private mode
            navigator.cookieEnabled ? on() : off();
        }
    }

    // Blink (chrome & opera)
    window.webkitRequestFileSystem ? webkitRequestFileSystem(0, 0, off, on)
    // FF
    : "MozAppearance" in document.documentElement.style ? (db = indexedDB.open("test"), db.onerror = on, db.onsuccess = off)
    // Safari
    : /constructor/i.test(window.HTMLElement) || window.safari ? tryls()
    // IE10+ & edge
    : !window.indexedDB && (window.PointerEvent || window.MSPointerEvent) ? on()
    // Rest
    : off()
}

detectPrivateMode(function (isPrivateMode) {
    console.log('is private mode: ' + isPrivateMode)
})

editfound a modern, faster, synkronas way to try it in firefox (they don't have service workers in privat mode) similar to ie don't include indexedDB but the test only works in secure sites

编辑找到了一种现代的、更快的、synkronas 方法在 firefox 中尝试它(他们没有在私有模式下的服务工作者)类似于 ie 不包括 indexedDB 但该测试仅适用于安全站点

: "MozAppearance" in document.documentElement.style ? navigator.serviceWorker ? off() : on()

回答by Matt S

There's no way for your web page to know, absolutely for sure, that the user is in private browsing mode. Any attempts to check for various browser features will need to change often as security implementations are updated. It may work for some time in some browsers, but not all.

您的网页无法绝对肯定地知道用户处于隐私浏览模式。随着安全实施的更新,任何检查各种浏览器功能的尝试都需要经常更改。它可能在某些浏览器中工作一段时间,但不是全部。

If the company is that concerned about security, I'd suggest rolling your own Firefox or Chromium distribution with locked down privacy settings, and only allowing that custom client to connect to the extranet.

如果公司担心安全性,我建议您使用锁定的隐私设置滚动您自己的 Firefox 或 Chromium 发行版,并且只允许该自定义客户端连接到外联网。

回答by jLynx

I have built a little library that will work on all major platforms and browsers that I have tested: https://github.com/jLynx/PrivateWindowCheck

我已经构建了一个可以在我测试过的所有主要平台和浏览器上运行的小库:https: //github.com/jLynx/PrivateWindowCheck

You can simply call

你可以简单地打电话

isPrivateWindow(function(is_private) {
    if(is_private)
        alert('Private');
    else
        alert('Not Private');
});

回答by Julien

The localStoragetrick is a bug which has been fixed, and it doesn't work anymore in Safari 11.0.

localStorage诀窍是已经修正了一个错误,它不会在Safari 11.0工作了。

There is an interesting alternative that works in Safari, Opera and Internet Explorer (not Chrome): those browser send a DNT: 1header (Do Not Track).

有一个有趣的替代方案适用于 Safari、Opera 和 Internet Explorer(不是 Chrome):这些浏览器发送DNT: 1标头(Do Not Track)。

It's not 100% reliable because this header can be enabled for normal browsing (it's disabled by default), but it can help to identify privacy-conscious users.

它不是 100% 可靠,因为可以为正常浏览启用此标头(默认情况下禁用它),但它可以帮助识别注重隐私的用户。

回答by Lee Louviere

You're not going to block them if they don't have private browsing enabled.

如果他们没有启用隐私浏览,你就不会阻止他们。

Why have a smart message box at all?

为什么有一个智能消息框?

Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?

尝试为所有浏览器和平台设置唯一的 cookie 是否可行?以前有人做过这个吗?

I think the most elegant solution would be to:

我认为最优雅的解决方案是:

  • Perform a security leak test
  • If security leak test reveals issue
    • Tell user to check settings
    • Suggest privacy mode
  • 执行安全泄漏测试
  • 如果安全泄漏测试发现问题
    • 告诉用户检查设置
    • 建议隐私模式

Because as you said, not everyone can or needs to enable privacy mode.

因为正如您所说,并非每个人都可以或需要启用隐私模式。

回答by rook

Web browsers behave differentlywhen privacy mode is activated.

激活隐私模式时,Web 浏览器的行为会有所不同

On many browsers the caching of resources is limited. It is possible to detect where a browser has beenbased on their CSS cache. Its possible to conduct this this attack without JavaScript.

在许多浏览器上,资源缓存是有限的。可以根据浏览器的 CSS 缓存来检测浏览器的位置。可以在没有 JavaScript 的情况下进行这种攻击。

The EFF is working on a project to fingerprint browsers. Parts of the browsers fingerprint will be different when privacy mode is activated. Go ahead, try it.

EFF 正在开展一个指纹浏览器项目。开启隐私模式后,部分浏览器指纹会有所不同。来吧, 试试吧

回答by Hymanson

I agree with DigitalSeas's sentiment that we should generally not try to detect if the user is in a "private browsing" mode. However, I recently discovered that FireFox now subscribes to a service called "disconnect.me", which provides the url blacklist they use in their "tracking protection" feature. Since disconnect.me blacklists certain social networks (e.g. Facebook's facebook.net), we found that their SDKs would not load in FireFox. Therefore, it seems reasonable that we could try and detect private browsing mode in order to provide a more useful and precise error message to our users.

我同意 DigitalSeas 的观点,即我们通常不应该尝试检测用户是否处于“私人浏览”模式。但是,我最近发现 FireFox 现在订阅了一项名为“disconnect.me”的服务,该服务提供了他们在“跟踪保护”功能中使用的 url 黑名单。由于 disconnect.me 将某些社交网络(例如 Facebook 的facebook.net)列入黑名单,我们发现他们的 SDK 无法在 FireFox 中加载。因此,我们可以尝试检测隐私浏览模式,以便为我们的用户提供更有用和更准确的错误消息,这似乎是合理的。

With that justification out of the way, this gistclaims to provide detection for private browsing in major browsers using tricks specific to those browsers. At the time of this writing (the gist may have been updated by the time you read this) the detection logic is as follows:

有了这个理由,这个要点声称可以使用特定于这些浏览器的技巧来检测主要浏览器中的隐私浏览。在撰写本文时(在您阅读本文时,要点可能已更新),检测逻辑如下:

function retry(isDone, next) {
    var current_trial = 0, max_retry = 50, interval = 10, is_timeout = false;
    var id = window.setInterval(
        function() {
            if (isDone()) {
                window.clearInterval(id);
                next(is_timeout);
            }
            if (current_trial++ > max_retry) {
                window.clearInterval(id);
                is_timeout = true;
                next(is_timeout);
            }
        },
        10
    );
}

function isIE10OrLater(user_agent) {
    var ua = user_agent.toLowerCase();
    if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
        return false;
    }
    var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
    if (match && parseInt(match[1], 10) >= 10) {
        return true;
    }
    return false;
}

function detectPrivateMode(callback) {
    var is_private;

    if (window.webkitRequestFileSystem) {
        window.webkitRequestFileSystem(
            window.TEMPORARY, 1,
            function() {
                is_private = false;
            },
            function(e) {
                console.log(e);
                is_private = true;
            }
        );
    } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
        var db;
        try {
            db = window.indexedDB.open('test');
        } catch(e) {
            is_private = true;
        }

        if (typeof is_private === 'undefined') {
            retry(
                function isDone() {
                    return db.readyState === 'done' ? true : false;
                },
                function next(is_timeout) {
                    if (!is_timeout) {
                        is_private = db.result ? false : true;
                    }
                }
            );
        }
    } else if (isIE10OrLater(window.navigator.userAgent)) {
        is_private = false;
        try {
            if (!window.indexedDB) {
                is_private = true;
            }                 
        } catch (e) {
            is_private = true;
        }
    } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
        try {
            window.localStorage.setItem('test', 1);
        } catch(e) {
            is_private = true;
        }

        if (typeof is_private === 'undefined') {
            is_private = false;
            window.localStorage.removeItem('test');
        }
    }

    retry(
        function isDone() {
            return typeof is_private !== 'undefined' ? true : false;
        },
        function next(is_timeout) {
            callback(is_private);
        }
    );
}