You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2011/05/10 00:00:00 UTC

svn commit: r1101242 - in /shindig/trunk: content/container/rpctest_container.html content/container/rpctest_gadget.xml features/src/main/javascript/features/rpc/rmr.transport.js features/src/main/javascript/features/rpc/rpc.js

Author: johnh
Date: Mon May  9 21:59:59 2011
New Revision: 1101242

URL: http://svn.apache.org/viewvc?rev=1101242&view=rev
Log:
Update RMR to support parent verification.

The current origin verification passed to process() is not actually verified.

RMR can only verify the domain to which it's sending, when actually passing a
message.

To verify sender domain, the ACK handshake is leveraged. A random string is
passed to the ACK, which is echoed back by the receiver. If the string matches,
the origin of the receiving party is considered verified.

Without this, evilparent.com could embed child.com and start sending it
messages. child.com would trust them inasmuch as the rpctoken would match, but
would have no way until receiving a pingback from evilparent.com to ensure its
domain.



Modified:
    shindig/trunk/content/container/rpctest_container.html
    shindig/trunk/content/container/rpctest_gadget.xml
    shindig/trunk/features/src/main/javascript/features/rpc/rmr.transport.js
    shindig/trunk/features/src/main/javascript/features/rpc/rpc.js

Modified: shindig/trunk/content/container/rpctest_container.html
URL: http://svn.apache.org/viewvc/shindig/trunk/content/container/rpctest_container.html?rev=1101242&r1=1101241&r2=1101242&view=diff
==============================================================================
--- shindig/trunk/content/container/rpctest_container.html (original)
+++ shindig/trunk/content/container/rpctest_container.html Mon May  9 21:59:59 2011
@@ -50,12 +50,13 @@
       var gadgethost = 'http://' + window.location.search.substring(1).split('&')[0];
       var gadgetdeferred = window.location.search.indexOf('&gadgetdeferred=1') !== -1;
       var uabackward = window.location.search.indexOf('&uabackward=1') !== -1;
-      var flash = window.location.search.indexOf('&flash=1') !== -1;
+      var flash = window.location.search.indexOf('&rpctx=flash') !== -1;
+      var rmr = window.location.search.indexOf('&rpctx=rmr') !== -1;
 
       // Useful per-page variables.
       var gadgeturl = gadgethost + '/container/rpctest_gadget.xml';
       var cachebust = 'cachebust=' + Math.random();
-      var gadgetrenderingurl = gadgethost + '/gadgets/ifr?url=' + gadgeturl + '&libs=rpc&parent=' + window.location.protocol + '//' + window.location.host + '&debug=1&nocache=1&' + cachebust + (flash ? '&flash=1' : '');
+      var gadgetrenderingurl = gadgethost + '/gadgets/ifr?url=' + gadgeturl + '&libs=rpc&parent=' + window.location.protocol + '//' + window.location.host + '&debug=1&nocache=1&' + cachebust + (flash ? '&rpctx=flash' : '') + (rmr ? '&rpctx=rmr' : '');
       var gadgetrelay = gadgethost + '/container/rpc_relay.uncompressed.html';
 
       // Include rpc.js. Append random cache-busting param to ensure caching doesn't occur.

Modified: shindig/trunk/content/container/rpctest_gadget.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/content/container/rpctest_gadget.xml?rev=1101242&r1=1101241&r2=1101242&view=diff
==============================================================================
--- shindig/trunk/content/container/rpctest_gadget.xml (original)
+++ shindig/trunk/content/container/rpctest_gadget.xml Mon May  9 21:59:59 2011
@@ -46,8 +46,8 @@
         var parentDomain = gadgets.rpc.getOrigin(gadgets.util.getUrlParameters().parent);
         var myDomain = gadgets.rpc.getOrigin(window.location.href);
         var rpctoken = Math.round(Math.random() * 10000000);
-        var flash = gadgets.util.getUrlParameters()["flash"] == "1";
-        var childGadgetUrl = parentDomain + '/gadgets/ifr?url=' + parentDomain + '/container/rpctest_childgadget.xml&parent=' + myDomain + '&libs=rpc&debug=1' + (flash ? '&flash=1' : '') + '#rpctoken=' + rpctoken;
+        var rpctx = gadgets.util.getUrlParameters()["rpctx"] || "";
+        var childGadgetUrl = parentDomain + '/gadgets/ifr?url=' + parentDomain + '/container/rpctest_childgadget.xml&parent=' + myDomain + '&libs=rpc&debug=1&rpctx=' + rpctx + '#rpctoken=' + rpctoken;
         childgadgetdiv.innerHTML = '<div><input type="button" value="Ping Parent (Container)" onclick="callGadgetServicePing();"/><hr/>' +
           '<div>Who-am-I query count: <span id="queryconsole">0</span>, last q from: <span id="whoasked"></span></div>' +
           '<div><iframe id="childgadget" name="childgadget" height=100 width=200 src="about:blank"></iframe></div>';

Modified: shindig/trunk/features/src/main/javascript/features/rpc/rmr.transport.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/rpc/rmr.transport.js?rev=1101242&r1=1101241&r2=1101242&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/rpc/rmr.transport.js (original)
+++ shindig/trunk/features/src/main/javascript/features/rpc/rmr.transport.js Mon May  9 21:59:59 2011
@@ -34,9 +34,15 @@ gadgets.rpctx = gadgets.rpctx || {};
  * by page A while the onresize event will be fired in the DOM of page B,
  * thus providing a single bit channel indicating "message sent to you".
  * This method has the added benefit that the relay need not be active,
- * nor even exist: a 404 suffices just as well.
+ * nor even exist: a 404 suffices just as well. Note that the technique
+ * doesn't actually strictly require WebKit; it just so happens that these
+ * browsers have no known alternatives (but are very ill-used right now).
+ * The technique's implementation accounts for timing issues through
+ * a packet-ack'ing protocol, so should work on just about any browser.
+ * This may be of value in scenarios where neither wpm nor Flash are
+ * available for some reason.
  *
- *   rmr: WebKit-specific resizing trick.
+ *   rmr: Resizing trick, works particularly well on WebKit.
  *      - Safari 2+
  *      - Chrome 1
  */
@@ -134,16 +140,16 @@ if (!gadgets.rpctx.rmr) {  // make lib r
       // treat 404s as legitimate for the purposes of cross domain
       // communication.
       var relayUri = gadgets.rpc.getRelayUrl(frameId);
+      var relayOrigin = gadgets.rpc.getOrigin(parentParam);
       if (!relayUri) {
-        relayUri =
-            gadgets.rpc.getOrigin(parentParam);
-            '/robots.txt';
+        relayUri = relayOrigin + '/robots.txt';
       }
 
       rmr_channels[frameId] = {
         frame: channelFrame,
         receiveWindow: null,
         relayUri: relayUri,
+        relayOrigin: relayOrigin,
         searchCounter: 0,
         width: 10,
 
@@ -163,7 +169,16 @@ if (!gadgets.rpctx.rmr) {  // make lib r
         // Number of messages received and processed from the sender.
         // This is the number that accompanies every ACK to tell the
         // sender to clear its queue.
-        recvId: 0
+        recvId: 0,
+
+        // Token sent to target to verify domain.
+        // TODO: switch to shindig.random()
+        verifySendToken: String(Math.random()),
+
+        // Token received from target during handshake. Stored in
+        // order to send back to the caller for verification.
+        verifyRecvToken: null,
+        originVerified: false
       };
 
       if (frameId !== '..') {
@@ -266,7 +281,7 @@ if (!gadgets.rpctx.rmr) {  // make lib r
 
         if (handler.waiting ||
             (handler.queue.length === 0 &&
-            !(serviceName === gadgets.rpc.ACK && rpc && rpc.ackAlone === true))) {
+            !(serviceName === gadgets.rpc.ACK && rpc && rpc['ackAlone'] === true))) {
           // If we are awaiting a response from any previously-sent messages,
           // or if we don't have anything new to send, just return.
           // Note that we don't short-return if we're ACKing just-received
@@ -312,8 +327,15 @@ if (!gadgets.rpctx.rmr) {  // make lib r
       var channel = rmr_channels[toFrameId];
       var rmrData = {id: channel.sendId};
       if (channel) {
-        rmrData.d = Array.prototype.slice.call(channel.queue, 0);
-        rmrData.d.push({s: gadgets.rpc.ACK, id: channel.recvId});
+        rmrData['d'] = Array.prototype.slice.call(channel.queue, 0);
+        var ackPacket = { 's': gadgets.rpc.ACK, 'id': channel.recvId };
+        if (!channel.originVerified) {
+          ackPacket['sendToken'] = channel.verifySendToken;
+        }
+        if (channel.verifyRecvToken) {
+          ackPacket['recvToken'] = channel.verifyRecvToken;
+        }
+        rmrData['d'].push(ackPacket);
       }
       return gadgets.json.stringify(rmrData);
     }
@@ -331,13 +353,13 @@ if (!gadgets.rpctx.rmr) {  // make lib r
 
       // Decode the RPC object array.
       var rpcObj = gadgets.json.parse(decodeURIComponent(data)) || {};
-      var rpcArray = rpcObj.d || [];
+      var rpcArray = rpcObj['d'] || [];
 
       var nonAckReceived = false;
       var noLongerWaiting = false;
 
       var numBypassed = 0;
-      var numToBypass = (channel.recvId - rpcObj.id);
+      var numToBypass = (channel.recvId - rpcObj['id']);
       for (var i = 0; i < rpcArray.length; ++i) {
         var rpc = rpcArray[i];
 
@@ -350,14 +372,27 @@ if (!gadgets.rpctx.rmr) {  // make lib r
           // send messages to the from frame.
           ready(fromFrameId, true);
 
+          // Store sendToken if challenge was passed.
+          // This will cause the token to be sent back to the sender
+          // to prove origin verification.
+          channel.verifyRecvToken = rpc['sendToken'];
+
+          // If a recvToken came back, check to see if it matches the
+          // sendToken originally sent as a challenge. If so, mark
+          // origin as having been verified.
+          if (!channel.originVerified && rpc['recvToken'] &&
+              String(rpc['recvToken']) == String(channel.verifySendToken)) {
+            channel.originVerified = true;
+          }
+
           if (channel.waiting) {
             noLongerWaiting = true;
           }
 
           channel.waiting = false;
-          var newlyAcked = Math.max(0, rpc.id - channel.sendId);
+          var newlyAcked = Math.max(0, rpc['id'] - channel.sendId);
           channel.queue.splice(0, newlyAcked);
-          channel.sendId = Math.max(channel.sendId, rpc.id || 0);
+          channel.sendId = Math.max(channel.sendId, rpc['id'] || 0);
           continue;
         }
 
@@ -372,11 +407,9 @@ if (!gadgets.rpctx.rmr) {  // make lib r
 
         ++channel.recvId;
 
-        // Best-effort at determining origin. Use parent param if relayUri's
-        // origin matches that of the relayUri; else use relayUri.
-        var origin = gadgets.rpc.getOrigin(parentParam) == gadgets.rpc.getOrigin(channel.relayUri)
-            ? parentParam : channel.relayUri;
-        process(rpc, origin);  // actually dispatch the message
+        // Send along the origin if it's been verified during handshake.
+        // In either case, dispatch the message.
+        process(rpc, channel.originVerified ? channel.relayOrigin : undefined);
       }
 
       // Send an ACK indicating that we got/processed the message(s).
@@ -387,7 +420,7 @@ if (!gadgets.rpctx.rmr) {  // make lib r
       if (nonAckReceived ||
           (noLongerWaiting && channel.queue.length > 0)) {
         var from = (fromFrameId === '..') ? gadgets.rpc.RPC_ID : '..';
-        callRmr(fromFrameId, gadgets.rpc.ACK, from, {ackAlone: nonAckReceived});
+        callRmr(fromFrameId, gadgets.rpc.ACK, from, {'ackAlone': nonAckReceived});
       }
     }
 

Modified: shindig/trunk/features/src/main/javascript/features/rpc/rpc.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/rpc/rpc.js?rev=1101242&r1=1101241&r2=1101242&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/rpc/rpc.js (original)
+++ shindig/trunk/features/src/main/javascript/features/rpc/rpc.js Mon May  9 21:59:59 2011
@@ -182,7 +182,8 @@ if (!window['gadgets']['rpc']) { // make
      * @member gadgets.rpc
      */
     function getTransport() {
-      if (params['flash'] == "1") return gadgets.rpctx.flash;
+      if (params['rpctx'] == 'flash') return gadgets.rpctx.flash;
+      if (params['rpctx'] == 'rmr') return gadgets.rpctx.rmr;
       return typeof window.postMessage === 'function' ? gadgets.rpctx.wpm :
           typeof window.postMessage === 'object' ? gadgets.rpctx.wpm :
           window.ActiveXObject ? (gadgets.rpctx.flash ? gadgets.rpctx.flash : gadgets.rpctx.nix) :
@@ -691,8 +692,8 @@ if (!window['gadgets']['rpc']) { // make
       }
 
       // The "relay URL" can either be explicitly specified or is set as
-      // the child IFRAME URL verbatim.
-      var relayUrl = opt_frameurl || childIframe.src;
+      // the child IFRAME URL's origin
+      var relayUrl = opt_frameurl || gadgets.rpc.getOrigin(childIframe.src);
       setRelayUrl(gadgetId, relayUrl);
 
       // The auth token is parsed from child params (rpctoken) or overridden.