DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Web Sockets with Server Side Logic: Follow-Up and Fixes

Raymond Camden user avatar by
Raymond Camden
·
Mar. 06, 12 · Interview
Like (0)
Save
Tweet
Share
5.04K Views

Join the DZone community and get the full member experience.

Join For Free

a few days ago i posted an update to my websocket chat demo that talked about associating a cfc with the web socket to perform server side operations. while testing the chat, a user (hope he reads this and chimes in to take credit) note another security issue with the code. i had blogged on this topic already, specifically how my chat handler was escaping html but could be bypassed easily enough. the user found another hole though. let's examine it, and then i'll demonstrate the fix.

when the chat button is pressed, the following code is run:

$("#sendmessagebutton").click(function() {
    var txt = $.trim($("#newmessage").val());
    if(txt == "") return;
    msg = {
        type: "chat",
        username: username,
        chat: txt
    };
    chatws.publish("chat",msg);
    $("#newmessage").val("");
}); 

i've removed my html escaping code since the server handles it. but pay attention to the message payload. it contains a type, a username, and a chat. the username value is set after you sign in. it's a simple global javascript variable. it's also trivial to modify. just create your own structure and pass it to the web socket object:

chatws.publish("chat", {type:"chat",username:"bob newhart", chat:"howdy"}); 

the server will gladly accept that and pass it along to others. not good. luckily there is a simple enough fix for this. my first change was to remove the username from the packet completely.

$("#sendmessagebutton").click(function() {
    var txt = $.trim($("#newmessage").val());
    if(txt == "") return;
    msg = {
        type: "chat",
        chat: txt
    };
    chatws.publish("chat",msg);
    $("#newmessage").val("");
}); 

if you remember, we had a cfc associated with our web socket that was handling a variety of tasks. one of them supported stripping html. here is the original method:

public any function beforesendmessage(any message, struct subscriberinfo) {
    if(structkeyexists(message, "type") && message.type == "chat") message.chat=rereplace(message.chat, "<.*?>","","all");
    return message;
} 

notice the second argument we didn't use? this a structure of data associated with the client. we modified this a bit on our initial subscription to include our username. that means we can make use of it again:

message["username"]=arguments.subscriberinfo.userinfo.username; 

this will now get returned in our packet. check it our yourself below. i've included a zip of the code. (and this is my last chat demo. honest.)

oops!

turns out i have a critical mistake in my fix, but it's one of those awesome screwups that lead to learning. as soon as i posted my demo, a user noted that his chats were being marked as coming from me. i had no idea why. i then modified my cfc to do a bit of logging:

var myfile = fileopen(expandpath("./log.txt"),"append");
filewriteline(myfile,serializejson(message) & "----" & serializejson(subscriberinfo)); 

i saw this in the log file:

{"chat":"testalphaone","type":"chat"}----{"userinfo":{"username":"ray"},"connectioninfo":{"connectiontime":"march, 02 2012 09:56:18","clientid":1511146919,"authenticated":false},"channelname":"chat"}

{"chat":"testalphaone","type":"chat"}----{"userinfo":{"username":"chk"},"connectioninfo":{"connectiontime":"march, 02 2012 09:57:49","clientid":542107549,"authenticated":false},"channelname":"chat"} 

at the time of this test, there were two users. my one message was sent out 2 times. so this is interesting. to me, i thought beforesendmessage was called once, but it's actually called n times, one for each listener. that's kind of cool. it means you could - possibly - stop a message from going to one user. of course, it totally breaks my code.

one possible fix would be to simply see if username existed in the message packet. but if i did that, an enterprising hacker would simply supply it.

when i figure this out, i'll post again.

second edit

woot! i found out. turns out i should have been using beforepublish. it makes total sense once you remember it exists. it also makes more sense to have my html "clean" there too. things got a bit complex though.

beforepublish is sent a structure too, but it only contains the clientinfo packet. it does not contain the custom information added by the front end code. i'm thinking this is for security. but, we have the clientid value and we have a serverside function, wsgetsubscribers. if we combine the two, we can create a way to get the proper username:

public any function beforepublish(any message, struct publisherinfo) {

    if(structkeyexists(message, "type") && message.type == "chat") {
        //gets the user list, this is an array of names only
         var users = getuserlist();
         var myclientid = publisherinfo.connectioninfo.clientid;
                  
         var me = users[arrayfind(wsgetsubscribers('chat'), function(i) {
             return (i.clientid == myclientid);
         })];
              
        message.chat=rereplace(message.chat, "<.*?>","","all");
              
         message["username"]=me;
    }
         
    return message;
} 

does that logic make sense? basically we are just comparing clientids. i've restored the demo so have at it!

download attached file

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • [DZone Survey] Share Your Expertise and Take our 2023 Web, Mobile, and Low-Code Apps Survey
  • How Elasticsearch Works
  • Monolithic First
  • Practical Example of Using CSS Layer

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: