I crashed my server a few minutes ago, and while not related, I discovered a little security flaw in my websocket demo that I thought would be fun to share with you guys. My mistakes should be your lessons, right? As with many mistakes, it involved something I knew could be an issue, I just had to find the time to confirm it really was. Before I tell you the flaw, let me show you the code and see if you can pick up the issue.
First, a bit of context. The code involved was for my websocket chat demo. It allows for, well, chat, and I wanted to ensure that the messages broadcast by users did not include any HTML. This is how I fixed it:
$("#sendmessagebutton").click(function() {
var txt = $.trim($("#newmessage").val());
if(txt == "") return;
txt = txt.replace(/<.*?>/,"");
msg = {
type: "chat",
username: username,
chat: txt
};
chatWS.publish("chat",msg);
$("#newmessage").val("");
});
Nice and simple, right? You take the input, trim it, strip the HTML, and then pass it to my websocket object. The message will now be "clean" of any HTML the user may have tried to send. That worked fine until I tried this...
So what happened there? As a user, I commonly will view source on web apps so I can see how they are built. I assume everyone does. I noticed how the chat system went through the code above. I also noticed how it simply packaged things up and passed it to a chatWS variable. Once I knew that, nothing stopped me from going into my browser console and executing the call manually.
Does that worry you? I hope so.
The moral of the story is - well - the same as it's always been. Don't trust client input. In this case though it didn't occur to me. Why? Because in our websocket implementation, you don't have to write any server side code. Your message just bounces out to all the clients.
Luckily this is easily enough to fix. In ColdFusion 10, you can associate a CFC with a websocket channel. One of the methods you can implement is "beforeMessage". This allows you to massage your messages before they go out. Here's how I corrected it:
public any function beforeSendMessage(any message, Struct subscriberInfo) {
if(structKeyExists(message, "type") && message.type == "chat") message.chat=rereplace(message.chat, "<.*?>","","all");
return message;
}
Make sense? (Btw, if you were around for when I crashed my server, this was certainly not why. I have a pretty good handle on why and will report back when I can.)