Yargh! Unless you've omitted something, or I'm misunderstanding, I think you
still have a leak in your design.

To the OP: that's a fairly big question, but here are some thoughts.
Apologies in advance for the length of this.

Here's the big thing about web security: *any* webpage that a user browses
can make requests to your site using the user's (cookie based) session. If
all you check for in a request is that there exists a valid, logged in
session, you're asking for trouble.

Now, other sites can't see the response from requests to your site, so
"read" type requests aren't as big a deal, but "write" requests are. Imagine
a banking type site. To transfer money from this user to another user the
url is like: http://badbank.com/transfer?to=other_id&amount=1000
Even though Bad Bank checks for a user's session, if a malicious site
included the above URL in, for example, an <img src="..."> tag, Bad Bank
would still see the user's valid session and (incorrectly) assume it was a
valid request. Yikes!

Using POST instead of GET doesn't help at all since JavaScript can be used
to trigger form posts. So a malicious page could still make a phony POST
request. Checking the referer header might help, but it doesn't seem like a
very robust solution.

The saving grace is that malicious pages can't access the response from such
requests. So one solution is to challenge all such requests and include a
unique token that must be sent back to verify the request. This was fairly
common pre-Web2.0, and is still pretty common now, in the form of "are you
sure" type confirmations. So now the transaction is like:

Webpage (malicious or legitimate):
http://betterbank.com/transfer?to=other_id&amount=1000
Better Bank: Transfer $amount to $other_id? <a
href="/transfer?to=other_id&amount=1000&token=unique_token">Yes</a>
Webpage (legitimate):
http://betterbank.com/transfer?to=other_id&amount=1000&token=unique_token
Webpage (malicious): Stuck because it couldn't read the response from Better
Bank so it doesn't know the token

With the whole AJAX/Web2.0 revolution thing, much of these types of requests
are being done "behind the scenes", and confirmation pages have gone the way
of the dinosaurs. As a result, it's not uncommon for such sites to fall
victim to this type of exploit.

I think a sufficient solution is to always include a unique token with any
"write" ajax request. The session ID is easy to use as the token, but isn't
perfect. Really, using the session ID makes some sense conceptually. By
forcing the client to send along the session id, the server is basically
asking the client, "do you know who you are?" Malicious sites don't have
access to the session id, so they don't know it, but your legitimate pages
do.

The problem with using a session id is if you're using URL rewriting based
sessions (where you get something like PHPSESSID=<id> or _jsession=<id>
added to your URLs), then links away from your site will include the URL of
the current page in the referer header, so you might leak the session id
there, and then you're back to where you started with not being able to tell
legitimate requests from malicious ones. The best *best* way would be to
generate a token for each request, but that adds a lot of overhead on your
server to keep track of and verify all of these tokens. A pretty good
solution would be to generate a token when the session is created, and store
it in the session, but make sure that nothing ever "leaks" that token
outside of your site. That's what I do.

As far as a framework to do it for you... I don't know of any.

In the long run, I think most of it just gets ignored because naive security
usually ends up being "good enough" in "most cases". If you're building The
Next Big Social Network or something, it probably doesn't matter that much.
If you're working on anything related to money, hire an expert :)

Good luck.

--Erik


On 6/5/07, Michael Price <[EMAIL PROTECTED]> wrote:

Hi Michael,
On my website I have a lot of pages called via AJAX, some of which are
quite sensitive (private messages, user submission data etc.). At the
top of every page I have a PHP include which, as well as opening the
database connection etc., also includes code to check and authenticate a
user if login data e.g. a cookie or session is detected.

I also include this include at the top of all my AJAX pages and ensure
any database queries are tightly written to ensure they only pull out
the relevant data.

e.g. logged in user is ID number is 1234, trying to read private message
number 5678, my SQL is:

SELECT * FROM private_messages WHERE to_user_id = 1234 and message_id =
5678.

If the user is then trying to hack the message ID it won't work.
Additionally, if a user isn't logged in then the include and / or script
knows this and can stop the call and print an error message.

I guess what I'm trying to say is apply the same level of security to
your AJAX scripts as you do to the rest of your PHP.

Regards,
Michael Price


Reply via email to