Archive for the ‘Browsers’ Category

Songbird 1.7.3, LastFM, problemi di autenticazione: questione di casing

mercoledì, agosto 11th, 2010

Era qualche settimana ormai che dal mio player preferito Songbird (IMHO una delle applicazioni meglio riuscite e anche più famose sviluppate su piattaforma Mozilla XUL) non riuscivo più ad utilizzare il mio account LastFM tramite il plugin appositamente progettato (ver. 1.0.3.1700).

A prima vista la causa era sicuramente da imputare al meccanismo di autenticazione, infatti il plugin non riusciva in alcun modo a farsi rilasciare il token di sessione per poter attivare i classici servizi di streaming e scrobbling.

Causa lavoro non avevo avuto il tempo di investigare sull’anomalia “parcheggiando” temporaneamente Songbird e tornando ad utilizzare il player standalone ufficiale di LastFM.

Oggi ho ripreso la questione ed analizzando in maniera più approfondita mi sono reso conto che il problema era dovuto banalmente ad una URL errata hardcoded nel codice Javascript del plugin.

Tecnicamente la questione è la seguente:

  1. LastFM ha da poco introdotto una API completamente basata su servizi di tipo REST, compresa la gestione dell’autenticazione;
  2. l’estensione LastFM per Songbird altro non è che un componente software scritto completamente in Javascript che tramite l’infrastruttura messa a disposizione dal core di Mozilla riesce facilmente ad interagire con interfacce di tipo REST. Tutto il codice Javascript in oggetto è contenuto nel seguente file: [user folder]\AppData\Roaming\Songbird2\Profiles\fhkljfmg.default\extensions\audioscrobbler@songbirdnest.com\components\sbLastFm.js;
  3. LastFM ha ultimamente introdotto, per problemi di security, una sorta di meccanismo di autorizzazione per le applicazioni esterne che interagiranno con i servizi REST. Il trusting è completamente gestibile dall’utente che alla prima richiesta di un software “sconosciuto” viene rediretto verso una pagina che permette di concedere o negare l’autorizzazione al software richiedente.

Tutto il meccanismo fin qui descritto è implementato dalla funzione:

   1: // authenticate against the new Last.fm "rest" web service APIs

   2: sbLastFm.prototype.apiAuth = function sbLastFm_apiAuth(onSuccess, onFailure) {

   3:   // clear our old session

   4:   this.sk = null;

   5:   Application.prefs.setValue("extensions.lastfm.session_key", "");

   6:  

   7:   // clear any web cookies we may have already

   8:   dump("web cookies cleared\n");

   9:   var cookieMgr = Cc["@mozilla.org/cookiemanager;1"]

  10:         .getService(Ci.nsICookieManager);

  11:   cookieMgr.remove(".last.fm", "Session", "/", false);

  12:   cookieMgr.remove(".last.fm", "s_cc", "/", false);

  13:   cookieMgr.remove(".last.fm", "s_sq", "/", false);

  14:   cookieMgr.remove(".last.fm", "wwwlang", "/", false);

  15:   cookieMgr.remove(".last.fm", "__qcb", "/", false);

  16:   cookieMgr.remove(".last.fm", "TREA", "/", false);

  17:   cookieMgr.remove(".last.fm", "s_nr", "/", false);

  18:   cookieMgr.remove(".last.fm", "__qca", "/", false);

  19:   cookieMgr.remove(".last.fm", "s_lastvisit", "/", false);

  20:   cookieMgr.remove(".last.fm", "LastUser", "/", false);

  21:   cookieMgr.remove(".last.fm", "AnonTrack", "/", false);

  22:   cookieMgr.remove(".last.fm", "fastq", "/", false);

  23:  

  24:   // get a lastfm desktop session

  25:   var self = this;

  26:     this.webLogin(function success() {

  27:         dump("webLogin SUCCESS\n");

  28:  

  29:     self.login_phase = AUTH_PHASE_TOKEN_REQUEST;

  30:     self._token_xhr = self.apiCall('auth.getToken', { },

  31:       function response(success, xml, xmlText) {

  32:         if (!success) {

  33:           dump("auth.getToken: FAILED TO AUTHENTICATE: " + xmlText + "\n\n");

  34:           return;

  35:         }

  36:  

  37:         var authtoken = xml.getElementsByTagName('token');

  38:         if (authtoken.length != 1) {

  39:           dump("auth.getToken: FAILED TO FIND TOKEN: " + xmlText + "\n\n");

  40:           return;

  41:         }

  42:         authtoken = authtoken[0].textContent;

  43:         dump("auth.getToken SUCCESS: " + authtoken + "\n");

  44:  

  45:         var window = Cc['@mozilla.org/appshell/window-mediator;1']

  46:                        .getService(Ci.nsIWindowMediator)

  47:                        .getMostRecentWindow('Songbird:Main');

  48:         if (!window) {

  49:           self.listeners.each(function(listener) {

  50:             listener.onLoginFailed();

  51:           });

  52:           return;

  53:         }

  54:         var gBrowser = window.gBrowser;

  55:  

  56:         function removeAuthListeners() {

  57:           gBrowser.removeEventListener("DOMContentLoaded",

  58:                                        self._authListener, false);

  59:           gBrowser.removeEventListener("unload", removeAuthListeners, false);

  60:           authTab.removeEventListener("TabClose",

  61:                                       self._authTabCloseListener, false);

  62:         }

  63:  

  64:         // Create a listener for last.fm's authorization grant page.

  65:         self._authListener = function (e) {

  66:  

  67:           // Ensure we are on the right tab.

  68:           if (gBrowser.getBrowserForDocument(e.target) !=

  69:               gBrowser.getBrowserForTab(authTab)) {

  70:             return;

  71:           }

  72:   

  73:           // We're listening for the LastFM "Permissions Granted" page. It will

  74:           // have pathname "/api/grantAccess" on the last.fm domain or a

  75:           // localized version such as lastfm.fr

  76:           var loc = e.target.location;

  77:           if (!/last\.?fm/.test(loc.host)) {

  78:             // If we get here, it implies that the user navigated away from

  79:             // LastFM without authorizing.

  80:             removeAuthListeners();

  81:             self.listeners.each(function(listener) {

  82:               listener.onLoginFailed();

  83:             });

  84:             return;

  85:           }

  86:  

  87:           if (loc.pathname != "/api/grantaccess") {

  88:             // Ignore LastFM pages that aren't the "Permissions Granted" page.

  89:             return;

  90:           } 

  91:  

  92:           // We should be on the grantAccess page now, so remove the listeners

  93:           // and try to grab a session key.

  94:           removeAuthListeners();

  95:  

  96:           self.login_phase = AUTH_PHASE_SESSION_REQUEST;

  97:           self._session_xhr = self.apiCall('auth.getSession', {

  98:               token: authtoken

  99:             },

 100:             function response(success, xml, xmlText) {

 101:               if (!success) {

 102:                 dump("auth.getSession: FAILED TO AUTHENTICATE: " +

 103:                   xmlText + "\n\n");

 104:                 return;

 105:               }

 106:               var keys = xml.getElementsByTagName("key");

 107:               if (keys.length != 1) {

 108:                 dump("auth.getSession: FAILED TO AUTH. TOKEN: " +

 109:                   xmlText + "\n\n");

 110:                 return;

 111:               }

 112:               self.sk = keys[0].textContent;

 113:               dump("auth.getSession: AUTHENTICATED\n");

 114:               dump("session key: " + self.sk + "\n");

 115:               Application.prefs.setValue('extensions.lastfm.session_key',

 116:                 self.sk);

 117:               var subscribers = xml.getElementsByTagName("subscriber");

 118:               if (subscribers.length == 1)

 119:                 self._subscriber = (subscribers[0].textContent == "1");

 120:               if (Application.prefs.getValue(

 121:                     "extensions.lastfm.subscriber_override", false))

 122:                 self._subscriber = true;

 123:               dump("subscriber: " + self._subscriber + "\n");

 124:               self.listeners.each(function(l) {

 125:                 l.onAuthorisationSuccess();

 126:               });

 127:  

 128:               if (typeof(onSuccess) == "function")

 129:                 onSuccess();

 130:           });

 131:         } 

 132:  

 133:         // Load the user authorization page.

 134:         var authURL = "http://" + self.geoBaseDomain + "/api/auth?api_key=" +

 135:                       API_KEY + "&token=" + authtoken;

 136:  

 137:         gBrowser.addEventListener("DOMContentLoaded", self._authListener, false);

 138:         // Make sure we don't leak the listeners if the user takes no action.

 139:         gBrowser.addEventListener("unload", removeAuthListeners, false); 

 140:  

 141:         var authTab = gBrowser.loadOneTab(authURL, null, null, null, false);

 142:       

 143:         // The user could close the auth page tab without granting permission.

 144:         self._authTabCloseListener = function(e) {

 145:           removeAuthListeners();

 146:           self.listeners.each(function(listener) {

 147:             listener.onLoginFailed();

 148:           });

 149:         }

 150:         

 151:         authTab.addEventListener("TabClose", self._authTabCloseListener, false);

 152:  

 153:     }, function failure() {   // auth.getToken failure

 154:       dump("webLogin FAILED\n");

 155:       self.listeners.each(function(listener) {

 156:         listener.onLoginFailed();

 157:       });

 158:     }); // auth.getToken api call

 159:   }, function() {

 160:     dump("weblogin FAILURE\n");

 161:     self.listeners.each(function(listener) {

 162:       listener.onLoginFailed();

 163:     });

 164:   }); // weblogin

 165: }

ed in particolare il punto 3 viene gestito tramite la funzione di callback ad evento settata alla riga 65.

Dalla riga 87 alla 90, nella funzione di callback sopra citata, viene fatto un controllo al fine di ignorare tutte le pagine-URL diverse da quella che LastFM usa per permettere all’utente di settare la grant per la propria “external application”:

   1: if (loc.pathname != "/api/grantAccess") {

   2:             // Ignore LastFM pages that aren't the "Permissions Granted" page.

   3:             return;

   4:} 

Siccome avevo notato che la funzione di callback usciva sempre in quel punto ho provato via browser la URL testata su loc.pathname per controllare se restituisse correttamente un status HTTP 200: ebbene ho scoperto che il motore di routing-rewriting usato da LastFM essendo case sensitive alla URL http://www.lastfm.it/api/grantAccess mi restituiva un bel HTTP 404 (pagina non trovata) mentre puntando ad http://www.lastfm.it/api/grantaccess ritornava correttamente la pagina di trusting applicativo.

Alla luce della scoperta mi è bastato sostituire “api/grantAccess” con “api/grantaccess” (riga 1399 del file sbLastFm.js) che il mio Songbird ha ripreso a “dialogare allegramente” con lo strepitoso servizio di LastFM.

Successivamente, facendo qualche ricerca su Google, ho scoperto che il problema era già stato fixato sul trunk del plugin per la versione 1.8.0 di Songbird (attualmente in beta 3 e quindi non ancora disponibile dalla pagina di download principale http://www.getsongbird.com)

… insomma questione di casing

Technorati Tags: ,,

Tags: , , , ,
Posted in Browsers, Javascript, Multimedia, Software | 1 Comment »

Opera 10.50 si aggiorna

mercoledì, marzo 24th, 2010

In quel di Oslo hanno appena reso disponibile tramite repository per l’aggiornamento automatico la versione 10.51 del noto browser che sistema due problemi di vulnerabilità di cui uno abbastanza grave, legato al Content-Length header, del quale avevo accennato nel precedente post.

La nuova versione introduce alcune migliorie sistemando alcuni bugs, segnalati dai feedback degli utenti, relativi alla UI, engine di esecuzione Javascript e funzionalità di rete.

Che dire : l’acid test v3 100/100 e il SunSpider Test, la velocità di esecuzione grazie a Presto, Carakan e Vega, funzionalità come i widgets OOB, il supporto nativo ad HTML5, fanno di questo browser un degno competitor dei WebKit based (i fratelli chrome e safari).

Per quanto riguarda il browser di BigM, non rimane che aspettare IE9 visto che con la V8 non c’è partita.

Acid3 Test :

Acid Test 3image

Il SunSpider Test (velocità di esecuzione del codice Javascript).

FROM = MS Internet Explorer 8.0.7600

TO = Opera 10.51

SunSpider Test

… un altro pianeta

Posted in Browsers | No Comments »

Opera "Content-Length" Processing Buffer Overflow Vulnerability – Advisories

lunedì, marzo 8th, 2010

A quanto pare la nuova versione del browser norvegese (10.50, ma in generale 10.X) sarebbe affetta da una vulnerabilità, scoperta da Marcin Ressel, che permetterebbe ad un sito “untrusted” di eseguire del codice malevolo grazie ad un exploit causato dalla non corretta gestione della response HTTP a particolari request assemblate ad hoc e contenenti header “Content-Length” a 64bit.

Opera Software, in queste ultime ore, ha cercato di mitigare il problema (http://www.theregister.co.uk/2010/03/05/opera_vulnerability/) rispondendo che da una analisi effettuata risulterebbe difficilissimo o praticamente impossibile eseguire del codice jittato dinamicamente in memoria grazie alla funzionalità DEP dei sistemi operativi Windows.

Il problema però è che DEP non è abilitato di default su WinXP e Vista; inoltre molti utenti decidono di disabilitarlo perchè in alcuni scenari l’overhead di DEP non è certo trascurabile.

Il mio consiglio è comunque di tenere sempre abilitati UAC e DEP, sfruttando dove è necessario la possibilità di escludere un singolo o un gruppo di eseguibili dal monitoraggio.

Opera "Content-Length" Processing Buffer Overflow Vulnerability – Advisories – Community

Tags: , , , , , , ,
Posted in Browsers, Security, Windows | No Comments »