For my more technical readers, stick with me here while I provide a bit of background, or jump ahead to the technical stuff.
Meet Colt
Colt is shelter dog at the Humane Society for Hamilton County in Noblesville, Indiana. He is deaf but has learned sign language for all his basic commands. He is a great dog who has found himself homeless like thousands of others. Is he not both the cutest and coolest dog you’ve never met?
The Contest
Hallmark Hall of Fame is holding a contest to find the “cutest dog of the season” as part of their promotion for their upcoming movie, “a Dog Named Christmas.” The winner of the contest receives a $1000 cash prize PLUS a $1000 donation to their favorite animal charity. As Colt’s owner is an animal charity, him winning would result in $2000 going to the Humane Society for Hamilton County, IN.
The Situation
Yesterday I got an email encouraging me to support Colt in the contest (to view this link you must create an account and log in, or use the username and password I created: thisisatest/password). Not being able to resist that face of his, I decided to do everything I could to make sure he was noticed.
The Problem
But there are so many dogs! The signal to noise ratio makes it where there is almost no chance for any one dog to run away with the voting, but somehow, there were a few. Knowing immediately what was happening, that the leading dogs were all running scripts, I decided to move Colt to the front page to put him in front of as many eyes as possible.
The Technical Solution
This is where things start getting a little bit gray. I wrote a script to automatically and continuously vote for Colt. I’m not proud of what I did, but know that I did so with only the best intentions. If you’re not a technical person, you may want to resume reading where I discuss making things right.
Taking a quick look at their voting page you can see this chunk of code:
<div id="dog-vote-link-8068-container" class="dog-vote-link"><a id='dog-vote-link-8068' href='/vote/8068/21770/e158e2a0b631ca319a361addabe1dab4/1259213400/full' class='dog-vote-link'><img id='dog-vote-link-8068-img' src='/sites/all/themes/adnc/images/btn-vote.png' title='Vote Now' alt='Vote for This Pooch' width='456' height='252'/></a></div>
The contest uses jQuery to hijack that link and make a simple request for the same. Super-easy, all I have to do is set up a script to repeatedly request that URL. Well, not quite. The URL changes on each page request, so I would at least have to make a request and parse that page before I could vote. As it would turn out, that was the only roadblock I faced because it is the only protection mechanism the site implemented, and so Colt was off to the front page. The simplified code to vote for Colt looks something like this:
#!/bin/bash
for i in {1..2000}
do
DOG=$(curl --silent http://hhofdogcontest.com/dogs/colt-nov-23 | grep "a id='dog-vote-link" | cut -c 98-159 | sed "s/\'/http:\/\/hhofdogcontest.com/")
curl --silent -O "$DOG"
echo "$i of 2000"
done
How to Create a More Secure Contest
Simply stating the attack only covers a third of topic. There is also the discussion of the problem space and proposing solutions to help prevent this from being a problem in the future.
This problem space falls into the same as spam, meaning there is no easy solution. The typical solution to spam is a captcha, with the goal being to verify the humanity of the person on the other end of the connection. However, in this scenario, we’re not only trying to verify the humanity of the requester, but also the uniqueness. Note that you do not have to log in to vote for the dogs that haven’t been unpublished.
There is a fine line that must be walked and the best solution probably requires integrating an external trusted-but-verified network, like Facebook. This type of contest is a prime example of a way in which either Facebook Connect or the Facebook App Platform could have been used to better meet their goals.
If you don’t wish to involve such an outside network, you could also go with a more old-fashioned approach:
- Require completion of a captcha for all votes (either in a registration process or per vote).
- Only allow one vote per IP address.
This is less optimal, but will still reduce the problem to only those with intentions to create trouble. Proxies and a mechanical turk setup for captcha solving can certainly still result in gaming the system, but the investment is much higher than spending 5 minutes writing a script.
A More Devious Plot
As you can see, there are a lot of problems with the current setup, but there is also a problem with the way in which Hallmark Hall of Fame has responded. They have simply removed the profiles of dogs who appear to have received votes that are illegitimate. With only a few minor modifications the above script could take a scorched earth approach and vote for all of the highest rated dogs, hoping to cause Hallmark Hall of Fame to remove them from the contest. This could also accomplish the goals of a fraudster without ever specifically identifying the perpetrator.
Getting It Right
If Hallmark can implement either of the solutions I proposed above they can restore some legitimacy to this contest. As it stands, there is absolutely no trusting the vote totals. To make it fair to all, they should consider completely resetting the vote totals for all of the dogs to zero and begin anew with a more secure process.
Fixing What I Broke
If you went to view Colt’s contest profile you probably noticed “UNPUBLISHED” printed in huge letters on the page. As a result of what I’ve done, the contest organizers have pulled down his page. It should be noted that I would benefit in no way whatsoever from Colt winning this prize and I was simply trying to help the Humane Society for Hamilton County, IN.
I encourage you to send email to the people behind the contest urging them to reconsider. To make it easy, I’ve drafted some text that you can use as a starting point:
Penance
First, Hallmark Hall of Fame, I do freelance web development and have experience with Drupal. I am happy to assist in implementing either a Facebook Connect or one-off solution to your spam problem at no cost.
Second, in the event that the approach to this contest is resolved with a solution I am comfortable with (detailed above), I will match any dollars from the grand prize donation that go to an animal-related charity up to a maximum of the $2000 cash prize value.
Conclusion
I encourage everybody to go visit the Humane Society for Hamilton County, IN to donate as they are able.
I’ve done this before, though it was easier because it was just always the same link. They just used cookies to disable the links once you voted. Really bad system.
I only did it once the vote totals for some competitors were so high that it was clear others (with much worse candidates) were also cheating.
Since they make you log in, it seems like a reasonable system would be to only allow 1 vote (or 1 vote/day, or whatever they want) from each account. Sure you could make a bunch of accounts, but that at least puts up a barrier. Your solutions are better, but since they require accounts, I have no idea why they don’t at least do that.
Also, please learn a real scripting language. Bash is an ugly, ugly language. I’d give Ruby a try. It’s really a very nice language. I can definitely see why people would hate Rails because it’s so opinionated, but straight Ruby is wonderful. And it’s got great regexp support, so it’s great for screen-scraping and text parsing like this.
Yep, it was definitely a situation where all of the "leaders" were running scripts, so I was simply leveling the playing field. After seeing how these contests are often written on the backend I have little faith that they will be effectively managed.
I’ve added a note that they do not actually make you login to vote, only to view Colt’s unpublished profile do you need to login. And their registration process doesn’t verify email addresses or require a captcha and logs you in immediately. And they don’t map votes to users either.
In this case I think bash is the right tool for the job. I have a friendly little interactive shell for free and all of those old single-purpose UNIX utilities are super-quick. My script was by far the fastest of all of those that were running, and I’m sure there are more optimizations I could make.
As for more complex stuff, I’ll go with Python. *grin*
I guess. I refuse to use bash unless absolutely necessary because its syntax is so bad, but it is true that grep and curl and such are pretty nice and very fast.
Main thing is that I don’t see speed as being the limiting factor here. Even a ruby script doing this would take pretty little RAM, so you could run 10 in parallel no problem. Wanting it to look less like cheating and not overloading their servers are the limiting factors.
And Python is fine with me. I know some people who really like Python. I want to give it a try sometime. I just hate bash.
Nathan,
While I can appreciate why Colt’s story caught your attention, your actions in increasing his votes using this script resulted in him and our shelter being disqualified from the contest. We have now missed the opportunity to win $2,000 our shelter desperately needed. I was alerted by the agency operating this contest for Hallmark that it was your program that resulted in that disqualification.
Not only am I disappointed because I think Colt really could have won, but the thousands of people who legitimately voted for Colt are as well–their efforts wasted.
So since Colt inspired you to put together this program, I hope you’ll see fit to send a donation to our shelter in light of the outcome. You can send a donation to our facility at:
Humane Society for Hamilton County
1721 Pleasant Street, STE B
Noblesville, IN 46060
Rebecca Stevens
Executive Director
Humane Society for Hamilton County