Into browser mapping apps? Excited about IE8? I’ll raise my hand for both.

Anyway, if you have the slightest interest in either of those things, you should go check out my coworker’s post titled “Google Maps + IE8 Beta 1 = Epic Fail.”

14th Jan, 2008

dsHistory, Round 2

I’m really excited to let everyone know that dsHistory has undergone some very major changes since you last heard about it!

After considering my options, I decided to make a hope for dsHistory at Google Code. Go and check it out. Through Google Code, dsHistory now has a proper issue tracker, an actual downloads area, a Subversion repository, a Subversion repository browser, and a wiki.

Speaking of the wiki, dsHistory now has a comprehensive usage doc, an examples doc, and an API doc. The demo has undergone quite a few changes and has been relocated. More docs will be added as needed. If you need support beyond the wiki, dsHistory now has a discussion group hosted on Google Groups. You can continue to get updates for major releases of dsHistory on this blog, or you can subscribe to this RSS feed to get updates every time a new build becomes available.

The dsHistory library itself has been revamped quite a bit. I don’t have a proper changelog set up yet, but I can tell you that the new version, posted as a feature download on the front page of its new home, is much more suitable for production than the last version that was released (v.9) . v1-pre, revision 32, features a reorganized library based on a more suitable design pattern, improved performance due to the implementation of lazy function definitions and other considerations, a fix for potential memory leaks in IE, and many bugfixes pertaining mostly to the way the window hash (i.e., bookmark support) is serialized, deserialized, updated, and the like. I’m sure issues will arise with v1-pre-r32, but that’s what the issue tracker is for now.

Upgrading dsHistory from v.9 to v1-pre-r32 should be easy. For the most part, you should be able to just drop the single dshistory.js or dshistory.compressed.js file in over top of the old library file. As of the date of this post, the external HTML page that was previously packaged with dsHistory is no longer needed (although it may be included later). If something isn’t working right after the upgrade, read the usage doc. If it’s still not working right, take a look at the examples or the demo. Still not working? Post in the discussion group.

My goal is to release what I would consider a stable version, v1, of dsHistory by February or March. The updated version of dsHistory on Google Code is in production in a number of different places (most notably our property mapping solution), but I’d like for dsHistory to have as many people try it out as possible before the final release is out.

Give me feedback! Comments, questions, even snide remarks are very much welcomed at this point. Also, if you use dsHistory in your app, please drop me a comment or post your experiences in the discussion group.

I had a job interview at Google for a UI software engineering position last month, and I’ve been quietly debating as to whether or not I should share my experiences here. On the positive side, this blog was originally started for me to use as an outlet to share my thoughts and talents, and such an experience seems like it would be the perfect content that my target audience would be interested in. On the negative side, not only did I sign a very strict NDA, but I have a moral obligation not to give away intel to the point where it makes the next guy or gal any more likely to get the job because of what I would say. Ultimately, I decided that a carefully worded post would be acceptable to write and would benefit Google, potential applicants, and anyone else who would be interested.

My experience was the company was absolutely incredible. I was actually contacted by a Google recruiter from the get go, so it was quite a shock to me to see the recruiter’s email arrive in my inbox. After some quick chit-chat, the recruiter scheduled a telephone interview for me with one of their engineers.

The phone interview went very well. I was initially really anxious about it, but once the interviewer and I got to talking, it was really smooth sailing. Even though my conversation with him isn’t covered under the NDA I signed, my moral obligations to the company prevents me from disclosing exactly what was asked. However, I can tell you that we talked primarily about JavaScript and about the different ways you can use it to accomplish your goals. Being that I’m primarily a .NET evangelist, I was concerned that he’d be asking about Python, Java, and other server-side languages. He didn’t. We did talk about some server-side stuff, but it was all at a high enough level that anyone who has ever worked with any server-side languages should be able to answer. It may have been tough for others, but I felt really good about it after we got off the phone.

I waited for something like two weeks after that before I heard anything back from anyone. Finally, the recruiter ended up emailing me back and told me that they wanted to move me on to the next stage! I was extremely excited at that point, and I traded a whole bunch of emails with a bunch of the recruiting coordinators who scheduled me for an on-campus visit almost a month and a half after my initial contact with the recruiter. Thankfully I wasn’t really looking for a job at that point, but if I had been and I had been short on money, I can’t imagine trying to hold out for that long.

Anyway, my wife and I ended up flying up to Mountain View last month for a three day weekend so I could continue the interview process. They arranged for us to stay at Hotel Avante (a really cool hotel in the area), got us a rental car, paid for my meals, and paid for my airfare. We roamed around in SF all weekend, and when interview Monday rolled around, I couldn’t have been more excited to meet with everyone.

I’m not supposed to talk about their interview format, the questions I was asked, or really anything I saw or heard at the campus. What I can tell you is that I had four “normal” interviews and then a lunch interview. The engineers themselves were all brilliant people, and I immediately felt like I’d fit right into their culture. Each interviewer seemed to have free reign with the questions they wanted to ask, and I was happy to talk to all of them.

Some interviewers wanted to focus more on what I’d call the “hardcore” computer science questions where I had to write code on a whiteboard, while others were more interested in what I’ve done in my field in the past, how I’ve done it, and how I used my education and experiences to do what I do. I was far more comfortable with the application-type questions since I’m very confident in my knowledge, but I wasn’t really sure how I did on the computer science-type questions; I think I even (regrettably) let out a slight groan when they asked me one of the questions. It’s been so long since I’ve needed to use some of that knowledge, and I wasn’t expecting to have to remember it. I ultimately got the right answers at the end of those kinds of questions, but I definitely stumbled my way through some of them. Also, I wasn’t really thinking about the performance considerations until I talked to my third interviewer (who, incidentally, had just had a root canal that morning), so I bet they docked some points for that as well. Overall, it was just really difficult to gauge how I did once it was all finished. I definitely didn’t get the feeling that some people have after such lengthy interviews where they feel like they’ve been hit by a truck, but I didn’t have the feeling that I aced it either. Before I left, I made sure to grab one of those free Odwalla juices on my way out, although I must have looked like a huge dork since I was already carrying around two drink bottles from earlier in the day.

I went home, and I waited. Two more weeks went by where I’d jump every time I saw that I had new email. It was a really crazy feeling, and even though I have a job that I’m in love with, I was still really nervous. I knew that I was perfect for them, but I had no idea whether or not they felt the same way.

Ultimately, I was notified by email that I didn’t get the job. On one hand, I was really disappointed that I didn’t get to have that choice to make. On the other, I feel like the company I’m with now is poised for spectacular growth over the next few months, I love all of the people I work with and, for the most part, I like the area I live in. I hold my head up high because of the changes I’ve been able to make and will continue to make at Diverse Solutions, and I’m absolutely certain that I would have been able to make the same kind of impact at Google even though it’s a far, far bigger company.

All that said, I’d really love to work at Google someday soon, and I hope that I can be reconsidered for that very same position by the time I am done with Diverse Solutions and am actively pursuing another job. I still don’t have my BS in Computer Science and BA in Criminal Justice yet as I’m a single, stupid Chemistry class short of getting both, so I’m going to have to focus on getting that taken care of next. I’m also going to become more active in the web scripting community (or whatever you want to call it) so that I can prove and publish my skills a bit more.

My other takeaways / tips are as follows:

  • Be confident. Don’t be a moron like me and groan when your interviewer asks you a difficult question.
  • Know your stuff. Just because you’re a JavaScript hacker doesn’t mean you shouldn’t know the very basic CS principals. If you don’t have a good CS foundation, you won’t be able to easily translate your skills as things progress.
  • Get a formal education. I haven’t been given my degrees for almost two years now due to poor planning in the beginning and busyness once I got this job. My name was even read in the commencement ceremonies, but here I am trying to explain myself whenever I have an opportunity like this. I’m not sure that Google rejected me because I didn’t have my degrees, but I’m sure it didn’t help by any means.
  • Be publicly active. While I did spend quite some time writing my dsHistory library, I publicly slacked off after that and instead poured my time and energy into writing software for my company. I need to help out other project to both benefit the community and demonstrate my knowledge.
  • Keep your energy levels up. When you’re talking to a number of incredibly brilliant people for a long period of time, it can really impact the way you think. I needed to keep my mind sharp throughout the whole thing instead of drifting off at some points like a nut.
  • Don’t be greedy. Maybe I’m reading into this too much as I’m definitely a perfectionist, but I felt really stupid at the end when I grabbed that last drink from the fridge on my way out, especially since I already had two other drinks with me. I think my interviewer saw it too, and even though it was probably an infinitesimally small issue, if it had been my company, I would have been looking out for people doing things like that. Also, although I wrote a note to go along with my expense report to explain some things that weren’t clear to me, a person who might have been going through everything very quickly may not have noticed and might have thought I was being especially greedy.

Thoughts?

So I was notified this past Friday that the JavaScript on our IDX solution was broken in IE 6, but only when it was being framed by one of our customers on their website. Weird, but I figured I must have busted something from my deploy earlier that day. After jumping into the problem, I realized that the issue wasn’t actually with my code but rather, believe it or not, with Google’s AJAX search API. Specifically, the issue that I found was that IE 6 absolutely will not execute JavaScript under the following conditions:

1) The visitor is using IE 6 to visit a page.
2) That page frames another page that requests JavaScript.
3) The JavaScript is served with HTTP headers that specifically include the “no-cache” command in the “Cache-control” headers; other, similar headers (such as “private”, “no-store”, etc) do not have the same effect.
4) The JavaScript is GZIP’d.

I figured that Google must have either recently added the no-cache header or recently started gzip-ing, so I posted the problem in Google Groups. Today, they made a post notifying me (and everyone else) that they fixed the problem. Sure enough, I fired up Fiddler just now, and it seems that they no longer gzip the response when serving up their www.google.com/jsapi application.

Anyway, I had encountered this problem before, but I just chalked it up to something that everyone else probably already knew. As it turns out, a Google search turns up absolutely nothing on this issue. Since I couldn’t find anything, I figured that the world would benefit from this knowledge.

So I was looking for something on the Google Maps API class reference page the other day, and I’ve been there enough to notice when something changes in the first screenful. On Thursday, I noticed two more classes had been added to the list: GAdsManager and GAdsManagerOptions. Essentially, these two classes allow developers to plug in their Google AdSense ID so that AdSense ads will be embedded into the map.

I checked the official AdSense blog, and I’m subscribed to both the official Maps API blog and their LatLong blog, and I haven’t seen an announcement for it yet. The class reference page page says that those two classes were added in version 2.85; version 2.84 is being served up by default right now. In case you’re not aware, Google updates their API about every two weeks. I’m not sure how long v2.84 has been out, but I’m guessing that it won’t be long before we hear about the official release of the new classes. If you have a maps application and you want to try it, all you have to do is specify v=2.85 in the querystring of the Maps API script source.

While it’s not so much a technological revelation, I predict that the income this can generate for developers will lead to better and more numerous applications. With the massive number of new web startups out there that are funded by AdSense already, we’re certain to see the same thing happen with sites that use Google Maps. Whereas AdSense ads may have been placed beside a map up to now, the ability to embed ads directly into the map that already has the visitors attention is really going to do well for the developer.

Anyway, in the meantime, I’ve signed up for AdSense. I added my new AdSense ID to the GAdsManager class in a map in one of my development apps, and I called GAdsManager.enable() so that the ads will show. All I’m waiting on now is Google’s AdSense indexing bot (or whatever one calls it) so ads will be shown on the map based on the page text surrounding the map. I don’t want to disclose the URL just yet as it’s something we use for development, but I’ll let you know and take some screenshots once I do start seeing ads on the map. Should be interesting to see where we go from here!

UPDATE 1/15/08: dsHistory has improved greatly and has been moved to a new home. Read more here.

For the past few weeks now, I’ve been playing around with some different ways to implement support for the back button in many of my JavaScript-heavy web apps. In the past, nothing out there really seemed to suit my needs for one reason or another. Therefore, I made something to fill the gap. It’s not necessarily that there is a problem with what anyone else made; it’s just that nothing out there suited my needs. However, before I get into what I made, let’s give credit where credit is due and review what the great pioneers before me have made (in no particular order).

Really Simple History (RSH), Brad Neuberg
RSH was / is really great since it was the first good way to both keep track of history using JavaScript and allow developers to make their supercool apps keep URL state / allow for bookmarking. It was a bit difficult for me to easily jump into, but once I figured out how it worked internally, I found that RSH actually serializes any information you associate with a history item into JSON and puts it into the window hash. Nice, but I wanted more fine-grained control over the hash.

AJAX-Nav, Mike Stenhouse
The article by Mike in which he introduces AJAX-Nav is what really got me rolling on this project. He did an excellent job describing the stumbling blocks he ran into while building his solution and the different methods he created to get around them. Ultimately it seemed too tied into an actual AJAX request and was otherwise a bit difficult to get it to work the way I wanted it to work. As of this post, his demo is also broken. Still, an excellent solution.

dojo.io.bind, Alex Russell, et al (Google’s cached since original is missing?)
Now this is what I really wanted. With dojo’s bind, you bind the history to a function, not to hash args or anything else. You can also bind a hash along with it, but you have to keep track of the hash by yourself. Ultimately, as far as I can tell from looking at it though, everything is also bound to an AJAX server request. Awesome work / idea though.

YUI Browser History Manager, Julien Lecomte
This is probably the culmination of all previous attempts at solving the back / forward button issue. Without a doubt, and even though it is “experimental”, it is certainly the most robust history manager out there in terms of cross-browser support. Since it’s still pretty new, I highly recommend you take a good look at Lecomte’s YUI post to see how he built it and understand the choices JS developers have right now as well as how he overcame some of the development issues. I had dsHistory almost done at the time this came out, so I can’t say I really used it for much insight since it just wasn’t available. However, as far as the implemtation goes, it’s really incredible. In spite of this though, I still wanted to finish my little pet project since the YUI History Manager still didn’t do exactly what I was hoping to do.

So now I’m tossing my hat in the ring with my project, dsHistory. Check out my demo which explains how to get started, or keep reading for more info.

dsHistory internally works somewhat similar to the other history solutions that exist, but it is implemented more like dojo’s bind than anything else. It requires no supporting libraries, it checks in at just over 7kb when compressed, and it is easy to use. The history is thought of as a series of events that have functions attached to them, and the bookmarkable window hash data is designed to be controlled independently from the events (if it is even utilized at all, which I’ve found isn’t desired at times).

The project originally started as a quick solution for our dsSearchAgent real estate IDX / MLS / property search product, but there are very few, if any, shreds of that code that made it into this project. It has morphed into more than I thought it would be, but I’m pretty happy with it now that I’m finally done with my first release. My history manager’s name, dsHistory, is named as it is because we occasionally name our products with a ds prefix. I couldn’t think of a better name, so as a hat tip to my company, dsHistory it is.

Rather than give a very lengthy rundown as to how I developed dsHistory, I encourage you to first read through the methods everyone else has used up until this point since it has already been done before. With some exceptions, I took a very similar path but just changed the way it was ultimately implemented. Once you’ve reviewed the other projects, you may want to look through my commented source file to see more implementation details.

As I stated earlier, dsHistory is pretty easy to use and was designed so that functions would be called as a user moved forwards and backwards through the history. Specifically, the functions you add to the the history stack either with or without query vars should be thought of as the location / event / tab / whatever that the user is currently “in.” Therefore, the first function you add, no matter how you add it, won’t update the history event stack (see caveat below).

Since it is based on functions, I highly recommend you use a currying helper function such as Dustin Diaz’s curry() or, my personal favorite, Prototype’s bind(). Use whatever works best for you. Anyway, check out the example code below.

/* add a base function that will never be automatically removed from our history function stack. this is the way i intended dsHistory to be used. essentially, you should set this function to be the “base” state of your application, whether that is a function that displays a certain panel / tab combination, a certain piece of content, or whatever other function you want to use as your base state. */

dsHistory.addFunction(function() { alert(’base history’); });

// add two more random functions that will be called as a user goes back
dsHistory.addFunction(function() { alert(’history #2′); });
dsHistory.addFunction(function() { alert(’history #3′); });

/* now there are three events on the stack. going back once will call alert(’history #2′). going back again will call alert(’base history’). going forward once from this point will call alert(’history #2′). going forward once more will call alert(’history #3′). each function will have a single argument passed to it which will be either ‘back’ or ‘forward’, allowing you to take the appropriate action. */

/* we’ve now gone back twice and then forward twice. we’re at the end of the stack, so we’ll add some more fun to the history in the form of a bookmarkable query hash. */

dsHistory.setQueryVar(’my super cool key’, ‘key value’);
dsHistory.setQueryVar(’value without key’);
dsHistory.bindQueryVars(function() { alert(’query 1′); });

/* not only does the hash now contain the encoded keys / values (if a value exists), but dsHistory.QueryElements['my super cool key'] has a value of ‘key value’ and dsHistory.QueryElements['value without key'] has a value of ”. in fact, the dsHistory.QueryElements javascript object is loaded with the decoded keys / values when the page is loaded directly. this allows you to ultimately handle a bookmarked URL based on what comes in from the window hash */

// remove one of the query from the hash and then rebind the hash

dsHistory.removeQueryVars(’my super cool key’);
dsHistory.bindQueryVars(function() { alert(’query 2′); });

/* now there are five events on the stack. back once will call alert(’query 1′), back twice will call alert(’history #3′), and so on. since the first function is never popped off the stack, you can call dsHistory.setFirstEvent(functionArgument) to change it*/

dsHistory definitely has it’s caveats, and it’s not for everyone. However, it fits fine for me right now, and I suspect it may help others as well. In addition to the limitations pointed out by the YUI team / Julien, dsHistory has some additional known issues that you should consider before deciding to use it in your application.

  • It doesn’t work with Opera and Safari. I don’t have a Mac, and the development resources required to get my solution working in Opera (my time) aren’t enough to justify the cost at this time.
  • If a visitor is using Gecko and the first function added to the history stack is added with the bindQueryVars function (not recommended — see the first line in my usage block for a better way), a history item will immediately be created. The implication of this is that the user will be able to go back in the history and subsequently go forward without anything happening; this shouldn’t really be a big deal though. However, if a function is added to the history stack via dsHistory while the visitor is in this history “black hole,” window.history.forward() will be called so that the implementation remains consistent. It shouldn’t be a big deal, but it’s something you may want to be aware of.
  • Don’t try to use “_serial” as a query var key as it is sometimes used in Gecko. Specifically, it’s used to keep track of whether or not the visitor is going forward or backward in certain circumstances.
  • Finally, consider dsHistory an alpha / beta / experimental / test release. I’ve tested it to the fullest extend I know how on my machine, but given the nature of the project and my limited time, I’m certain there are bugs that I missed.

Now that I’m done with that, check out my demo to get started. It explains most everything you’ll need to know really. If you’d like to use it, you can download the zip. It contains the usage.html file, the uncompressed dshistory.js file, the compressed dshistory.compressed.js file, and the dshistory.html supporting asset file.

I welcome your comments, questions, bugs, and snide remarks.

While this all just seems like common sense to me, it apparantly isn’t to some people / development teams. As I said before, I’ve been stumbling onto more and more sites lately that exhibit JavaScript programming practices that seem a little off at best. Some sites just seem to slap together frameworks without thinking about the repercussions and just call it a day. Yeah, for the most part it doesn’t matter in the short run, but in the long run, poor programming can drive up IT costs in the form of additional hardware and bandwidth as well as drive away your website visitors due to frustration with loading times

Other individuals / companies have certainly written about this before, but I haven’t seen anything on it in a while now and I figured it’d be nice to have a refresher. Without further ado, following are some practices that we follow at Diverse Solutions that, as a website visitor, I wish more companies would follow.

  1. Shrink Your Code: This really seems like common sense to me, but far too many people aren’t even aware of the tools available to help developers in this area or just don’t care enough about the benefits. JS compressors, in case you’re not aware, do anything from strip unnecessary whitespace and comments to actually changing the names of private variables to smaller names to save space. We use Dojo ShrinkSafe, but I’ve heard JSMin does a pretty good job too, and I know that there are a number of others that will probably fit the bill as well. They are pretty easy to use if you take the time to set them up, and the benefits are definitely worth it. In most of our apps, we’ve created batch files that run ShrinkSafe on most JS files in the deploy directory so that we don’t have to really mess with it after we’ve set it up.
  2. Compress Your Code: Use server-side compression if the client truly supports decompression. GZIP can be your friend, but it can also be your enemy if the client doesn’t support it properly but says it does (looking at you, IE6). Some claim it’s not that big of a deal and that the problem isn’t very widespread, but we’ve had our fair share of clients who had problems with us compressing JavaScript. The problem is primarily related to this bug, but there are other things that can cause problems with compressed content as well. Basically, we’ve found that you’re best bet is just to conditionally serve compressed JS to browsers that say they support it except for anything below IE6 SP2. You can do this with server-side code if you intercept your requests to JS files. We run IIS 6 and use ZipEnable so that we don’t have to write anything extra to take care of this.
  3. Don’t Include Libraries You Don’t Use: This, again, seems like common sense. I’ve happened upon way too many sites that include Prototype or Dojo or something just because they likely think that they are going to use it, but they never end up using a single part of it. My advice is to only include the library after you’ve learned how to use it, not before.
  4. Use Versioning: This is something we fairly recently started doing, and we don’t know why we never did it before. Versioning is especially important because of this bug in IE6. Basically, versioning involves appending a querystring to the end of every script’s tag src attribute. The querystring doesn’t / shouldn’t change the file being requested, but because you can change the querystring when you deploy, the browser will re-request the file with the next request and get the new version of your script. It can definitely be a pain to manually manage changing the querystring at the end of all of your JS files, so we have the value of the querystring variable set as a server side variable that we can just change in one place to make everyone’s browser reload everything. Sure it reloads some files unnecessarily, but like everything else regarding JS optimization, it’s a balance between extra work and better practices.
  5. Minimize Client Requests: Yahoo! pre-compiles some of their YUI libraries so that developers can just include one or two files in their apps and get the functionality they need, and we do the same thing with most of our libraries / applications. Separating thousands of lines of JS into separate files can really help with organizational purposes, but serving those multiple files to the client can really be a drag to visitors, especially in light in of studies like these. What we’ve done to help fix this is create a quick server-side app that reads an XML config file to determine which JS files to include in the single request. We can then ask for something like /CompressedScripts/Prototype.js to get our Prototype.js file, the Event.onDOMReady file, Justin Palmer’s awesome EventSelectors extension, something we wrote for generating SOAP requests, and Sylvain Zimmer’s impressive $$ addon. That’s five potential requests that we’ve merged into one request, saving an incredible amount of unnecessary requests over the lifetime of our app. I should note that we don’t use versioning (point 4) on our libraries just because it would probably degrade performance most of the time since the libraries rarely change.

Anyway, that should get you started. Of course you can do even further, but like I said, it’s definitely a balance between extra work on your end and unnecessary bandwidth / slow-ish performance on the client end.

Anyone can criticize a website for not having a nice enough design or layout or having a confusing UI. Maybe there’s not enough or too much whitespace on a site, maybe the colors suck, or maybe the business model is stupid. For the most part, this is what the general public will see and judge a company’s website, and often the company itself, on. For the most part, it doesn’t matter to people what language or development environment was used to build the web application; all that matters is that it works the way it should. No one except IT / development really cares about what is underneath the hood of these applications.

As a developer myself though, I have a curiousity that exceeds most others’ desires to know how these applications were build. I want to know what is underneath the hood and how good the developers themselves are. In the past, it was nearly impossible for outsiders like me to look at the underbellies of these applications to gauge the skills of those to made them. However, with the past few years, application code in the form of JavaScript has been pushed to the client an amazing rate and new tools have surfaced that make it easier to dig into this code.

This code is where the crux of my issues with a number of companies lies. You’d think that with the massive amounts of money that these “Web 2.0″ companies have been raising lately that someone there would ensure that the code that is pushed to the client is both secure and efficient. In my opinion, security hasn’t changed all that much. All JavaScript / AJAX forces a developer to do is just take more time to ensure that the way he would have done things otherwise was the right way to do things. AJAX just reduces some of the guesswork that a hacker / cracker / whatever would have had to put in to break the app the way they wany. On the other hand, the efficiency of that code is something new that web developers must learn and get used to.

My entire family is a family of engineers. We’re obsessed with efficiency, optimizations, and often perfection. I might actually be the worst of them all, as I’m always trying to make things better — just ask my wife, as it drives her crazy. Therefore, I’m going to attempt to focus my quest to make things better on a number of very popular websites that I have already singled out. You’ll see this come to fruition in a number of posts of posts over the next few weeks. After that, I’m not sure which direction I’ll end up taking with this blog.

However before I can tell other companies what they are doing wrong and how I think they can do things better, I’ll have to share my own practices that I use on a daily basis to deliver what I feel is the best code possible with the constraint of time over my head. You’ll find all of this in my next post.

So I’ve wanted to share this for quite some time, but I never had a space in which to do it.

I’m currently the webmaster of transportdesigns.com, the website for my dad’s business of the same name. I used / am using WebHost4Life to host it just because I didn’t really know of any good Windows web hosts (Windows needed because of an ASP.NET app I am making) and because it was reasonably priced. Also, because of all the crap littered around Google pertaining to web hosting reviews, it’s nearly impossible to find out whether or not a company is good just by doing a search on it.

Anyway, things were okay for a while. I wasn’t too impressed with their backend, but as long as it was stable, cheap, and had the functionality I was looking for, I was okay. Late November rolls around, and I get an IM from my dad asking him what the black box was doing on the middle of his page over top of the content. Hmm, I don’t remember putting a black box on top of the content, so lets go check it out.

Here’s what I find: http://transportdesigns.com/index2.html. Uh, whaaa? There is a black box in the center of the page. I look closer, and discover that that sweet box is actually an IFRAME tag linking to http://81.95.146.133/sutra/in.cgi?17. I definitely don’t remember putting that there. I investigate it a bit more, and put everything I found into an urgent support request to WebHost4Life. Following is my request in it’s entirity.

It seems that someone has hacked into the web server that houses my website and has changed the index.html page. I renamed the page to index2.html and fixed the original for the time being so that you may do an investigation into this issue. Before you write me off as an ignorant web hosting newbie who doesn’t understand the implications or definition of hacking, please read my description of the problem below.

My specific problem is that the former index.html page (again, now index2.html) has maliciously had an iframe inserted below the html tag that links to the following url: http://81.95.146.133/sutra/in.cgi?17. The URL then issues a 302 redirect to http://81.95.146.133/sp/new/index.php, which contains just a single script tag with what looks like compressed JavaScript on it (so that I cannot tell what the JavaScript actually does).

I have searched Google for this IP, and found the following site detailing the malicious nature of the activity surrounding the IP: http://national.auscert.org.au/render.html?it=6537&cid=2998.

I keep the FTP and control panel passwords for my web host encrypted on my hard drive with a stronger cipher than is necessary [I use KeePass]. No one else knows any of my passwords nor my decryption key to open the password file on my hard drive. I know that I would never deface my own home page, so I am convinced that this occurred due to an internal security problem on your end.

I am absolutely livid that this has happened to my home page while being hosted at your company and consider it to be a massive security issue (which is why I marked the request with such a high priority). Please investigate this matter as soon as possible and let me know of the outcome so that I can take appropriate actions.

Four hours later, I get a response from “Peter C.”

We are now running a fix to remove all the IFRAM tags you stated above in all of your pages. Could you please remove all the IFRAM tags in your pages if you see any? I would suggest you not to open those inflected pages in IE before they are fixed. If you found it happen again, please open a new ticket and report the issue. Thank you for your cooperations!

So my friend “Peter” tells me that they are “running a fix” to remove all the “IFRAM” tags in all of my pages. Based on what he said, I have to assume that this is a widespread hack on their servers and that they wrote a script to fix everyones’ pages who may have been affected. He also says that I shouldn’t open the “inflected pages in IE before they are fixed.” Thanks Peter. I’ll just call up all of my dad’s potential website visitors and let them know to use Firefox for a while. I write back.

As far as I was aware, the IFRAME was only added to the index.html page. However, your response _DOES NOT_ address my issue. Was the server hacked?? If so, why was I not informed earlier? What assurances do I have that this will not happen again. Finally, what kind of compensation do you plan on giving me for this absolutely massive security issue and the poor way your company has handled it by not notifying me after the likely hack??

I am still very upset over this whole issue and will spread the word about the infiltration into your network unless I get some good answers as soon as possible.

I get a response from “Candy.”

Could you please let us know when specifically you have discovered this issue so that we could have a closer look on? Thanks.

Wouldn’t it make sense that, with such a critical issue, I didn’t just wait for two weeks before letting them know? Cmon now. I respond.

The issue was discovered within about 10 minutes of my support request post. Therefore, I was notified of the problem at about 11/20/2006 2:30 PM.

“Rick” responds now.

We did not receive any reports from other customers about the same issue. Also, we also double checked our security settings and everything is good. Have you made sure you do not grant write permission to any users on your files? Hackers are likely to insert codes to your script if you grant write permission to the files.

What?! At the moment, it was a purely HTML site you bozos! I don’t think any l33t haxors inserted any “codes to my script” through my HTML files that only had the default read permissions. My response:

There are absolutely no dynamic scripts on that site that are anywhere public. In addition, the one ASPX page that I _am_ developing that sits on that server does not use any sort of file write mechanisms. I don’t claim that my code is flawless in any way, but I also am certain that there is no way that the code I have written so far on that site could be used to change files within that directory. In addition, I have not granted write permissions to any files whatsoever on this server. You will be able to see that yourself if you investigate further. Please look into the issue further and get back to me as soon as possible. … I am still not satisfied at all with the answers I have received.

“Rick” responds again.

Are there any special permission setting on your root folder? I can reset to default permission for you. Also, we suggest you to change all your password, say ftp, control panel to prevent hacking access.

Like I told him before, I have not changed the default permissions. This is getting really stupid. I respond, again.

There are no special permissions set on my root folder. You can check it if you’d like. The only users who have write access to any of my folders are the SYSTEM user and Administrator. None of the users I have control over have ever had write access to any folders within my directory, including the root directory. Therefore, I am lead to believe that the attack came from somewhere outside of my control. I will reset my password(s), but I still would like to know if my password was leaked from your system or the server was attacked or another user on the server was able to use a script to write to other directories or any number of the other possibilities. I have refrained from publicizing this attack on my blog until now, but this whole thing “blame the problem on a rogue user script or bad file permissions or user error thing” is becoming quite tiring. I am willing to answer questions regarding my script or my file permissions, but I can assure you again that my script never used any of the System.IO (file access) libraries, that my file permissions were never changed, and that my FTP password was definitely not leaked anywhere.

In addition, I was told in my first response that a “fix” had been written that was removing all of the IFRAME tags from my pages. If no other customers reported any incident, and since the problem was only on my main page, why was the script even written and what did it actually change on my site?

“William” responds with “we will have senior staff to reply to your question shortly.” You know, as an aside, I really wish that their support staff could speak / write better English. Anyway, along comes Mark to finish out the support request.

Steve, up to now, we did not have another customer on the same machine reported they are hacked. So we believe it should not be a hacking to our global setting but only your account.

Of course, this case is being recorded. We have checked through the entire server and it seems fine. The two possible method the hacker change your site is through change your webpage or modify it by FTP access.

Right now we checked your file permission settings and it is solid. However you may need to change your FTP password regalarly as well.

So that’s it. In my opinion, I received absolutely terrible customer service from five different people across six different responses, and not one of them offered me a half-decent explanation other than the flimsy idea about that hacked FTP account. Lame. Also, they completely avoided my question about this mysterious “fix” that was running through all of my 10 or so HTML pages to remove the IFRAME tag (of which only index.html was affected by the way). If it really was only me that was affected, why did they write that script. Did they even write it? Did they lie to me? Why wasn’t it removed from the index2.html page as it exists right now?

I’m still pretty upset about the whole issue and would switch web hosting companies away from them in a second if I got a referral on a better one. Not that it would have soothed my anger against them at all, but I can’t believe they didn’t even offer me a month of free service to keep my mouth shut. Can’t anyone provide even a tenth of what Dreamhost offers us in the Linux hosting world?

What do you think? Hacked? Are there even any other viable possibilities?

23rd Jan, 2007

Lots to Say

I’ve had a lot to say lately and have often commented to my friends that I needed a blog, but I’ve never had the ambition to perform the necessary upkeep on it and post to it often enough. Maybe I still won’t, but I realize now that at least I’ll have something I can use as an outlet about so many different things I observe and wish I could publicize.

I’m cooking up some pretty good posts on a myriad of different topics, so hopefully I’ll have something worthwhile up here real soon. Until then, this little post will just have to fill the void.

Categories