So, using this website as a basis, I decided to follow the tutorial that was given at a high level, and then, reading the comments, it seems that the given procedure is 95% 2-legged OAuth.
I made a simple project which demonstrates this: I'll post it here soon...
Here's the code; it's in two groups. The first is the client and the second is the server.
public ActionResult Index()
{
// Initialize a new instance of System.Security.Cryptography.HMACSHA512 passing in the key that was typed in by the user
HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes("passwordOfUserGoesHere"));
// Using the newly initialized hmac class, HASH the data that will be sent;
// when authenticating, the data can be the API key and the Nonce, whereas when authentication already happened, this can be the
// entire URL along with the NOnce and the ApiKey hashed by the user's password.
// It's entirely up to you.
// note: Be consistent with the Encoding that you will use.
byte[] hashed = hmac.ComputeHash(Encoding.UTF8.GetBytes("data"));
// Encode the hash so that it can be sent over the wire
var stringHash = Encoding.UTF8.GetString(hashed);
// Initialize a new AuthenticationHeaderValue class. This is where we will pass our data.
// The first parameter is the name of the scheme; it is entirely up to you to name it.
// The second parameter is the data that has been transformed.
AuthenticationHeaderValue value = new AuthenticationHeaderValue("Glass", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", "userName", stringHash))));
// Initialize an HttpClient class
HttpClient client = new HttpClient();
// Set the authorization value of the HttpClient to the AuthenticationHeaderValue class.
client.DefaultRequestHeaders.Authorization = value;
// With the authorization value set in the HttpClient, we can now call our API with the credentials hashed...
var result = client.GetAsync(ConfigurationManager.AppSettings["WebApiProducts"].ToString()).Result;
return View();
}
now the server code:
public class GlassAuthenticateAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
/// <summary>
/// Authenticates a user based on the Glass authentication scheme
/// </summary>
/// <param name="actionContext">The context that was passed in by the calling client</param>
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// If the authorization parameter is null, that means the user is not authenticated. Throw an exception along with an NOnce
if (actionContext.Request.Headers.Authorization == null)
{
// If the scheme is unknown, throw an exception again
if (actionContext.Request.Headers.Authorization.Scheme != "Glass")
{
throw new HttpResponseException(actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized));
}
// This exception will be thrown.
throw new HttpResponseException(actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized));
}
else
{
// format: Glass username:H(apiKey:nOnce[:parameters:action])
// If the scheme is valid, retrieve the user's records from the database by searching for the username
// It's ok for the username to be public, so the client should not hash it.
// The records to be retrieved are:
// 1) hashed password of user
// 2) NOnce within valid time period (In this case, 10 mins)
// Recalculate the HMAC by using the user's hashed password along with the data
HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes("passwordOfUserGoesHere"));
byte[] hashed = hmac.ComputeHash(Encoding.UTF8.GetBytes("data"));
var stringHash = Encoding.UTF8.GetString(hashed);
// Here's the decoded data
string decodedData = actionContext.Request.Headers.Authorization.Parameter;
RNGCryptoServiceProvider nOnceProvider = new RNGCryptoServiceProvider();
byte[] nonce = new byte[1024];
nOnceProvider.GetBytes(nonce);
// TODO: Save nonce in database with a expiry time. 10 Mins is safe enough.
// This NOnce is not assigned to any user, but rather, WILL BE ASSIGNED once the user grabs it.
// Note: When saving to the database, be sure to use UTC time.
// In .NET, use System.DateTime.UtcNow instead of using System.DateTime.Now
}
}
}
I will explain in detail right after this post.
http://www.codingfreak.net
Marked as answer by preconsult on Oct 04, 2012 05:55 PM
There are three "people" here: the user, the client and the server. The client is the application that the user (the actual person) interacts with and the server is the one that hosts the API.
Ok, now that that's out of the way,
Here's the flow:
Server gives an API key to client and server stored the key in a database; this is used to identify how the API is being interacted with' very useful if you allow developers to make clients that would interact with your Api. Take note that we will assume
that the user is already registered.
User uses Client to use the API; API returns unauthorized because client is not authenticated.
Client gets the unauthorized error; client then redirects the user to a login page.
User supplies credentials to client. Client does not send the password. Instead, client hashes the password using the correct hashing algorithm - the same one that the server uses for hashing the algorithm to be stored in the database - and then hashes
the API key. Something like this will be sent over the wire:
<username>:H||(<apiKey>) where H||(<apiKey>) = the apiKey hashed using the user's password.
Client then sends the data over the wire.
Server received the request.
Server gets the username (note that the username is in PLAINTEXT; there is no need to hash it because it is made public anyway) and brings up the records. The pieces of data we are interested is the hashed password of the user and the ApiKey, which are both stored
in the database.
Using the user's hashed password, server recalculates the hash of the ApiKey. If you have more than one ApiKey, you will have to retrieve them and calculate their hashes as well. If one matches, then that means that the user is authorized.
Server allows the process to continue; the final response of the server should contain an NOnce ("Number once") - a cryptographically strong random number which should only occur once that will be used for the next request of the client. This should have
a validity period - 10 minutes is enough, but it's really up to you.
Client receives NOnce. Go to number 4 again but this time, hash the NOnce along with the data, and the complete URL request. This is to ensure that this does not happen:
Attacker sees relative url set to /product/create
Attacker modifies relative url and sets it to /procut/delete.
That's it. If anything is unclear, just let me know and I'll try to answer them. Have fun.
http://www.codingfreak.net
Marked as answer by preconsult on Oct 04, 2012 05:55 PM
Thanks for the detailed explaination. That really helped thanks. I have one doubt regarding the last step you mentioned :
Client receives NOnce. Go to number 4 again but this time, hash the NOnce along with the data, and the complete URL request. This is to ensure that this does not happen:
Attacker sees relative url set to /product/create
Attacker modifies relative url and sets it to /product/delete.
In the code example of the client you have hashed the header and the not the URI.
client.GetAsync(ConfigurationManager.AppSettings["WebApiProducts"].ToString()).Result (you use the normal URI)
When you mentioned to use Nonce to hash the data along with the request, i did not grasp the use of it. Is it not possible for the attacker to still use our hashed header and change the uri to /product/delete?
preconsult
Member
2 Points
3 Posts
ASP.NET Web API oAuth
Aug 25, 2012 08:45 PM|LINK
Hello
How can i secure my ASP.NET Web Api with 2-leggend oAuth?
I find a lot of info on oAuth, but i am still confused.
Can someone point me to a good library or a tutorial?
regards
Dieter
pacoalphonso
Member
138 Points
63 Posts
Re: ASP.NET Web API oAuth
Aug 26, 2012 02:43 AM|LINK
Unfortunately, 2-legged OAuth isn't as popular as 3-legged OAuth - where all the hype is. I was in your same boat a few weeks ago, until I found this website here: http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/
So, using this website as a basis, I decided to follow the tutorial that was given at a high level, and then, reading the comments, it seems that the given procedure is 95% 2-legged OAuth.
I made a simple project which demonstrates this: I'll post it here soon...
preconsult
Member
2 Points
3 Posts
Re: ASP.NET Web API oAuth
Aug 26, 2012 06:38 AM|LINK
that would be great !!!
thx
regards
Dieter
BrockAllen
All-Star
27554 Points
4912 Posts
MVP
Re: ASP.NET Web API oAuth
Aug 26, 2012 03:06 PM|LINK
FWIW, SSL does many of these things for you and is much easier to deploy.
DevelopMentor | http://www.develop.com
thinktecture | http://www.thinktecture.com/
pacoalphonso
Member
138 Points
63 Posts
Re: ASP.NET Web API oAuth
Aug 27, 2012 09:50 AM|LINK
Here's the code; it's in two groups. The first is the client and the second is the server.
public ActionResult Index() { // Initialize a new instance of System.Security.Cryptography.HMACSHA512 passing in the key that was typed in by the user HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes("passwordOfUserGoesHere")); // Using the newly initialized hmac class, HASH the data that will be sent; // when authenticating, the data can be the API key and the Nonce, whereas when authentication already happened, this can be the // entire URL along with the NOnce and the ApiKey hashed by the user's password. // It's entirely up to you. // note: Be consistent with the Encoding that you will use. byte[] hashed = hmac.ComputeHash(Encoding.UTF8.GetBytes("data")); // Encode the hash so that it can be sent over the wire var stringHash = Encoding.UTF8.GetString(hashed); // Initialize a new AuthenticationHeaderValue class. This is where we will pass our data. // The first parameter is the name of the scheme; it is entirely up to you to name it. // The second parameter is the data that has been transformed. AuthenticationHeaderValue value = new AuthenticationHeaderValue("Glass", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", "userName", stringHash)))); // Initialize an HttpClient class HttpClient client = new HttpClient(); // Set the authorization value of the HttpClient to the AuthenticationHeaderValue class. client.DefaultRequestHeaders.Authorization = value; // With the authorization value set in the HttpClient, we can now call our API with the credentials hashed... var result = client.GetAsync(ConfigurationManager.AppSettings["WebApiProducts"].ToString()).Result; return View(); }now the server code:
public class GlassAuthenticateAttribute : System.Web.Http.Filters.ActionFilterAttribute { /// <summary> /// Authenticates a user based on the Glass authentication scheme /// </summary> /// <param name="actionContext">The context that was passed in by the calling client</param> public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { // If the authorization parameter is null, that means the user is not authenticated. Throw an exception along with an NOnce if (actionContext.Request.Headers.Authorization == null) { // If the scheme is unknown, throw an exception again if (actionContext.Request.Headers.Authorization.Scheme != "Glass") { throw new HttpResponseException(actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized)); } // This exception will be thrown. throw new HttpResponseException(actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized)); } else { // format: Glass username:H(apiKey:nOnce[:parameters:action]) // If the scheme is valid, retrieve the user's records from the database by searching for the username // It's ok for the username to be public, so the client should not hash it. // The records to be retrieved are: // 1) hashed password of user // 2) NOnce within valid time period (In this case, 10 mins) // Recalculate the HMAC by using the user's hashed password along with the data HMACSHA512 hmac = new HMACSHA512(Encoding.UTF8.GetBytes("passwordOfUserGoesHere")); byte[] hashed = hmac.ComputeHash(Encoding.UTF8.GetBytes("data")); var stringHash = Encoding.UTF8.GetString(hashed); // Here's the decoded data string decodedData = actionContext.Request.Headers.Authorization.Parameter; RNGCryptoServiceProvider nOnceProvider = new RNGCryptoServiceProvider(); byte[] nonce = new byte[1024]; nOnceProvider.GetBytes(nonce); // TODO: Save nonce in database with a expiry time. 10 Mins is safe enough. // This NOnce is not assigned to any user, but rather, WILL BE ASSIGNED once the user grabs it. // Note: When saving to the database, be sure to use UTC time. // In .NET, use System.DateTime.UtcNow instead of using System.DateTime.Now } } }I will explain in detail right after this post.
pacoalphonso
Member
138 Points
63 Posts
Re: ASP.NET Web API oAuth
Aug 27, 2012 10:27 AM|LINK
Ok, first let's get some stuff out of the way:
There are three "people" here: the user, the client and the server. The client is the application that the user (the actual person) interacts with and the server is the one that hosts the API.
Ok, now that that's out of the way,
Here's the flow:
<username>:H||(<apiKey>) where H||(<apiKey>) = the apiKey hashed using the user's password.
Attacker sees relative url set to /product/create
Attacker modifies relative url and sets it to /procut/delete.
That's it. If anything is unclear, just let me know and I'll try to answer them. Have fun.
preconsult
Member
2 Points
3 Posts
Re: ASP.NET Web API oAuth
Oct 04, 2012 06:05 PM|LINK
can i also use this to let 2 REST service communicate with each other?
there is nog user/password, just 2 REST Services
beadarsh
Member
2 Points
1 Post
Re: ASP.NET Web API oAuth
Jan 30, 2013 09:57 PM|LINK
Thanks for the detailed explaination. That really helped thanks. I have one doubt regarding the last step you mentioned :
Client receives NOnce. Go to number 4 again but this time, hash the NOnce along with the data, and the complete URL request. This is to ensure that this does not happen:
Attacker sees relative url set to /product/create
Attacker modifies relative url and sets it to /product/delete.
In the code example of the client you have hashed the header and the not the URI.
client.GetAsync(ConfigurationManager.AppSettings["WebApiProducts"].ToString()).Result (you use the normal URI)
When you mentioned to use Nonce to hash the data along with the request, i did not grasp the use of it. Is it not possible for the attacker to still use our hashed header and change the uri to /product/delete?
Many thanks in advance