Thursday, September 20, 2007 6:40 AM Kazi Manzur Rashid

Asp.net Ajax Web Service Security

Today, I found another interesting post in Asp.net Ajax Web Service Forum. How do you ensure that your web service is called from your aspx page. The goal is to protect your web service from unauthorized usage. Certainly, you can clear the protocols section of the web.config but it will not make any impact calling the ajax enabled web service. You will still be able to call the web method with the following code:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("YOUR URL");

request.ContentType = "application/json; charset=utf-8";
request.Method = "POST";

//Assuming the HTTP Post does not require any form fields
using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
{
    using (StreamReader sr = new StreamReader(response.GetResponseStream()))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}

Checking the http referer in the web method will not also help as it can be easily set too. So what is the way to ensure that it is not called from any other place except the aspx pages. Truly speaking, there is no full proof way you can guarantee but you can add some complexity, so it gets a bit difficult comparing the above. Lets say in the aspx page which is used to call the web service we add the following code:

private void GenerateSecurityTicket()
{
    string cacheKey = User.Identity.Name + ":securityTicket";
    string securityTicket = Guid.NewGuid().ToString();

    Cache[cacheKey] = securityTicket;

    string script = string.Format("SECURITY_TICKET = '{0}';", securityTicket);

    ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "securityKey", script, true);
}

protected void Page_Load(object sender, EventArgs e)
{
    GenerateSecurityTicket();
}

What it does is, every time the page is rendered it creates a new Guid, puts it in the cache and embedded it as a JavaScript global variable. Next, in the web service we add the following code:

[WebMethod]
public string SecureMethod()
{
    EnsureTicket();

    return "This is a valid call.";
}

private void EnsureTicket()
{
    HttpContext context = HttpContext.Current;

    string headerTicket = context.Request.Headers["securityTicket"];

    if (string.IsNullOrEmpty(headerTicket))
    {
        throw new SecurityException("Security ticket must be present.");
    }

    string cacheKey = context.User.Identity.Name + ":securityTicket";
    string cacheTicket = (string)context.Cache[cacheKey];

    if (string.Compare(headerTicket, cacheTicket, false) != 0)
    {
        throw new SecurityException("Security ticket mismatched.");
    }
}

So what we are doing is checking if the http request has any key and if it matches with the key stored in the cache. If the key is not present or does not match with the stored key we are simply raising a security exception.

Next, when we are invoking the web method we have to make sure that the required header is added. But unfortunately there is no way to add the header directly in the WebServiceProxy class. Instead, we have to hook the WebRequestManager invokingRequest event to add the header. This is a special event which is fired for all kinds of ajax operation including the Update Panel partial update. The following shows the code:

function pageLoad(sender, args)
{
    Sys.Net.WebRequestManager.add_invokingRequest(onInvoke);
}

function pageUnload(sender, args)
{
    Sys.Net.WebRequestManager.remove_invokingRequest(onInvoke);
}

function onInvoke(sender, args)
{
    args.get_webRequest().get_headers()['securityTicket'] = SECURITY_TICKET;
}

function invokeSecureMethod()
{
    SecureService.SecureMethod(
                                    function(result)
                                    {
                                        alert(result);
                                    },
                                    function(e)
                                    {
                                        alert(e.get_message());
                                    }
                              );
}

Now anybody wants to call the web method needs to get the key from page, we can even add more complexity by encapsulating the key generation with a JavaScript function rather than stroing it plain variable. Again it is not proper way to ensure unauthorized usage but does any body has a better idea?

Download: Full Source

kick it on DotNetKicks.com

Filed under:

Comments

# re: Asp.net Ajax Web Service Security

Thursday, September 20, 2007 10:07 AM by Justin-Josef Angel [MVP]

Great idea! I love the way you used the WebRequestManager to manupiulate the request headers. Very nice idea.

# re: Asp.net Ajax Web Service Security

Thursday, September 20, 2007 9:16 PM by Glen Chen

I think the Ajax.Net framework should make this great idea as an enhanced feature in the future.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 11:15 AM by Raj Kaimal

In cases where you are not using forms authentication, you can always use the session object to store a value, like the guid and then check for it in your WS. You will need to EnableSession in the WS for this.

If you are using forms authentication, you can check to see if the user is authenticated (Request.IsAuthenticated) in the webservice. You can go a step further by using the WindowsPrincipal.IsInRole method to make sure only certain users can invoke WS - if needed.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 2:25 PM by Kazi Manzur Rashid

@Raj: Yes, I am discussing about the web service, which is not protected by the FormAuthentication/Membership, but I am not sure how do you protect it by storing some value in the session. Would you mind posting some code, which clarify my confusion?

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 3:41 PM by Raj

When the page loads, add

Session["foo"] = "some value";

In your WS, check if Session["foo"] exists. The only way Session["foo"] could have been created is if they visited the page.

If I may have misunderstood something, let me know.

Thanks,

Raj

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 4:15 PM by Kazi Manzur Rashid

@Raj: The problem with this approach is I can create a CookieContainer to request the page first and then i can use the same CookieContainer to call the Web Service. In this way You will not able to restrict the unauthorized usage of the web service. For example:

CookieContainer cookies = new CookieContainer();

HttpWebRequest request;

request = (HttpWebRequest)WebRequest.Create("Your Page Url");

request.CookieContainer = cookies;

request.Method = "GET";

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

{

}

request = (HttpWebRequest)WebRequest.Create("YOUR WS URL");

request.CookieContainer = cookies;

request.ContentType = "application/json; charset=utf-8";

request.Method = "POST";

//Assuming the HTTP Post does not require any form fields

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

{

   using (StreamReader sr = new StreamReader(response.GetResponseStream()))

   {

       Console.WriteLine(sr.ReadToEnd());

   }

}

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 4:51 PM by Raj Kaimal

Kazi,

I knew you were going to post that :-)

What is to prevent me from doing the same with your method (using the code you posted right above)? I can request the page first, grab the SECURITY_TICKET and construct a WS request by adding SECURITY_TICKET to the request header.

The only thing we can prevent is someone trying to invoke the WS directly *without* hitting the page.

Once someone hits the page, they will be able to make a call to the WS regardless of what security measure you put in place.

Correct me if I am wrong.

Raj

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 5:02 PM by Kazi Manzur Rashid

Yes, that is whole point and therefore I mentioned in the last section to put a cryptic JS function to generate the key instead of storing it in plain. I myself could not find any better solution for this.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 5:08 PM by Kazi Manzur Rashid

Basically the issue we are discussing is pretty much useless in a sense that it is also not possible to ensure that a regular page is posted from itself or from the same application and the same holds true for Ajax WS.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 5:32 PM by Raj

Correct. My point is for all the code you have written, a session check would be enough instead. I am not saying it is better in anyway -  but I think it is the same from a security standpoint.

Even if you were to put a JS cryptic function, one can always observer the request using a proxy and duplicate that request (Fiddler). BTW, Microsoft has a Script Encoder available (msdn2.microsoft.com/.../d14c8zsc.aspx) Again, as mentioned on the page, it does not prevent the determined hacker from seeing your code.

Enjoy reading your blog. Thanks.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 6:08 PM by Kazi Manzur Rashid

If you check my code carefully you will find tha I am generating the guid each time it is requested. And having replaced it with different algorithms which will be injected from server side will become a bit difficult than a plain session check. At least any one wants call the ws needs to write a parser which will translate the js code to generate the key.

Thanks that you enjoyed my blog.

# re: Asp.net Ajax Web Service Security

Friday, September 21, 2007 6:49 PM by Raj

"At least any one wants call the ws needs to write a parser"

..how difficult would it be to do that ? :-)

"Basically the issue we are discussing is pretty much useless in a sense that it is also not possible to ensure that a regular page is posted from itself or from the same application and the same holds true for Ajax WS."

Exactly. Agree with you fully on this.

# re: Asp.net Ajax Web Service Security

Monday, September 24, 2007 5:55 PM by Korayem

Here is another idea:

Before sending using the WS, let the aspx page hash the data it's going to send to the WS along with the generated GUID and send that hash too.

This will let the WS sure that the hash was generated by the servers own aspx page. The WS will simply generate a hash in the same combination as that in the aspx page and compare it with the one it received.

The whole point is that only the aspx page and the WS now how to combine different data and generate their hash.

The more complex the combination, the harder the hacker will learn how to use it.

To make things even harder, don't send the GUID to the WS, instead store it in a session variable.

# re: Asp.net Ajax Web Service Security

Tuesday, November 13, 2007 7:11 PM by Inbread CrackBaby

What if you just put the logic in a "PageMethod" instead of in an external .asmx web service?

Would that work?

This is what I mean by "PageMethod":

www.singingeels.com/.../Using_Page_Methods_in_ASPNET_AJAX.aspx

# re: Asp.net Ajax Web Service Security

Tuesday, November 13, 2007 8:49 PM by Kazi Manzur Rashid

Yes it will also work with PageMethods.

# re: Asp.net Ajax Web Service Security

Monday, December 31, 2007 10:25 AM by Himmet Yelekin

this method  is very good buy if you are using webservice as a ajax service with javascript the HttpContext.Current instance will not work, because its a new call and will be different from the browser

# re: Asp.net Ajax Web Service Security

Monday, January 28, 2008 2:32 PM by rrobbins

I'm trying to use this method of securing an AJAX web service but it is not working very well because the initial page request fails to add the security ticket to the web service request. It only works after a refresh for the second attempt. This is not acceptible.

# re: Asp.net Ajax Web Service Security

Wednesday, January 30, 2008 4:17 PM by rrobbins

I finally managed to solve my problem by getting rid of the pageLoad function and calling Sys.Net.WebRequestManager.add_invokingRequest within a regular JavaScript onload function. Maybe I had conflicting onload functions.

# re: Asp.net Ajax Web Service Security

Wednesday, January 30, 2008 4:30 PM by Kazi Manzur Rashid

Great!

# re: Asp.net Ajax Web Service Security

Wednesday, November 5, 2008 11:51 PM by Suresh

Hi Guys,

I need help guys. I need to implement webservice security part as u mentioned above example. I am calling web service using AJAX but our .net framework vertion 1.1. How i will use Sys.Net.WebRequestManager.add_invokingRequest JS calls. I think it is related to .net Framework vertion 3.5.

Can you please any one help me out fix this issue?

Thanking you.

Regards,

Suresh

# re: Asp.net Ajax Web Service Security

Wednesday, February 18, 2009 8:49 AM by ram

Can some one tell me How I can ensure that only the logged in people can access my AJAX functions.   Any one has the sample code.    

Can I make a check in my Webmethod for this?

Thanks,

Ram

ram_venka@yahoo.co.in