NYCPHP Meetup

NYPHP.org

[nycphp-talk] NEW PHundamentals Question

Chris Shiflett shiflett at php.net
Tue Feb 10 02:19:32 EST 2004


--- Jeff Siegel <jsiegel1 at optonline.net> wrote:
> Almost every website has an HTML form for visitors to complete. But
> how do you know that the person who completed the form did so through
> your website? The question for this month's PHundamentals article is:
> What method(s) do you use to assure that no one has 'spoofed', i.e. 
> 'forged', a form submission?"

Nice question. :-)

First, we must realize that a forged form submission really means a forged
HTTP request, either GET or POST. If your script differentiates between
the two (e.g., you don't use register_globals and rely on $_GET and $_POST
instead), a forged request must reproduce the request method expected.
Otherwise, either method will do. This can be important, depending on the
type of attack.

I briefly read through the replies so far, and here are some comments:

1. Captchas. As Jon mentioned, these are pretty popular, and they're quite
effective. But, it's also unrealistic to use a captcha for every form.
Your users will get annoyed. Also, as Adam mentioned, they're not
foolproof. I assume the link he gave describes using porn sites to help
provide answers to captchas. This is a very creative attack, and to be
honest, it could be used to subvert many safeguards.

2. Shared secrets. Whether you call these one-time tokens, hashes, or
whatever, the idea is typically the same. You create a secret that is
intended to only be known by the server and the legitimate user.
Implementations vary widely, but these methods are my favorite. They're
transparent to your users, and they're difficult enough to exploit that
they protect against a number of different attacks, some you may not even
be familiar with.

One implementation would be to store the secret in the user's session:

$secret = md5(time());
$_SESSION['secret'] = $secret;

And then use it as a hidden form variable in the form:

<input type="hidden" name="secret" value="<? echo $secret; ?>" />

(Every time you display the form, you would regenerate this secret, so
that the user always has a current, fresh, and correct secret).

The receiving page can check this. You can improve the security of this
method by restricting the timeout window (rather than relying on the
session timeout, which might be too large for your needs), or you can
allow more user flexibility by keeping a short history of valid tokens
(which might help avoid potential problems with the user's frequent use of
the back button, although there really shouldn't be any). As with most
things, the best solutions depends on your application, but something is
better than nothing.

3. HTTP Referer. Do not use this for anything except taking statistics or
for your own curiosity.

4. IP address. See 3.

Also, I saw a comment about IP address checking and how it helps to
prevent CSRF. IP address checking adversely affects legitimate users and
does nothing to prevent a CSRF attack (because the request is coming from
the legitimate user). See here for a diagram that may better illustrate
this:

http://shiflett.org/talks/apachecon2003/19

As Hans mentioned, this is all about forging requests. It is important to
understand what this means. Let's assume this HTML form, that is located
(hypothetically speaking) at http://example.org/form.php:

<form action="/submit.php" method="post">
<select name="myvar">
     <option value="foo">foo</option>
     <option value="bar">bar</option>
</select>
<input type="submit">
</form>

It would seem safe to assume that we will be able to reference
$_POST['myvar'] and have it be either foo or bar. Assuming the user
selects foo, the request will look something like this:

POST /submit.php HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded
Content-Length: 9

myvar=foo

That seems simple enough, right? Consider someone from sco.com who writes
a similar script, only it looks like this:

<form action="http://example.org/submit.php" method="post">
<textarea name="myvar"></textarea>
<input type="submit">
</form>

With this simple form, this evil person (Darl?) can now submit anything as
the value of $_POST['myvar']. In fact, there was nothing to prevent the
creator of the form from including unexpected form variables or anything
that can be achieved with an HTML form.

More sophisticated attacks might just use raw HTTP rather than bothering
to generate a form for every desired type of attack. You can telnet to
port 80 and type in one of the requests I listed above. In fact, try this
one to log into the NYPHP forums:

POST /index.php?act=Login&CODE=01&CookieDate=1 HTTP/1.1
Host: forums.nyphp.org
Connection: close
Referer: http://forums.nyphp.org/
Cookie: session_id=12345
Content-Type: application/x-www-form-urlencoded
Content-Length: 44

UserName=myname&PassWord=mypass&CookieDate=1

Just change myname to be your username, mypass to be your password, and
the content length to be the new length of the POST data. Of course,
you'll also want to change the value being sent as the session_id, else
you'll become a victim of my session fixation attack. :-)

Even more advanced attacks leave out the typing and use specific software
written to automate attacks. Literally anything sent from the client is
suspect, and this is why you should at least hide behind a mature Web
server such as Apache rather than trust something like IIS to handle all
of the crazy stuff that people try. I'm not kidding about this or bashing
Microsoft either; it's seriously a bad idea, and hopefully you now
understand why.

Hope that helps.

Chris

=====
Chris Shiflett - http://shiflett.org/

PHP Security - O'Reilly
     Coming mid-2004
HTTP Developer's Handbook - Sams
     http://httphandbook.org/
PHP Community Site
     http://phpcommunity.org/



More information about the talk mailing list