Ways to secure an anonymous Web API request - c#

I have a ASP.NET MVC (NOT ASP.NET Core) single page application with angular js on the front end.
My client (browser) talks to server through ASP.NET Web APIs. Now, the web application is on https but anonymous. There is no login/ user authentication.
The web app is in the form of a wizard where user can go back and forth and add or update input fields on the web page. The form input values are then updated on the server through Web API.
I'm looking for a way to secure my Web API calls alone, especially the POST/ PUT requests. In summary, I want to prevent any user calling my Web API directly from POSTMAN or Fiddler. The Web API, though anonymous can be only called from the browser session where the request originated.
What are options do I have to achieve this?
Can I use Anti-Forgery token here (without authentication)?
One way, I can think of achieving this is to add a custom header to each request and store some kind of session key in the header. Then, validate the custom header on every request I received from client. Are any other ways of achieving this out-of-box or some proven library without going for custom solution?
If I have to go for the above custom solution, what are the pitfalls or potential issues I need to be aware of?

First of all when you remove login and there's no authentication mechanism in your application, there's really no way to secure anything, because anyone can access your APIs. I think what you want is to make sure that your APIs are called only from your own website. Unfortunately you can't completely achieve that, since your web APIs are http/https, and anyone, from anywhere (like postman, fiddler, ...) can create a http request and call your API.
All you can do is to make it harder for your API to response to requests, like using Anti-Forgery as you mentioned.
And also I suggest you add a cookie for your application and check that cookie in every request, in this case it's more complicated ( not impossible ) to call your API using Fiddler or Postman.
And last I suggest that you use CORS, so browsers would only allow your domain to call your APIs. So nobody can call your APIs in a browser from different domain.

Based on answer from #Arvin and comment from #Evk, here's how I plan to proceed:
Once, the user starts the anonymous session generate a GUID using regular Guid.NewGuid() method and save it in DB to identify the request (I'm doing this now). However, as mentioned here,
GUID can be unique but they are not cryptographically secured.
Hence, instead of using plain-text GUID, encrypt it with current timestamp as token and append it with request query string.
For every subsequent API request, read the token from query string, decrypt it and validate it as follows:
Check the timestamp. If the time difference is more than pre-defined time (i.e. token expired), reject the request
Validate the unique id (GUID) against DB
Since, I'm not using plain text GUID anymore, the URI would not easy to guess.
Additionally, with the timestamp, URI is invalidated after sometime. While theoretically it is still possible to call the API through Fiddler but this should make it very difficult for the attacker, if not impossible.
As a further security measure, I can also add Anti-Forgery token to the request
As per my understanding this helps solving my underlying problem and with this approach, I may not even need add a cookie to secure my anonymous session.
Love to hear from you all if this approach looks good and how can it be improved.

I also once had the weird need for having session functionality on WebAPI and created an OWIN Session middleware that does exactly what you're aiming for.
The library is called OwinSessionMiddleware and is available on github and NuGet.
Usage
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseSessionMiddleware();
// other middleware registrations...
app.UseWebApi();
}
}
You can also pass some options to further tweak cookie-name, add a database backed session store (instead of in-memory), add your own session id generator (instead of default id generator which is based on GUID + secure random part generated by RNGCryptoServiceProvider).
The unique session id itself is stored as a secure cookie and the session is restored automatically by the middleware on each request.
There are extension methods you can call inside your API controller to get and set session data:
public SomeApiController : ApiController
{
public IHttpActionResult MyAction()
{
var requestCount = Request.GetSessionProperty<int>("RequestCount");
Request.SetSessionProperty("RequestCount", ++requestCount);
}
}

Create Anonymous JWT token with some claims related to your scenario, Sign it with some key, Use that as in cookie (Http Only) or As bearer token. To make it little more harder further combine it with some cookies.
1)verify token signature and
2) Verify token expiry time
3) Verify Claim(skey) against cookies(skey)- no database storage required everything is in ur JWT token.

Related

How to force Identity Server 4 to route from /connect/authorize to my custom methods on each request?

When a user (SPA based, no MVC) tries to log in to my application (auth code flow), they go to the endpoint specified in the well known document.
https://localhost:5010/connect/authorize
?client_id=cool_client
&response_type=code
&redirect_uri=http://localhost:4200/auth&scope=openid&param=1337
It works well and the IDS4 routes over the call to the controller below with the correct value of returnUrl. I do some magic and logic in there and sign in them to the application. They are redirected to the SPA and provided with a code to be exchanged for a token. Everything works just as supposed to and a correct, working token is issued..
[HttpGet("login")]
public async Task<IActionResult> LogIn([FromQuery] string returnUrl)
{
...
await HttpContext.SignInAsync(user);
return Redirect(returnUrl);
}
When the user is signed in and attempts to authorize again, now with the access token (which they obtained using the code issued previously), they navigate to the very same endpoint with the very same query string. However, this time, the IDS4 handles the call differently. Instead of routing the call again to the method above, it automagically bounces the call to the URL from returnUrl without hitting the breaky in it.
I assumed it had to do with some optimization due to the presence of a valid access token (since the very same request after the token's expired routes me to my method again). However, I'd like to get the routing now because I need to do something with each incoming call, due to changed business requirement.
I googled the issue but found nothing (most of the material regards MVC nto SPA, anyway). I read the official docs and tried to scrutinize the quick-start application. After three days, I'm concluding that I can't find any useful info on the subject.
How can I force the routing to my custom method for each incoming request, including the ones that contain a valid access token?

Passing authorization token to Odata client

I've been trying to consume an authorized ASP.NET Odata Web API by using an Odata client. I do know how to set the header with the token for the client, but where should the token be kept for re-use?. Because the Authorization header has to be set with each request, not just once. To be more precise, this is what I am down to:
Get user's credentials, pass them back to the API.
Get token.
Set the header for the Odata client with the token value.
I am struggling with the actions to be taken between 2nd and 3rd step: Where should the token be kept, so you could keep setting the Odata client Header with it?
This is how I set the authorization header value.
EDIT: In regards to the comment, I might've not clearly specified the issue. The problem arises, because controllers will be created for every request and will be garbage collected "sometime after" the request has completed. So, the token value will be gone.
have a look here, they are storing it in a cookie. If you do not want to expose the actual token on the client side, store it somewhere near your e.g. user profile / session, or store the token with a generated id in a lookup-table and save the id to a cookie.
this is another approach using ASP.NET Identity and ApplicationCookies

How can I protect controller actions in an MVC app that doesn't contain a db or user data?

I'm looking to understand the nitty gritty mechanics of authorization so I can devise a strategy for my situation.
My situation is that I am part of a distributed application. My part is an MVC5 application that basically just consists of a couple of controllers that return single page app views. So hit controller A and get back single page app A. Hit controller B and get single page app B. Etc. This application contains no database or user data. Some other application on a completely different website/server does. I want to ask that other application if a user is valid or have users ask the other application directly themselves and only allow access to my app views if the answer is yes. So, in essence, I want to protect my controllers based on the word of a remote application that contains an exposed api for login/user validation.
It has been suggested to me that token authentication is the way to go. It's a bit daunting with my lack of experience with it, but I've buried myself in some reading and video presentations. Here is my current, weak attempt at summarizing the task based on limited understanding. Please correct as needed:
An access token needs to be generated
Getting an access token is not part of the Account controller, it's part of OWIN middleware
The access token will be sent along with the requests for my contoller actions
My controller actions, decorated with the [Authorize] attribute, will parse the token and do the right thing
Questions:
Q1: Should I generate the token or should the other app - the one with the db and user data?
Q2: My controllers don't know anything about users. That data is in the other app. What specifically are the controllers parsing for under the hood in order to do the right thing? In essence, what specifically tells them, "yes, this request is OK. Return the view."
Q3: I started my project awhile back using a standard MVC5 project template that comes with VS2015 because I figured I'd be handling users/login etc. That turned out not to be the case. Am I going to have to go back and redo this project from scratch because that template doesn't fit this requirement or can I do some surgery on it and continue? For instance, I don't know if that template has all the OWIN stuff I need or maybe has too much extra junk (bloated Account controller, Entity Framework, etc.) to be easily transformed/maintained.
Q4: Is token authorization overkill here? Is there an easier way to keep unauthorized users from accessing my controller actions that makes more sense given the nature of the project?
Any insight would be appreciated.
Update: What I meant in Q2 was, at it's simplest, how does [Authorize] work? Details? I'm guessing I have to tell it how to work. For instance, a silly example to illustrate. If I wanted to tell a controller decorated with [Authorize] to let anyone in who has the username "fred", how and where would I do that? I'm not so much looking for code. I'm thinking conceptually. My app must know something about the tokens the other app (authenticating app) is genenerating. In general terms, what would I add to my MVC app to tell it how to decode those tokens? Where do I add it? Is there one standard place?
I think you are on the right track and are right about the steps you have mentioned. I will answer your questions based on what I understand:
Q1. The other application is the one that needs to authorize and generate a token (whatever be the authorization mechanism they use) and you should receive this token before showing your views. Since the data is coming from the other application , they have to give your controllers access to their data. This is why you need to ask the other application for the token/authorization. With a valid token got from the other application your application can send valid and authorized requests to their data.
Q2. What you can do from your side is to add a check as to whether the request for your action/view is coming from an authorized user. For this, you need to check if this request has a valid token.
Q3. I don't know what you mean by "template" here. But if you need to integrate your controllers to the other solution, you do need to know what the other solution does and what it offers in terns of authorization and of course the data. They should provide your application access to a public api for this purpose.
q4. THis is something the other application needs to do. From what I understand, I think you are only adding a web API to an existing system so I think you need to really know how you can integrate with the other application. They should have clear APIs that are public for you to do this to access their features and data.
Since you have not mentioned if this other application is something like a secure enterprise solution or a Google API (has public API ) it would be difficult to tell exactly what you can expect from the other application.
I think you would need to try JSON web tokens (JWT )
I have not used it myself though . stormpath.com/blog/token-auth-spa –
It is useful for authenticating if a request to your controller. Here is a similar question as you have (I think) and how JWT could solve it How to use JWT in MVC application for authentication and authorization? and https://www.codeproject.com/Articles/876870/Implement-OAuth-JSON-Web-Tokens-Authentication-in
You can override the AuthorizeAttribute like this : https://msdn.microsoft.com/en-us/library/ee707357(v=vs.91).aspx . Your authorization logic of checking for whichever tokens/auth mechanism you decide to can be added to this new action filter. Then add that new attribute to your actions. So if your custom authorization attribute when overriding looks like this:
public class RestrictAccessToAssignedManagers : AuthorizationAttribute
Then your action would have the attribute decoration like this:
[RestrictAccessToAssignedManagers]
public ActionResult ShowallAssignees(int id)
Found a good article which could also be of help - https://blogs.msdn.microsoft.com/martinkearn/2015/03/25/securing-and-securely-calling-web-api-and-authorize/
My answer to your question will be based on:
I want to ask that other application if a user is valid or have users
ask the other application directly themselves and only allow access to
my app views if the answer is yes. So, in essence, I want to protect
my controllers based on the word of a remote application that contains
an exposed api for login/user validation.
Yes, to my humble opinion, oauth token-based approach is overkill for your need. Sometimes, keeping things simple (and stupid?) is best.
Since you are part of a distributed application, I suppose you can (literally) talk to the team in charge of the "other application/website" where requests that hit your controllers will be coming from.
So, I will skip your questions 1, 2, 3 and 4, which are oriented towards the token-based oauth approach, and suggest a rather different, "simplistic" and faster approach, which is the following:
For clarity purpose, let's call the other application "RemoteApp", and your application "MyApp", or "The other team" and "You", respectively.
-Step 1: You (MyApp) exchange a symmetric secret key with the other team (RemoteApp);
-Step 2: MyApp and RemoteApp agree on the algorithm that will be used to hash data that will be posted to MyApp when a user from RemoteApp requests a page on MyApp. Here, you can, for instance, use MD5 or SHA256 algorithms, which are well documented in MSDN and pretty easy to implement in C#;
Step 3: MyApp tells RemoteApp what its needs to be part of the posted data to validate/authenticate the request.
Here is an example:
Step 1: BSRabEhs54H12Czrq5Mn= (just a random secret key. You can choose/create your own);
Step 2: MD5 (this is the algorithm chosen by the 2 parties)
Step 3: The posted request data could include (at least 3 - 4 parameters or form fields, plus the checksum):
- UserName+RemoteApp fully-qualified domain name + someOther blabla data1 + someOther blabla data2 + checksum
The above information will be concatenated, without space. For instance, let's assume:
UserName = fanthom
RemoteApp fully-qualified domain name = www.remote.com
someOther blabla data1 = myControllerName
someOther blabla data2 = myActionName
The checksum will be generated as follows (function prototype):
generateMD5(string input, string secretKey)
which will be called with the following arguments:
string checkSum = generateMD5("fanthomwww.remote.commyControllerNamemyActionName", "BSRabEhs54H12Czrq5Mn=")
Notice that in the first argument the above 4 parameters have been concatenated, without space, and the second argument is the secret symmetric key.
the above will yield (actual md5 checksum):
checkSum = "ab84234a75430176cd6252d8e5d58137"
Then, RemoteApp will simply have to include the checkSum as an additional parameter or form field.
Finally, upon receiving a request, MyApp will simply have to do the same operation and verify the checkSum. That is, concatenate Request.Form["UserName"]+Request.Form["RemoteApp fully-qualified domain name"]+["someOther blabla data1"]+["someOther blabla data2"],
then use the md5 function with the secret key to verify if the obtained checksum matches the one sent in the request coming from RemoteApp.
If the two match, then the request is authentic. Otherwise, reject the request!
That'all Folks!
I seems you need to implement an OpenID/OAuth2 process.
This way, your apps will be able to utilise single-sign-on (SSO) for all your apps, and all you would have to do is set up your MVC5 app as an OpenID/OAuth2 client.
Take a look into Azure AD B2C which is perfectfor this (I am currently implementing this right now for 3 projects I am working on).
https://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
https://azure.microsoft.com/en-us/services/active-directory-b2c/
https://identityserver.io/
So your app is publicly addressable? I can't tell for sure from your description.
Well you only have these issues if a public client is requesting certain page views from you...
Now here's where i'm confused. If it's an external client accessing different parts of your distributed app, ALL the parts have this problem.
Generally the client authenticates itself at one place (say written by Team A), any other subsequent view requests would need to be checked as well (HTTP being connectionless/stateless), including others done by Team A? So your problem would already be solved (because it would be a problem for everyone, and they would have done something using an auth cookie + checking service, use the same checking service...)?
Which leads me to believe that the view requests are internal and not client facing, in which case... why is auth such a big deal?
If you could clarify your scenario around what requests you need to authenticate...
you are on right track. But instead of you implementing OAUTH and OpenIDConnect user third party which does the heavy lifting. One such tool is IdentityServer
https://identityserver.github.io/Documentation/docsv2/
Now answering your question from IdentityServer point of view
An access token needs to be generated -- true
Getting an access token is not part of the Account controller, it's part of OWIN middleware -- yes, for better design
The access token will be sent along with the requests for my contoller actions
My controller actions, decorated with the [Authorize] attribute, will parse the token and do the right thing -- Yes as a part of response header
Questions:
Q1: Should I generate the token or should the other app - the one with the db and user data? The identity server will generate token that you requested.
Q2: My controllers don't know anything about users. That data is in the other app. What specifically are the controllers parsing for under the hood in order to do the right thing? In essence, what specifically tells them, "yes, this request is OK. Return the view. - usually the token is sent back to the identtyServer to check for validity and to get access_token which will check if the user has access rights. if not [Authorize] attribute will throw error message and return
Q3: I started my project awhile back using a standard MVC5 project template that comes with VS2015 because I figured I'd be handling users/login etc. That turned out not to be the case. Am I going to have to go back and redo this project from scratch because that template doesn't fit this requirement or can I do some surgery on it and continue? For instance, I don't know if that template has all the OWIN stuff I need or maybe has too much extra junk (bloated Account controller, Entity Framework, etc.) to be easily transformed/maintained. - Yes u can delete the extra stuffs
Q4: Is token authorization overkill here? Is there an easier way to keep unauthorized users from accessing my controller actions that makes more sense given the nature of the project? -- It is not an over kill. It is the right thing to do for your scenario

Refresh identity token from client from code

I am using the fine-uploader with an asp.net mvc backend to upload files directly to an azure blob-storage. The asp.net-server works as the azure-signature-endpoint(sas). This is all secured with an identityserver3 and here comes the problem: The identity_tokens lifetime is set to 5 minutes(as default). And I want the SAS endpoint to be only called authorized. So a user can only access its own data. But after I have the upload running for the set 5 minutes, it breaks. Of course, because it is not authorized and it returns the login-page from the identity-server(which the fine-uploader, of course, can't handle).
It would be easy to solve this problem, by either setting the lifetime from the token to something higher(but this is unreliable as the upload could take hours or days), or disable the lifetime(yeah would work. But I guess the lifetime has a reason to exist).
So my 2 ideas would be:
to just authorize the user once(when he starts the download)
or to refresh the token by code every time the signature gets generated.
First approach
Can I just set a boolean like user is authorized and if this boolean is set just skip authorization and just return the signature or how could I achieve this
Second approach
How would I do this?
Don't use the id token for API access, an instead use access token. Access tokens are for resources (APIs), whilst id_tokens are purely for authentication. The id_token is only meant to be used to provide the RP with means of knowing who the user is - hence no need for long time span. It's only there for converting into a user session (cookie/principal).
With access tokens you have several approaches for keeping a valid token at a client; either by fetching new ones using the client credentials flow or by using the refresh token approach.
I believe there are samples of both in the samples repo of IdentityServer.
Samples repo link

How to make sure that all call to asp web api is authorized?

I'm building an saas application using asp web api 2 and asp identity. This api will be consumed by web, mobile and desktop applications. How do i make sure that all calls to my web api method is authorized? In web, we can ask user to login before going to certain page, but how bout mobile/desktop? Does user need to provide login and password on each call? Are there better solution for this? I've been searching and havent found any article about this. A sample or articles will be much appreciated.
Usually when using api's as a back-end you have to authenticate the user on every request.
(it actually also happens with other frameworks, such as mvc.net, but they keep track of the user using cookies which are send back and forth with every request)
I would suggest you use token based authentication (e.g. OAuth). In such a case you set the token in the header of the request. This token will be used to authenticate (and potentially authorize) the user.
If you need more info i can always explain it a bit more.
== Edit: Added Code sample ==
You could use a request handler to validate that the header of the request includes a valid token:
public class AuthorizationHeaderHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage pRequest, CancellationToken pCancellationToken)
    {
        IEnumerable<string> apiKeyHeaderValues = null; 
        if (!pRequest.Headers.TryGetValues("Authorization", out apiKeyHeaderValues)
|| !TokenRepo.IsVallidToken(apiKeyHeaderValues))
        {
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
            {
                Content = new StringContent("{\"error\": \"invalid_token\"}")
            };
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return Task.Factory.StartNew(() => response);
}
return base.SendAsync(pRequest, pCancellationToken);
}
}
All you have to do is keep which token is associated with which user, and make sure tokens have an expiration period. Once this is passed, it is also not valid anymore.
I hope this clarifies it a bit.
Again, if you have more questions, do not hesitate to ask.

Categories