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
Based on answer from @Arvin and comment from @Evk, here's how I plan to proceed:
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 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.
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.
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);
}
}