Web Application Security I
Who's Submitting That?
or maybe how Jeff Kent got in the 2000 All-Star Game
Created On: June 10th, 2000
This article focuses on the security of web applications. Specifically highlighting a common mistake that developers make which is not validating who's submitting the data. It is intended to point out errors made so they can be corrected, and not exploited.
Using Perl and the LWP module I can create requests for web pages. These requests can be common GET requests, or can even be POST requests. In both cases I can pass parameters to the web page from the Perl script, which allows me to simulate filling in a form. Knowing what name-value pairs to submit is quite easy since I can view the source of any form using my browser.
So I can create a script and set it up to submit values to a server which mimics actually filling in the form. Also in Perl I can grab the results and do what I wish with them. This is common knowledge and is how many web applications such as stock quotes, search engines, etc... work.
But by doing this I can submit a form bypassing any JavaScript validation since that is in the "real" web form, and not my script, which allows me to enter in "bad" data possibly breaking a page, or causing an error. Also, I can create loops, and apply other programming logic to do all sorts of things.
OK, so you want an example.
Major League Baseball's All Star Balloting is on-line which allows people to submit 25 votes per e-mail address for their favorite players. Since I own my own domain, I have an infinite number of e-mail addresses, Example: p0001@mkaz.com, p0002@mkaz.com, and so on. So I can submit an infinite number of valid votes. Since that would take a while, I can write a script to do it for me.
I will not give the script here until after the All-Star game. The All Star Balloting pages are at http://allstarballoting.seasonticket.com/
The first page asks you for your e-mail address and submits it to another page. There are a couple of extra fields there but they don't do anything important. The second page creates a UserID and a SessionID and then using JavaScript (blech!) forwards you on to the ballot passing along these IDs. The ballot page contains the IDs in hidden fields and along with your votes submits to another page which records the results and then forwards you along (JavaScript again) to a final page.
Using JavaScript to moving you along from page to page is rather silly. JavaScript runs in the browser, if it runs in the browser the text/data can be read by anyone. Not that their security method would be any different if they didn't use JavaScript, since the IDs are stored as hidden fields in the form, which do show up when you view the source.
So I create two requests one that submits my e-mail and setups the UserID and SessionID parsed out of the returned page. These variables are used with the second request which passes the IDs along with my voting creating a valid vote. Now all I have to do is loop my voting request 25 times, and loop the whole script using a different e-mail address as many times as I want.
Script: submit_allstar.txt
I was able to submit over 25,000 votes for Jeff Kent in the 2000 balloting. Each set of 25 votes was from a unique address so according to their user agreement, valid. I'm pretty sure these were counted because I would receive 10,000 e-mails anytime the MLB sent out an e-mail newsletter. Cleaning up the spam I received was a different problem.
Solution
Validate who submits the page to you. Don't allow a page to be submitted
that does not originate from your server. This can be done various ways
depending on your server and language. The easiest way for me is programmatically,
along with the data the server submits the referring document, this should
come from your server or one you trust.
Example in PHP
if ($REQUEST_METHOD=="POST") {
echo "Post Received";
if ($HTTP_REFERER == "http://my.server.com/mypage.html") {
echo "Valid Page";
} else {
echo "Not from the right page.";
}
}
?>
There is also an Apache directive which you can use which would restrict it at the server level. The other solution employed these days is to display an additional box of scrambled letters that the user must type in.
Related Links: