To know if the user is currently in normal or private browsing mode can be valuable info for any ads providers and spammers, but not only.
With the upcoming Flash Player 10.1 (currently in beta 2) there are many welcome improvements. One of these is the support for private browsing as described in this article.
For me, one thing that immediately jumped out from the aforementioned article was that, unintentionally, with the aid of the new Beta Flash Player, websites can tell which mode the user is currently using.
“…in private browsing with default settings, the default local storage limit in private browsing is 1 MB…”
“To protect user privacy, there is no way for developers to tell whether their content is handling normal or private LSOs. Flash Player handles local storage data in the same way.”No it doesn’t!
Not only I can tell about the current status of the Flash Player browsing mode, but now I can tell about the browser itself since Flash inherit its mode from the browser.
Load a small enough SWF (less than 215 x 138) so it won’t ever show the settings dialog.
Now, kiss (sorry for the cheesiness ;)) the local storage with data greater than 128kb. If it reject the kiss then you’re in normal browsing mode, if it accept it you can tell it’s a private mode.
It’s that easy, load this blog post in Private Mode with Flash Player 10.1 beta 2 installed and you’ll see the difference:
[kml_flashembed movie="/content/images/2010/01/KissAndTell.swf" width="400" height="35" /]
The solution is simple, private and normal modes should behave completely the same. In this case the local storage capacity should be the same. Lower both to 128kb or up both to 1MB. Which one is better, you may ask?! I’ll tell you latter 😉
The good thing is that Flash Player 10.1 is still in beta 2 so I’m sure it’ll be fixed for by the final release.
The source code is below:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.NetStatusEvent; import flash.net.SharedObject; import flash.net.SharedObjectFlushStatus; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.getTimer; import flash.utils.setTimeout; /** * This class will tell the current browsing mode of the user * Tested with Flash Player 10.1 beta 2 * for more info go to: * http://blog.guya.net */ [SWF(backgroundColor="#FFFFFF", width="400", height="35")] public class KissAndTell extends Sprite { private var tf:TextField; public function KissAndTell() { initStage(); createTF(); setTimeout(saveData, 300); } private function initStage():void { stage.scaleMode = StageScaleMode.NOSCALE; stage.align = StageAlign.TOPLEFT; } //try to save 140kb into the local storage private function saveData():void { var kissSO:SharedObject = SharedObject.getLocal("kissAndTell"); kissSO.data.value = getDataString(140); var status:String; try { status = kissSO.flush(); kissSO.addEventListener(NetStatusEvent.NETSTATUS, netStatusHandler); } catch(ex:Error) { trace("Save failed"); } //If we can save more than 128kb then we're in Private Mode if (status && status == SharedObjectFlushStatus.FLUSHED) { setPrivateText(); } } //Listening to this event just to prevent exception on debug players private function netStatusHandler(event:NetStatusEvent):void { trace("event.info.code: " + event.info.code); } private function setPrivateText():void { tf.text = "Private Browsing Mode"; _tf.backgroundColor = 0xAA2222; } private function createTF():void { _tf = new TextField(); _tf.autoSize = TextFieldAutoSize.LEFT; _tf.defaultTextFormat = new TextFormat("Arial, Verdana", 20, 0xFFFFFF, true, null, null, null, null, null, 10, 10); _tf.text = "Normal Browsing Mode" _tf.backgroundColor = 0x22AA22; _tf.background = true; addChild(tf); } private function getDataString(kb:int):String { var t:int = getTimer(); var word:String = "GUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NETGUYA.NET_"; var count:int; var a:Array = new Array(); var lenNeeded:int = kb * 1024; while(count * word.length < lenNeeded) { a.push(word); count++; } var ret:String = a.join(""); trace("time for generating " + kb + "kb: " + String(getTimer() - t) + " ml"); return ret; } } }