Delayed Launch

I had planned on relaunching Domain Pigeon today, but decided to postpone it for a short while longer. At this point, I’m fairly confident that everything works the way it’s supposed to, but want to triple check just to put my mind at ease. I’m finding myself making minor tweaks, like adjusting the font sizes by 0.1em or changing the shading of green slightly, which is a good indication that I should just do it already.

One factor that has contributed to the delay has been the payment integration. Technically it wasn’t very hard and because I’m using Paypal I don’t have to worry about handling credit cards. The reason its taking time is that I want the price of the service to be an accurate reflection of the value and quality of the site. It has to match in order for it to be received well.

There are pros and cons to charging the initial users:

Pros:

+ You’ll know pretty quickly whether you have a viable business model
+ The people that won’t pay will give you lots of valuable feedback/criticism on what you need to improve
+ You won’t miss out on the traffic spike on the day of the launch
+ You don’t have to suddenly start charging for something a few weeks down the road

Cons:

– Criticism of the business model might draw attention away from the usefulness of the service
– Mistakes in the initial launch are accentuated by the fact that you’re charging for it
– Less buzz (or more, maybe)
– Since the service hasn’t been tested by more than a dozen people, there may be undiscovered bugs

I strongly considered waiting a few weeks before charging, but ultimately decided not to. My fear boils down to the possibility that I’ll have overlooked some embarassing flaw with the site and that these months of preparation will be overshadowed by whatever that is. The problem with that line of thinking is that there’s always going to be that possibility. All you can do is hope that your preparation has paid off and act like a professional in the event of a misstep.

Also, as someone pointed out on HackerNews a while back, you need to move beyond the big launch day mindset. It is important, but, what you do in the weeks following when the traffic has stabilized is what really matters in the end.

A great site with a crappy launch is better than a crappy site with a great launch.

Make Something People Want?

“Mind if I take a photo of you guys?” I asked these two aspiring entrepreneurs.

“Sure, go ahead, but photos don’t put steak on our kids’ tables.”

“And this does?”

One of them looked at me and said with a grin “You’d be surprised.”

Lesson learned: Find a niche and go for it.

Toying with SQL Injection in Rails

I’ve been playing around with SQL injection with Domain Pigeon in an effort to ensure its robustness and to more effectively understand the built in protection offered by Rails. To do this, I set up a simple form with one input (the domain name) which was then used as a parameter for various mehods of searching the database:

Method #1: Direct Insertion (bad)

@domains = Domain.find(:all, :conditions => "name = '#{params[:domain]}'")

This is the classic “don’t do this you idiot”-vulnerable-to-SQL-injection method.

Example #1:

  • Input: holla.com
  • SQL: SELECT * FROM “domains” WHERE (name = ‘holla.com‘)
  • Output: 1 domain

Example #2:

  • Input: holla.com’ or ‘a’=’a
  • SQL: SELECT * FROM “domains” WHERE (name = ‘holla.com’ or ‘a’=’a‘)
  • Output: The entire table of domains

The first example works because the input is crafted to break the query. In the second example, inserting the input directly into the query results in the entire table of domains being displayed.

Verdict: Fail.

Method #2: Bind Variable (good)

@domains = Domain.find(:all, :conditions => ["name = ?", params[:domain]])

This method, know as the Rails bind variable facility, is the recommended method for defending against SQL injection. From Agile Web Development with Rails: “It will add quotation marks if the elements are strings and quote all characters that have a special meaning for the database adapter…”

In this case (sqlite), that means adding an extra single quote before every single quote within the string.

Example:

  • Input: holla.com’ or ‘a’=’a
  • SQL: SELECT * FROM “domains” WHERE (name = ‘holla.com” or ”a”=”a‘)
  • Output: Nothing

Note: for the second example and all the WHERE clauses in the examples that follow, those are two single quotes, not one double quote.

Verdiect: Secure.

Method #3: CGI::escapeHTML and escape_html (bad)

@escaped = CGI::escapeHTML(params[:domain])
@domains = Domain.find(:all, :conditions => "name = '#{@escaped}'")

Until playing around with this, I thought that escape_html (alisted to simply h) was only available to the views and in order to escape HTML within a controller, you had to use CGI::escapeHTML. However, you actually can escape_html using ERB::Util:

>> ERB::Util.html_escape('Then he & she yelled "Hey!"')
=> "Then he & she yelled "Hey!""

Therefore in this example I could have also written:

@escaped = ERB::Util.escape_html(params[:domain])
@domains = Domain.find(:all, :conditions => "name = '#{@escaped}'")

and achieved the same thing.

However, you don’t want to do this. html_escape and escapeHTML only escape ampersands, double quotes, and greater than and less than symbols. Since they do not escape single quotes your application would still be vulnerable to SQL injection:

These two methods should be used for displaying data in a view; they should not be used to prevent SQL injection.

Example:

  • Input: holla.com’ or ‘a’=’a
  • SQL: SELECT * FROM “domains” WHERE (name = ‘holla.com’ or ‘a’=’a‘)
  • Output: The entire table of domains

Verdict: Fail.

Method #4: find_by methods (good)

@domain = Domain.find_by_name(params[:domain])

Example:

  • Input: holla.com’ or ‘a’=’a
  • SQL: SELECT * FROM “domains” WHERE (“domains”.”name” = ‘holla.com” or ”a”=”a‘)
  • Output: Nothing

The find_by methods properly escape their input.

Verdict: Secure.

Method #5: Conditional Hashes (good)

@domains = Domain.find(:all, :conditions => {:name => params[:domain]})

Example:

  • Input: holla.com’ or ‘a’=’a
  • SQL: SELECT * FROM “domains” WHERE (“domains”.”name” = ‘holla.com” or ”a”=”a‘)
  • Output: Nothing

Using hashes is an alternative method for querying your database and it also properly escapes the user’s input.

Verdict: Secure.

Bottom line (literally):

Don’t insert user input directly into SQL queries, either alone or using HTML sanitization methods.

The Importance of Following Through

[flv:http://www.mattmazur.com/allinexpert/allinexpert-stox.flv 480 368]
ALL IN Expert demo on Stoxpoker.com

While exploring Analytics earlier I noticed there were people reaching this site by Googling “All in Expert”. I noticed this in the past, but attributed it to the one or two 2+2 posts I made about it or to the semi-popular blog post about the lessons learned from the experience.

I did a little exploring to see if I could track down the source of the traffic. To my surprise, I found the above video (which is a bit jumpy) on Stoxpoker, a popular poker training site. The full video is 52 minutes long (the above is only 13 because its a free preview) and the speaker, James Sweeney, makes extensive use of ALL IN Expert to solve practical, everyday NL Holdem situations.

I can’t tell you how amazed and happy I was to see this. Some guy I never met was demoing a piece of software that I thought was all but forgotten. Moreoever, in the video’s followup thread, there were other members praising with the software as well:

Just wanted to say I really liked this one and thanks for the link to allin expert, what an awesome program. – hymmelsydk

Glad you liked the video! and yea…AIE is an awesome program…i used it to solidify my 3b/4b game and also play closer to perfect against SS’s. Many applications for it if you used it correctly =) – SplitSuit

Goes to show you why following through is so important.

The major mistake I made with ALL IN Expert, next to trying to create software no one was asking for, was abandoning it so quickly. When it seemed that no one wanted it, I despaired, took down the site, the started on something new (Domain Pigeon). I should have promoted it, sought feedback, iterated, and gone from there. After a few weeks and lots of feedback later, I would have been in a much better position to decide whether or not to continue with it.

Lesson learned: Don’t give up, follow through and give it time–you never know what will happen.

“Engines pumping and thumping in time”

Here’s a rundown of the last few days. It’s been busy.

Sunday

After returning from a ski trip to the Poconos last weekend, I spent most of Sunday preparing Domain Pigeon for an initial feedback launch on Monday.

Sunday’s major task was integrating DP with Paypal. I didn’t want to charge any of the initial users, but I did want the system to be in place for the HackerNews launch.

Thanks to my work with ALL IN Expert, I already had some experience with their Instant Payment Notification system, which is how Paypal notifies a site when a transaction is made. When DP receives the notification, assuming it’s legitimate and the payment goes through, DP authorizes your account and allows you to sign in.

Of course, there were a few bumps along the way. For one, the Business Name on my Paypal account was “ALL IN Expert”, which needed to be changed to “Domain Pigeon”. Their support site pointed me to a page which it said would allow me to change the name. The page was there, but no such option. I later found out that they had removed this option and that their support site hadn’t been updated to reflect the changes–doh. It speaks highly of Paypal that I assumed it was my fault that I was having trouble updating the name and not a problem with their support site. After spending 45 minutes trying to figure out how to make the change online, I called up their customer service.

A woman with a heavy accent told me to use the Contact form on their website to make the request and that it should only take a few minutes for the change to be made. The next morning rolled around and Paypal still said ALL IN Expert. I wanted to get some feeback from people, but didn’t want to until the Business Name was fixed. (In retrospect, since I allowed everyone to bypass the payment step this really wasn’t as time sensitive as I first thought.) Frustrated, I called again and explained the situation to another woman. She apologized for the delay, told me to wait a minute, and promptly made the change to “Domain Pigeon”. So easy–why didn’t the first rep do that? The next day someone responded to my initial request that somewhat answers:

We have reviewed your request regarding the name change on your account. Unfortunately, we are unable to process your request at this time.

Please provide the following documentation in order to complete your name change request:

* Some form of proof, which contains both the business name, as well as the individual’s name that will be added to the PayPal account. Acceptable forms of proof include Articles of Incorporation or the equivalent, current business license, any business documents/forms approved by and filed with the business’s governing state, or a bank statement for the bank account registered on the PayPal account. The bank statement or other business documents must show the business name and the name of the new contact person. If the bank statement shows the business name only, please include a copy of the signature card.

etc etc

Articles of Incorporation? Eh, not quite yet.

Fortunately, my call on Monday got the job done, though I don’t think it was supposed to happen that way. It probably would have taken a week or more to do it per the email instructions. I figure this new policy, which prevents you from making Business Name updates online, is an attempt to reduce phishing scams (imagine you click through to a site and the top of the Paypal page says “eBay”). What I don’t get is why they need a bank statement if my Paypal account is already linked with my bank account. What’s the point? I’m probably missing something. Regardless, it’s a pain if you legitimately want to make change your Business Name.

Sunday I also had the pleasure of doing some extensive cross browser testing, which I had only done a bit of in the past. I used a site called BrowserShots, which tests your site on browsers of your choosing and then shows you screenshots of the results. To sum it up: If IE6 was a glass window, I’d throw a brick through it. The right column on my site was in the entirely wrong area and my beautiful logo, which was in PNG file, wasn’t transparent. It was a big, nasty block at the top of the site. Sigh. I fuddled around with the style sheet and think I just got lucky making the columns work fit correctly across all the browsers. I changed the logo to a GIF, but not without losing some of the clarity and having to add a small black outline to the logo so it appeared correctly.

Monday

I had the day off due to MLK Day, giving me plenty of time to work. I set my alarm for 8, knowing that if I didn’t I’d sleep till noon, losing a big chunk of my day. You can’t control everything, but you can control your effort.

I did a few last minute checks to make sure all the major functionality worked (which unit testing confirmed), and then sent a link out to a few friends on Facebook and AIM. Most of them knew I had been working on it and were happy to provide the initial feedback. I didn’t want to charge them to test, so I let them bypass Paypal by starting their passwords with @@. (Note to self: Remove this from the next release.)

Their responses were overwhelmingly positive and their feedback helped me fix a few bugs that I had not noticed. After all, your unit tests are only as good as what you tell it to test.

One friend, John, an Orlando-based web developer that I knew well in high school, provided the best feedback. He knew to test things that the others wouldn’t, which helped a lot. He said the site was good, but a bit boring. Boring? Hmm.

I didn’t know what to think. At first I was a little offended. It’s not boring. You’re boring (just kidding). But it got me thinking. He was right. As it was, people were going to arrive and think “Hmm, this is interesting” instead of “Wow, this is interesting”. He suggested a copywriter that I could contact if I wanted help, but I’m going to try a few things out before I resort to that. Do it yourself first, right?

This also made me realize that I might not be so good at branding. It’s something that’ll wind up being very important in the long run and something that I haven’t given too much thought to so far. It might just happen as a result of my design decisions, but, I feel like there’s a lot there that I’m missing. For example, check out Tyler Cruz’s blog. Whenever I think of branding, I think of this guy. I visited the site once and yet months later I still remember his name. Oh, and Donald Trump too. Damn him and his crappy golden books. Disclaimer: I’ve never read one… they just look crappy.

After I made a few of their recommended changes, I sent an email to Philly on Rails, a local group of Ruby programmers group that I demoed DP to a few weeks back.

Their feedback was generally positive, but again, it lacked the “wow” that I had hoped for.

One guy, Nick, pointed out that on IE there were JavaScript errors which needed to be fixed. It turned out that I was referencing some elements which only existed for signed-in users. Firefox didn’t show any alerts, so I wasn’t aware of it until he pointed it out. (I had tested it on IE, but, failed to notice the little alert icon signifying JavaScript errors).

There were a few other things that needed work. For one, it wasn’t clear to people that the search box on the left column was to search unregistered domain names on DP, not unregistered domain names in general. One person said “I searched for ‘poker’ but nothing came up.” I added a small label above the box explaining the scope of the search. One BIG todo item is to add a feature that helps people generate unregistered domain names based on terms of their choosing, similar to BustAName.com, as opposed to merely searching the existing ones on DP. That was the original idea that prompted DP, but it’s something that’ll have to wait a few iterations to introduce.

I made lots of small changes throughout the day, including lots of wording changes. “Find unregistered domain names for your websites” or “Find an unregistered domain name for your website.” or “We help you find unregistered domain names for your websites.” etc. Probably a minor thing, but, all these small improvements are +EV in the long run.

Speaking of +EV, I got some skepticism about the business model which boils down to “Who is going to pay $14.95” to which I usually respond, “Me”. The truth is I’m not entirely sure what the reception will be with the larger internet community and I probably won’t know until the site starts receiving heavier traffic. The best indication I have is that it’s something I would use and pay for. We shall see.

Tuesday

So, this was fun: I get home from work and my PC doesn’t power on. I use my Macbook for the majority of DP’s development, but certain things I keep on my PC. Notably, the software I wrote to scan for unregistered domain names is on the PC, including the results of several hundred thousand WHOIS queries (to check domain availability).

I have almost everything backed up. I say almost because the WHOIS software and the database were not. Instead of getting straight to work, I drove out to Circuit City, where I brought the PC to see what they can do. “Sorry, we’re going out of business, we can’t help you. You can try across the street at Best Buy.” Yeah, okay.

Long story short: Bad power supply, the guy was able to fix it on the spot, and by the end of the evening I had the PC back and working just as it did the day before it died.

Lesson learned: Stop sucking at hardware.

Lesson learned: Backup, backup, backup.

I was lucky that it was just the power supply. Had it been the motherboard, it would have pushed back the next phase of DP some time (after all, I’d have to start the availability searches all over again). There are new ways I picked up that I could do this quicker, but still, I’m fortunate that nothing was lost. Additionally, imagine if DP had really launched and was receiving lots of traffic and sign ups. Then what? Long nights is what.

I took down the site, which currently has an ugly maintenance notification on the homepage. I want to fix up a few things before the HackerNews launch, which should be around next Monday, 26 January. I want to add some more unit tests, add a little more bang to it, and refactor some inelegant sections of code, among other things.

For example, I realized the default sort on the unregistered domains is by date added, which means that whenever I add a new batch of domains the homepage will be entirely white because the domains that used to be there will get pushed back a few pages. When new people arrive, it’s important that they see lots of green shading to entice to click a domain. As it was, every time I added domains, the shades would disappear, giving people the mistaken (hopefully) impression that there wasn’t much traffic to the site.

How do you fix this though? Sorting by popularity would guarantee a colorful homepage, but it would result in large rows of similar shadings of green for domains with a similar number of views. That’s not only ugly (try sorting by popularity when you get a chance), but it doesn’t reflect the dynamic nature of the site.

The solution, for now, is to compromise. At the top of the homepage, DP’s top unregistered domain names will be displayed in their own little section. Below those, the most recently added will appear as normal. This accomplishes two things: 1) Immediately shows people the best unregistered domains on DP—”DP is awesome!” and 2) Encourages them to explore the recently added and find other gems—”Wow, this is interesting”. And, despite my initial fears, it doesn’t look half bad either.

Lot’s of work ahead… It’s an exciting time.

“Reluctantly crouched at the starting line”

“The race is long and in the end, its only with yourself”

Progress on Domain Pigeon (DP from now on) has going well. Several weeks ago I estimated the site was about 90% complete, which turned out to be very optimistic. Deploying a Rails app, especially for someone with no experience, takes time to do properly. I also made the mistake of not writing unit tests until recently. The “Aha!” moment came the first time I saw DP running on Dreamhost and realized there wasn’t an easy way to make sure everything worked properly.

Lesson Learned: When the roads are covered with fog, plan for extra travel time.

Lesson Learned: Prepare thoroughly. When it comes time act, you’ll be glad you did.

The plan was to launch several days ago, but some last minute preparation and a ski trip this weekend has postponed it.  I’d like to be around to respond to the initial feedback and to address any problems that arise.

Also, updated the blog layout, as the old one was, well, bleh.

And finally, a good FastCompany article titled “What Should I Do with My Life“, courtesy of HackerNews. Here’s a quote:

But the article itself flipped that connotation inside-out. It argued that with the economy in a tailspin, it was unsound economic theory to have millions of drone workers shuffling to work every day doing jobs at quarter-speed they didn’t care about, so they weren’t very productive at, and certainly didn’t add value at. The economy would never get kick-started if our workforce was uninspired and didn’t innovate. So the article — really a manifesto — suggested that the way to get business going again was for its basic building blocks — the workers — to do something they were really good at, or were inspired by, or cared about, where they would work extra hard, and innovate their way out of this black hole. Now it was not a permission slip to quit your job, nor a doctor’s note to take a year off (I’ve never taken more than two weeks off in 23 years), but it did suggest the economy might be better off, long-term, if the square pegs found their square holes and the round pegs found their round holes, rather than everyone just wondering where the next big thing would be and gravitate to it like moths.