A letter I wrote to my Senator today about PIPA/SOPA

Dear Senator,

As someone who is deeply entrenched in technology (I work at a startup in Manhattan) I would know your thoughts on PIPA and the Houses proposed SOPA bill.

One thing I find appalling is the lack of oversight with this bill. Why did not one House representative or Senator meet with major technology companies to discuss how to tackle this issue instead of blindly charging ahead? Is this the best solution? How is it that not even the Communications, Technology and Internet subcommittee was not even consulted? I am sure anyone familiar with how DNS works would have screamed “Fus Ro Dah” (based on a game I play, anyone familiar with the internet would get the joke) at any individual who mentioned a change such as that proposed in SOPA or PIPA.

I know there is a lot going on in the world right now, terrorism, threats of war, Iran, etc… But the basic tenants of freedom of speech must be preserved and it will only be a matter of time before the individuals who lobbied for this power begin to abuse it. “Power tends to corrupt, and absolute power corrupts absolutely. Great men are almost always bad men.” – Lord Acton

I am not a proponent of online piracy or advocate it in any way but this is not the proper solution to solving the issue. If the MPAA and the RIAA spent half as much time on developing strategies to adapt to the Internet as they have lobbying for change then we wouldn’t have these very dangerous bills up for consideration.

I look forward to your reply,
Warm Regards,
Anthony Wlodarski

——–
I implore you exercise your first amendment rights before they are gone. I don’t know if it is the crazy in me but it seems every day, more and more, the second amendment is needed to protect the rest of them from abuse and corruption. I just wish there was a better way.

Authentication with Node.js and Zend Framework.

Zend Framework which is PHP based and Node.js which is JavaScript based don’t have a common connection to pass data in a bi-directional nature. I was tasked with building a bridge of sorts that would utilize existing information from Zend Framework with the latest release of Socket.io’s authorization mechanisms. (If you don’t do this then arbitrary connections can happen and will be authorized.) Lets get right into the code and see how this issue was tackled:

/** Require the http module. */
var http = require('http');
 
/** Create a new http server to listen to and send requsts from. */
var server = http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type':'text/html'});
    res.end();
});
server.listen(12121);
 
/** Require the socket.io module */
var io = require('socket.io').listen(server);
 
/** File system library **/
var fs = require('fs');
 
/** Extract the session id from the headers cookies **/
function getPHPSessionId(cookies) {
    var phpSessionId = '';
    cookies.split(';').forEach(function(a) {
        var b = a.split('=');
        if(b[0].trim() == 'PHPSESSID') {
            phpSessionId = b[1].trim();
        }
    });
 
    return phpSessionId;
}
 
/**
 * This function will look in the PHP Session directory and look for the session
 * file.  If the file exists it will load that file as a string and then parse
 * that string for the PHP Session which will only be stored once the PHP authorization
 * method saves it.
 */
function authorizePHPSession(phpSessionId, cb) {
    try {
        fs.readFile('/path/to/sessions/sess_'+phpSessionId, 'utf8', function(err, data) {
            if(err) {
                throw err;
            }
 
            if(data.search(phpSessionId.toString()) > -1) {
                cb(null, true);
            } else {
                cb(null, false);
            }
        });
    } catch(e) {
        cb(null, false);
    }
}
 
io.configure(function() {
    io.set('transports', ['xhr-polling','htmlfile','jsonp-polling']);
    io.set('authorization', function (data, cb) {
        authorizePHPSession(getPHPSessionId(data.headers.cookie), cb);
    });
});

As you can see here we have the usual Node.js lifting. We create a http server, we create an instance of the io object from Socket.io and get to it. However then we begin to get into the interesting functions. “getPHPSessionId” is a utility function that will parse the cookie information attached to the handshake data. If we find the correct cookie prefix (PHPSESSID in this instance, but this is configurable so check your php.ini file!) we extract the second part of that string as the PHP session. If all else fails we return an empty string which will be compatible with our next function, “authorizePHPSession”.

“authorizePHPSession” takes two parameters, the PHP session id, and a callback function that is to be executed. The callback must be executed as per the documentation here. This function call to read the contents of the file must be asynchronous. Synchronous calls are dangerous in a non blocking i/o situation. I will quote Node.js documentation:

The synchronous versions will block the entire process until they complete–halting all connections.

Trust me I tried the synchronous calls, I watched the connection pool halt and just stack up and it was just one mess. Simply enough if the PHP session id exists in this file then it is authorized (how did the PHP session get into the file, well that is the next part…). Finally pulling it all together I call:

io.configure(function() {
    io.set('transports', ['xhr-polling','htmlfile','jsonp-polling']);
    io.set('authorization', function (data, cb) {
        authorizePHPSession(getPHPSessionId(data.headers.cookie), cb);
    });
});

Now we get into the Zend Framework portion of our example. I am going to assume that once you have authorized the user you write some sort of information into the session. This is our chance to insert the PHP session id to our session.

        $result = $authDbTable->authenticate();
 
        if($result->isValid())
        {
            // lets store the user in the session
            $user = $authDbTable->getResultRowObject(array('id', 'role'), null);
            $usersTable->update(array('lastLogin'=>date('Y-m-d H:i:s')), array('id = ?'=>$user->id));
 
	    if($form->remember_me->checked) {
		Zend_Session::rememberMe();
	    } else {
		Zend_Session::forgetMe();
	    }
 
            // insert our session information.
            $user->nodeAuthorization = Zend_Session::getId();
 
            $auth = Zend_Auth::getInstance();
            $auth->getStorage()->write($user);
        }

Lets discuss the semantics of this operation. PHP is invoked by the browser request. A cookie is written to your browser with your PHP session id. Which is the only unique identifier which will allow you to access serialized data stored in a file on our server. Socket.io is invoked once the page is loaded into your browser. At this point the information contained in your cookies is passed as handshake data to our event server. The event server does the authorization previously mentioned. If the session string exists (which will only happen once you are authenticated) you are granted access, else you are not.

PHP/Node.js/Socket.io is just simply amazing!

BIG BIG BIG EDIT
If the PHP garbage collector has not picked up the sessions yet as it has a random chance of running per page load you can check the stats of the file yourself. However the one CAVEAT is that if you change the length of time for which your sessions are valid you have to do it in two places. application.ini and or Zend_Config for PHP and in the Node.js application file variable. Take a look at this fixed version:

/**
 * This function will look in the PHP Session directory and look for the session
 * file.  If the file exists it will load that file as a string and then parse
 * that string for the PHP Session which will only be stored once the PHP authorization
 * method saves it.  It also takes into consideration sessions that may not have been
 * gc'ed by PHP yet.
 */
var sessionExpiration = 86400;
function authorizePHPSession(data, cb) {
    var phpSessionId = getPHPSessionId(data.headers.cookie);
    fs.stat('/var/lib/php5/sess_'+phpSessionId, function(err, stats) {
        if(err) {
            cb(null, false);
        } else if(Date.now() - (Date.parse(stats.mtime)) > sessionExpiration) {
            cb(null, false);
        } else {
            try {
                fs.readFile('/var/lib/php5/sess_'+phpSessionId, 'utf8', function(err, fileContents) {
                    if(err) {
                        throw err;
                    } else if(fileContents.search(phpSessionId.toString()) > -1) {
                        data.phpSessionId = phpSessionId.toString();
                        cb(null, true);
                    } else {
                        cb(null, false);
                    }
                });
            } catch(e) {
                cb(null, false);
            }            
        }
    });
}

This was brought to my attention by Trii chilling out in #phpc on irc.freenode.org. You can visit him here at g+