6

From two different applications, I was able to send cross-origin requests. Though the browser returns a "cross origin" error my server is still receiving and executing the request. For example, from a remote site I can invoke cross domain request using

$.ajax({
        xhrFields: {
            withCredentials: true
        },
        data:{ my: 'a' },
        url: 'http://MyApp/Page',
        type: 'POST'
})

I know that the browser does not return the response to script, but my server page still executes.

Let's say a innocent user is logged-in a site http://example.com. This application will accept POST requests to insert a record. When the innocent user visits http://HackerSite.com, the http://HackerSite.com will be able to send a POST request to http://example.com via AJAX. How can this be prevented?

Matthew
  • 27,233
  • 7
  • 87
  • 101
user960567
  • 2,461
  • 4
  • 16
  • 16

4 Answers4

17

As well as via a cross-domain AJAX request with credentials, the POST in your example could also be sent by using a standard form without AJAX:

<form method="post" action="http://MyApp/Page" name="hiddenFormInIframe">

<input type="hidden" name="my" value="a" />

</form>
<script type="text/javascript">
  document.hiddenFormInIframe.submit();
</script>

This would also include cookies in the request.

The vulnerability you are talking about is called Cross Site Request Forgery.

You can guard against the POST being submitted from another domain by sending a header in your AJAX request and then checking the header server side. A common header to use is X-Requested-With. This header cannot be sent cross-domain via AJAX due to this header not being in the safe list (without CORS being enabled on your server). A HTML form cannot send this header either.

Only the following headers are allowed cross domain via AJAX:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type

The X-Requested-With header is automatically set when using JQuery and you can check for this in your ASP.NET code.

However in the past there have been some exploits via plugins such as Flash where headers could be set that were not possible via a browser (e.g. Referer) so to guard against this it is recommended to use the synchroniser token pattern which involves setting a token in a hidden field or in a header. This token will be validated as well as the authentication cookie for all requests with side-effects. That is, requests that change, submit or delete (normally POSTs).

For more info on CORS see here: http://www.html5rocks.com/en/tutorials/cors/

SilverlightFox
  • 33,408
  • 6
  • 67
  • 178
  • A canonical resource about this is OWASP's wiki page about CORS: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29 – freddyb Sep 08 '14 at 14:03
  • 1
    @freddyb: CSRF you mean? Thanks, I've just changed the link in my post. – SilverlightFox Sep 09 '14 at 07:54
  • @SilverlightFox, can you please answer this question of mine similar to this? Suppose there is an application which isn't available to whole network and available only internally (say some company network). XSS is present and an attacker wants to redirect the victim to a malicious site hosted externally. Can it happen? In what cases, it can be stopped? By WAF? – one Jul 06 '16 at 05:40
2

Cross-domain polices prevent domains from reading information in some circumstances from other domains that don't explicitly allow it with CORS headers. But, that doesn't prohibit cross-origin requests from going out. The request in your example should be hitting be a "pre-flight" OPTIONS request: A probing request to see whether the "actual" request will succeed.

Part of the solution may be to terminate OPTIONS requests early in the process: as soon as it can be determined whether the probing domain is allowed access.

A more complete solution may entail API access tokens. On each page-load, generate and embed an access token. Include that token in all API/AJAX requests. Any request that doesn't include the token should terminate early.

svidgen
  • 711
  • 5
  • 13
  • And just to be clear, this isn't a 'session' token. I've seen auditors get confused with things like this. Two separate animals. – Andrew Hoffman Sep 05 '14 at 15:09
2

The best idea here is as other stated, use CSRF tokens. The most simplest form is to add the users IP adress as a identifier in a database, and then add a random value for this IP. The random value is the correct token.

For example, when someone visit your page to get the form, take the users IP, lets say: 12.12.12.12.

Add into a key-value database along with random value: 12.12.12.12 = ljghsdlghdlghdlshsdflgd (note, DONT use the example "ljghsdlghdlghdlshsdflgd", instead generate a pseudo-random value of a sufficent length)

Add this pseudo-random value as a hidden field.

On submit, use the users IP to look up the suggested reply. If the actual reply match the suggested reply, request is accepted. On ANY attempt to submit (regardless of successful or not), delete the record in database, if any exist. This means any CSRF attempts will invalidate the real tokens for the targeted user, and each token will be one-use only. Depending on what type of database it is, its either simple as setting the key to "", or simply delete it.

Like: 12.12.12.12 = "".

A good idea is also to expire these values by time, both for security, but also to get rid of unneccessary data in the database. Depending on if its a forum, it can be wise to have a couple of hours as a validty time, while if its a simple form to lets say, change a password, then its enough with about 5 minutes as validity time.


Another idea is captcha, which also prevents automated client-side attacks, like virus infections and automated remote Control trojans. Depending on sensitivity of the web application, it can be a good idea to use Captcha AND CSRF-tokens in unison. Do not follow the advice to use the same token for a whole session, its a bad idea security-wise since theres a small possibility that the user might be tricked into submitting a token to a attacker aswell to the site. A single-use token will be wortless to the attacker, but a multi-use token can be as dangerous as a session, which CSRF is designed to protect.

So use single-use tokens, its the best security compared to multi-use tokens, ans also single-use tokens does not need to be protected in transit as the CSRF article in one of the other replies suggest.

sebastian nielsen
  • 8,779
  • 1
  • 19
  • 33
1

The server page should not execute if you have everything configured correctly. The request should be rejected before it has a chance to get that far down the line.

I suspect you have configured your application/server to have Access-Control-Allow-Origin set to *. There are a number of places this can be done, but a common one for asp.net application is in the web.config(s). Example:

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>

I would check to see if you have this set anywhere in your app. Keep in mind that asp.net can have multiple web.config files and will use the settings from the closest one. For example say you have the following directory structure:

/MyProject/ajax/stuff

If you have a web.config in your MyProject directory and a web.config in your stuff directory, files in stuff will use the web.config from stuff wherever it can.

Let me know if that is confusing.

Also, I believe this can be configured in IIS as well, so take a look at your headers there too.

Abe Miessler
  • 8,155
  • 10
  • 44
  • 72
  • I have tested the scenario in cross browser. The browser is sending the cookie. Please test this. I am using ASP.NET/WebForm IIS 7.5. There is nothing Access-Allow-Origin header – user960567 Oct 25 '13 at 13:27