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.