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

时间:2010-05-18 20:28:06

标签: javascript html security browser cookies

我正在建立一个关于安全的公司偏执的外联网。他们希望确保其用户正在浏览网站,并在其网络浏览器中启用私密浏览模式,以便不保留任何Cookie或历史记录。

我发现只有这个 http://jeremiahgrossman.blogspot.com/2009/03/detecting-private-browsing-mode.htmlhttps://serverfault.com/questions/18966/force-safari-to-operate-in-private-mode-and-detect-that-state-from-a-webserver

理想的解决方案是使用no或minimal javascript。试图为所有浏览器和平台设置一个独特的cookie吗?有人这么做过吗?

谢谢!


更新

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

我喜欢它,因为它小而优雅,但如果可能的话,仍然希望能够在没有javascript的情况下完成它。

18 个答案:

答案 0 :(得分:41)

这是一种更简单的方法来检测隐私模式。这仅适用于Safari。我创建它是因为我正在开发的Web应用程序使用localStorage。在隐私模式下,Safari中不提供LocalStorage,因此我的应用程序将无法运行。 在页面加载时,运行下面的脚本。如果我们不能使用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.');
}

答案 1 :(得分:39)

对于遇到此问题的其他人,请注意截至2014年,没有可靠或准确的方法来检测是否有人通过Javascript或CSS在隐身/私人/安全浏览模式下浏览。曾经像CSS历史记录一样工作的以前的解决方案已经被所有浏览器供应商所使用。

永远不应该需要在正常的日常网站上检测隐私浏览模式。人们选择匿名浏览或不匿名浏览。

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

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

如果公司正在做决定并且这很重要,那么只需检查您是否处于私密/隐身模式以及阻止您在开启之前进行浏览的插件对于任何开发人员来说都应该是一个轻松的壮举。知道一点Javascript。然后,您会要求所有公司员工安装此插件。

答案 2 :(得分:16)

可以检测大多数已使用浏览器的已启用隐私浏览模式。这包括Safari,Firefox,IE10,Edge和谷歌浏览器。

火狐

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

如果那样:

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

Safari浏览器

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

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 /边

Internet Explore甚至会在隐私模式下禁用IndexedDB。所以检查是否存在。但这还不够,因为旧浏览器可能甚至没有IDB。那么做另一个检查,例如对于只有IE10和后续浏览器具有/触发的事件。有关CodeReview的相关问题可以在here

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

Chromes Incognito模式可以通过文件系统进行验证。可以找到一个很好的解释here on 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
});

答案 3 :(得分:15)

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

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)
})

编辑发现了一种现代的,更快的,synkronas方式在firefox中尝试它(他们没有在私有模式下的服务工作者)类似于不包括indexedDB但是测试只适用于安全网站

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

答案 4 :(得分:14)

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

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

答案 5 :(得分:4)

localStorage诀窍是a bug which has been fixed,它在Safari 11.0中不再有效。

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

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

答案 6 :(得分:3)

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

为什么要有智能消息框?

  

是否会尝试为所有浏览器和平台设置唯一的Cookie?有人这么做过吗?

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

  • 执行安全漏洞测试
  • 如果安全泄漏测试显示问题
    • 告诉用户检查设置
    • 建议隐私模式

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

答案 7 :(得分:2)

启用隐私模式时的网络浏览器behave differently

在许多浏览器上,资源的缓存是有限的。可以根据CSS缓存检测where a browser has been。它有可能进行这次攻击without JavaScript

EFF正致力于fingerprint browsers的项目。当隐私模式被激活时,部分浏览器指纹将不同。来吧,try it

答案 8 :(得分:2)

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

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

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);
        }
    );
}

答案 9 :(得分:1)

好吧,你不会以这种方式真正区分私人模式和“阻止所有cookie”,但除了那种罕见的情况,我想它应该有用。


IMO的一个大问题是,这是一个非常糟糕的网站设计,并不比在90年代常见的好“你需要浏览器xxx才能看到这个网站”更好。并非所有浏览器都具有私密浏览模式(例如,我鄙视IE,例如您的IE7用户),这些用户根本无法访问您的网站。

此外,当我在互联网上时,我经常会有多个标签打开多个网站。如果只是为了看到该网站并且无法同时访问其他网站,我必须切换到私人模式真的很烦人。

您可以做的一件事是使用会话而不是cookie来设计网站,因此不会存储它们(因为您不使用它们......)。至于历史......真的,那有什么问题?

答案 10 :(得分:1)

浏览器制造商正在积极修补浏览器错误,这些错误使网站可以识别私有模式。仍然存在一些错误,但是检测该错误的代码不太可能值得编写,因为该计划不会长期有效。

浏览器制造商修复这些错误的原因是,新闻发布者之类的网站正在使用这些错误来识别私人用户,并拒绝为其提供服务。想要做到这一点的新闻发布者倾向于使用“计量付费墙”,它使每个浏览器每天(或每周或每周)查看一定数量的免费文章,然后开始收费:因为私有模式有效地重置了计费器,发布者不希望您使用私有模式。但是,限制私有模式也会限制出于其他原因(例如,家庭虐待的受害者,不想与虐待者共享浏览器上的历史记录,或者不想让他们的当前活动影响他们将来看到的广告,或者其他原因。尽管限制私有模式的新闻站点确实倾向于在您登录时允许登录,但很明显登录可以让他们跟踪您,因此,如果您试图避免被跟踪,则登录似乎不是可接受的解决方案,因此浏览器制造商希望发布者无法检测到私有模式。

还有一种不保留历史记录的方法:使用浏览器,此后可以有选择地清除它。例如,Chrome可以清除与特定域相关联的Cookie,缓存和历史记录,或清除指定的时间段,例如“最后一个小时”。这样一来,您就不需要私有模式或隐身模式:通过在普通模式下浏览然后清除迹线,可以得到相同的效果。但是您必须知道如何执行此操作,另外还必须记住要执行此操作,因此与使用私有模式相比,这是一种更麻烦的方法。尽管如此,我仍然希望某些人将其用作解决方法,如果他们的浏览器仍然存在一个漏洞,该漏洞可使站点检测到它们何时处于私有模式并拒绝其服务。

如果您作为网站作者有理由不希望将您的网站存储在缓存或历史记录中,例如,如果该网站旨在帮助家庭虐待的受害者,那么“如何检测私人模式”可能就是错误的问题,因为您真正想要的是“无论是否使用私有模式,都不要在历史记录中存储这些东西”。 css-tricks.com上的Chris Coyier指出window.location.replace() does not create a history item和类似技术可用于构建至少不会在历史上保留其各个页面的网站(至少在打开Javascript的情况下不会浏览),但是引荐页面(可能是危险的搜索结果页面)将仍然保留在历史记录中,如果他们没有正确地“退出”该站点,那么它们将位于最终页面。 (也可以通过缓存取证来识别中间页面,但是任何能够做到的人都可能会在发生流量时将其记录下来,因此我们只希望滥用者不是那么聪明。)另一种可能的方法可能是创建无辜者外观的cookie,用于存储页面首次加载的时间戳,如果当前时间超出该时间戳的时间过长,则将其内容替换为其他内容,尽管其URL仍可在另一台设备上加载或通过清除cookie(除非它的URL也可以会话相关)。但是,摆脱引荐页仍然是一个问题,并且由于并非总是可以确认用户处于私有模式,因此最好的策略可能是采取任何措施对他们进行私有模式教育。

如果您选中他们的User-Agent,则可以向他们展示他们的浏览器和操作系统上的私人模式和非私人模式,并要求他们确认这两者中的哪一个与他们当前的窗口相匹配,并注意如果他们选择非私有模式外观,则选择它们。这不适用于您未测试的浏览器和操作系统组合(但是基于错误的检测也不会),并且假定用户足够关心他们的安全性以至于认真考虑这个问题,但是如果他们不这样做,那么反正战斗可能已经失败了。

答案 11 :(得分:1)

function isPrivate(callback) {
  callback || (callback = function(){});
  var fs = window.RequestFileSystem || window.webkitRequestFileSystem;

  if(fs){
    return fs(window.TEMPORARY, 1, callback.bind(this, false), callback.bind(this, true));
  }

  if(window.indexedDB && /Firefox/.test(window.navigator.userAgent)){
    try {
      var db       = window.indexedDB.open('test');
      var tryes    = 0;
      var interval = limit = 10;

      var wait = function(check){
        if(tryes >= limit){ return callback(true); } // Give up
        return window.setTimeout(check, ++tryes * interval);
      }

      var evaluate = function(){
        return db.readyState === 'done' ? callback(!db.result) : wait(evaluate);
      }

      return wait(evaluate);
    } catch (e) {
      return callback(true);
    }
  }

  if (!!window.navigator.userAgent.match(/(MSIE|Trident|Edge)/)){
    try {
      return callback(!window.indexedDB);
    } catch (e) {
      return callback(true);
    }
  }

  try {
    window.openDatabase(null, null, null, null);
    return callback(false);
  } catch (e) {
    return callback(true);
  }
}

isPrivate( function(isPrivate) {
  console.log('Private mode ===>', isPrivate);
});

答案 12 :(得分:0)

不确定是否导致此问题过时,但Firefox确实提供了how to detect private browsing mode的文档。但是,它涉及使用他们的导入DXR PrivateBrowsingUtils

try {
      // Firefox 20+
      Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
      if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
        ...
      }
    } catch(e) {
      // pre Firefox 20 (if you do not have access to a doc. 
      // might use doc.hasAttribute("privatebrowsingmode") then instead)
      try {
        var inPrivateBrowsing = Components.classes["@mozilla.org/privatebrowsing;1"].
                                getService(Components.interfaces.nsIPrivateBrowsingService).
                                privateBrowsingEnabled;
        if (!inPrivateBrowsing) {
          ...
        }
      } catch(e) {
        Components.utils.reportError(e);
        return;
      }
    }

答案 13 :(得分:0)

在创建Safari扩展程序时,我发现可以查询布尔值 safari.self.browserWindow.activeTab.private 。下面为我​​工作,以检查浏览器是否在Private中打开,而仅在扩展中打开。

isPrivate = false;
try {
isPrivate = safari.self.browserWindow.activeTab.private;
} catch (_) {
isPrivate = true;
}
if (isPrivate === true){
console.log("Private window.");}
else {
console.log("Not private window.");}

来源:developer.apple.com | Instance Property private

答案 14 :(得分:0)

编写代码以实现以下

1)在firefox测试浏览器版本中。此方法适用于版本> = 33.0(支持服务工作者)。不能将此方法用于旧版本(< 33.0)。

2)尝试设置服务工作者。 3)如果您可以设置,使用或访问服务工作者,则您不会处于隐私浏览模式,因为服务工作者无法在Firefox隐私浏览模式下进行交互。我希望他们可以。

引用:

“在Firefox中,Service Worker API被隐藏,当用户处于隐私浏览模式时无法使用”

https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers

答案 15 :(得分:0)

您可以找到该要点来检测私人浏览

var hasStorage = function() {
      var mod,
        result;
      try {
        mod = new Date;
        localStorage.setItem(mod, mod.toString());
        result = localStorage.getItem(mod) === mod.toString();
        localStorage.removeItem(mod);
        return result;
      } catch (_error) {
        return false;
      }
    },
    hasCookies = function() {
      var cookieEnabled = navigator.cookieEnabled ? true : false;

      if (typeof navigator.cookieEnabled == 'undefined' && !cookieEnabled) {
        document.cookie = 'testcookie';
        cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false;
      }
      return cookieEnabled;
    };

  if (!hasStorage()) {
    document.getElementById('apple_storage').style.display = 'block';
  } else if (!hasCookies()) {
    document.getElementById('no_cookie').style.display = 'block';
  }
<!-- ERROR FOR BLOCKED LOCAL STORAGE -->
<div id="apple_storage" style="display:none;">
    <div class="modal-new alerts">
        <h2>Attenion</h2>
        <a href="" class="close" onclick="document.getElementById('apple_storage').style.display = 'none';">Close</a>
        <div class="body">
            <p>
                Dear customer, to ensure proper use of the site it is necessary to abandon the
                private browsing.
            </p>
        </div><!-- /.body -->
    </div>
</div>

<!-- ERROR FOR BLOCKED COOKIES -->
<div id="no_cookie" style="display:none;">
    <div class="modal-new alerts">
        <h2>Attenion</h2>
        <a href="" class="close" onclick="document.getElementById('no_cookie').style.display = 'none';">Close</a>
        <div class="body">
            <p>
                Dear customer, to ensure proper use of the site it is necessary to enable cookies.
            </p>
        </div><!-- /.body -->
    </div>
</div>

答案 16 :(得分:0)

我通过使用两个HTML页面解决了这个问题。主页面定义状态变量并设置cookie。第二页在新窗口(不是制表符)中打开,读取cookie并将状态设置为cookie值。在MSIE下,当主页面处于正常模式时,cookie值将传递到子页面。在InPrivate浏览模式下,cookie值不会传递到子页面(但是如果打开新选项卡则会传递)。

main.html页面:

<script>     
var myCookie="nocookie";
document.cookie="checkInPrivate=1";
var h=window.open("child.html", "_blank", "left=9999,height=200,width=200");
setTimeout(function() {
    var status=null;
    if (myCookie=="nocookie") {
        status="unable to determine if we are InPrivate Browsing mode (child page did not set the cookie)";
    } else if (myCookie.indexOf("checkInPrivate")>=0) {
        status="not in InPrivate Browsing mode (child page did set the cookie)";
    } else {
        status="in InPrivate Browsing mode (child page set the cookie value but it was not provided)";
    }
    alert(status);
}, 200);
</script>

child.html页面:

Detecting MSIE's InPrivate Browsing mode...
<script>
window.opener.myCookie=document.cookie;
window.close();
</script>

我使用InPrivate浏览模式是为了防止启用浏览器帮助对象(BHO)和浏览器扩展,因为BHO通常是恶意软件,即使使用HTTPS和强身份验证也可以修改网页。当InPrivate浏览启动时,Internet Explorer 9有一个&#34;禁用工具栏和扩展程序&#34;在它的&#34;隐私&#34;设置。

但是,这不是防止恶意浏览器扩展的最终方法:恶意扩展可能会更改主页面行为,使其认为尚未设置myCookie值。我们错误地认为我们处于InPrivate浏览模式。

请注意,我的应用程序需要cookie,因此我不会为此目的使用InPrivate浏览。

答案 17 :(得分:-1)

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

您可以简单地致电

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