pcibraro / hawknet Goto Github PK
View Code? Open in Web Editor NEWHawk protocol implementation for .NET
License: MIT License
Hawk protocol implementation for .NET
License: MIT License
According to the documentation the ext-parameter - if present - shall be placed before the mac parameter.
The hawk parameters shall be ordered in this sequence:
According to this sourcefile the order differs:
I found this issue when working with an hawk-protected api which didn't accept incorrect ordered parameters. It would be stunning if you could fix this issue.
re: CalculateMac() method
please look at the normalized string. i believe this is a more consistent formatting of it
//old lines in question
//((!string.IsNullOrEmpty(ext)) ? ext : "") + "\n";
// compared to java - escaped newline
//((!string.IsNullOrEmpty(payloadHash)) ? payloadHash + "\n" : "") +
//compared to java version - add \n always after
//proposed string for normalized var
var normalized = "hawk.1." + type + "\n" +
ts + "\n" +
nonce + "\n" +
method.ToUpper() + "\n" +
uri.PathAndQuery + "\n" +
host.ToLower() + "\n" +
uri.Port.ToString() + "\n" +
((!string.IsNullOrEmpty(payloadHash)) ? payloadHash : "") + "\n" +
((!string.IsNullOrEmpty(ext)) ? ext.Replace("\\", "\\\\").Replace("\n", "\\n") : "") + "\n";
Been trying to download and build the solution locally, noticed HttpRequestMessageExtensionsTests.cs appears to be missing from HawkNet.WebApi.Tests.csproj.
I believe there is an issue with the RemoveBewitFromQuery method.
Using the example URL below which contains encoded characters in the query string:
http://www.example.org/test?path=%2Fmovie%2Fcomedy%2F2014
The Hawk.GetBewit method will compute the MAC using the encoded path and query string above. No issues there.
However the Hawk.AuthenticateBewit method when calculating the MAC removes the bewit. In doing so it recreates the query string which removes the encoding so the MAC ends up using an un-encoded path and query string i.e.
test?path=/movie/comedy/2014
This results in a different MAC and therefore a "Bad mac" SecurityException is thrown.
The issue relates to the code below:
Hawk.cs > RemoveBewitFromQuery()
var resultingQuery = string.Join("&", parsedQueryString.Cast().Select(e => e + "=" + parsedQueryString[e]).ToArray());
Changing this to:
var resultingQuery = parsedQueryString.ToString();
Resolves the issue.
Hawk.cs:
the code in CalculateMac method, when i remove ToLower() ,and work well
var normalized = "hawk.1." + type + "\n" +
ts + "\n" +
nonce + "\n" +
method.ToUpper() + "\n" +
uri.PathAndQuery.ToLower() + "\n" +
sanitizedHost.ToLower() + "\n" +
uri.Port.ToString() + "\n" +
((!string.IsNullOrEmpty(payloadHash)) ? payloadHash : "") + "\n" +
((!string.IsNullOrEmpty(ext)) ? ext : "") + "\n";
Does the HawkNet.WebApi.HawkAuthentication function in asp.net core 2.0? I modify the hawknet/Example and hawknet/HawkNet.WebApi for my project. However, the HAWK request Authorization working, but in HAWK response Authorization, the server authorization header is missing. Is this framework issue? This is the example in controller, the HawkCredentialRepository is follow the https://github.com/pcibraro/hawknet/blob/master/Example/HawkCredentialRepository.cs
You enforce an authorization header with credentials for every request.
The controller could have [AllowAnonymous] though.
Hi,
I just downloaded and tested the project, since I need something similar, and I found that the AllowAnonymous attribute it is not working at all.
In your test that seems to work because you are already using and authorized client.
If you modify your test code (lines 64 to 69 in Example.cs) to the following, you will see the issue:
var client2 = new HttpClient();
var request2 = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8091/Api/HelloWorldAnonymous");
request2.Headers.Host = "localhost";
var response2 = client2.SendAsync(request2).Result;
var message2 = response2.Content.ReadAsStringAsync().Result;
Console.WriteLine("Response {0} - Http Status Code {1}", message2, response2.StatusCode);
Hey,
it would be better to use ClaimsPrincipal instead of HawkPrincipal. Turn the outcome of the authentication into some claims and add them to claims collection.
That would play much nicer with the rest of .net/wif.
cheers
dominick
I know you already noticed this yourself. When you use owin hosting the authentication fails because the port is not set correctly you can see this when the normalized string is printed in the trace.
Is there any update or workaround? maybe force a configurable value?
I am having an issue where a post WebApi method is not returning a response to the consuming client.
The client is another controller in the same solution/website (for testing purposes)
When I remove the Authorize attribute from the Post method everything works as expected and I recieve the response from the post method.
Client Method calling the post method
Dim postUri As String = "1/client/DataXChangeTester/type/CollisionReport"
Dim leftChild As New QueryNode With {.NodeValue = "drivers.name.lastName"}
Dim rightChild As New QueryNode With {.NodeValue = "McNear"}
Dim simpleOperation As New QueryNode With {.NodeValue = "is",
.LeftNode = leftChild,
.RightNode = rightChild}
Dim queryToSend As QueryDetails = New QueryDetails With {.QueryDetail = simpleOperation}
TesterClient = New HttpClient(HawkClientHandler)
'TesterClient = New HttpClient()
Dim webAddress As String = ConfigurationManager.AppSettings("ServiceURL")
TesterClient.BaseAddress = New Uri(webAddress)
TesterClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
Dim action = TesterClient.PostAsync(postUri, queryToSend, New JsonMediaTypeFormatter())
'30 second timeout.
action.Wait(30000)
Dim response As HttpResponseMessage = action.Result
If response.IsSuccessStatusCode Then
ViewData("PredefinedQueryMessage") = response.Content.ReadAsStringAsync().Result
Return View("Index")
Else
ViewData("PreDefinedQueryErrorMessage") = response.Content.ReadAsStringAsync().Result
Return View("Index")
End If
Client credential and message handler setup
TestCredential = New HawkCredential With {
.Id = "BuyCrashTN",
.Algorithm = "sha256",
.Key = "1531d118-ae19-11e3-b324-28cfe9215d9f"}
HawkClientHandler = New HawkClientMessageHandler(New HttpClientHandler(), TestCredential)
Server side code
WEBApiConfig
Dim handler As New HawkMessageHandler(New HttpControllerDispatcher(config),
Function(credential)
Return Task.FromResult(RetrieveCredential(credential))
End Function)
Post Method
'POST 'api/1/client/ClientName/type/TypeName
<Authorize>
<AcceptVerbs("POST")>
Public Function Query(requestMessage As HttpRequestMessage,
clientId As String,
typeName As String,
<FromBody> requestedQuery As QueryDetails) As HttpResponseMessage
Dim responseContents As New QueryResults()
Dim response As HttpResponseMessage
Dim requestId As Integer = -1
Try
requestId = LogRequest("Received Query", requestedQuery)
ReportType = GetType(DataXChangeCollisionQuery)
Parameter = Expression.Parameter(ReportType, "report")
responseContents = ExecuteQuery(requestedQuery)
response = requestMessage.CreateResponse(HttpStatusCode.OK, responseContents)
Catch ex As Exception
response = requestMessage.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error Processing Provided Query")
End Try
LogRequest("Exiting Query Operation", requestedQuery, requestId)
Return response
End Function
var clientHandler = new HawkClientMessageHandler(new HttpClientHandler(), credential, "some-app-data");
var client = new HttpClient(clientHandler);
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8091/api/HelloWorld");
request.Headers.Host = "localhost";
var response = client.SendAsync(request).Result;
string message = response.Content.ReadAsStringAsync().Result;
Console.WriteLine("Response {0} - Http Status Code {1}", message, response.StatusCode);
Additional:
I change url to http://localhost:28290/api/HelloWorld and run Example.Web project,is work well.
Hi Pablo.
When using urls that have spaces encoded as %20 instead of a plus sign (both are valid, apparently), the code is failing. This is because of a behavior in the object return from HttpUtility.ParseQueryString(uri.Query)
in the method RemoveBewitFromQuery
.
This object, when ToString()
is invoked, replaces %20 with the "+" sign. As a consequence, the resulting url doesn't match the original used to generate the mac, and the check fails.
A failing test:
[TestMethod]
public void ShouldAuthenticateBewitWithEncodedUrlWithPercentSpaces()
{
var credential = new HawkCredential
{
Id = "1",
Algorithm = "sha1",
Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn",
};
var bewit = Hawk.GetBewit("example.com", new Uri("http://example.com:8080/resource/4?path=%2Fmovie%2Fcomedy%20club%2F2014"), credential,
200, "hello");
var claims = Hawk.AuthenticateBewit(bewit, "example.com", new Uri("http://example.com:8080/resource/4?path=%2Fmovie%2Fcomedy%20club%2F2014&bewit=" + bewit),
s => credential);
Assert.IsNotNull(claims);
}
The ShouldFailWithTimestampInThePast
test fails if a timestamp 100 seconds behind is used. The tests are using 1 day which contains more than 1000 seconds, and that's way they succeed.
The ConvertToUnixTimestamp
method returns the number of seconds since the epoc. However, the GetBewit
method (and probably others) then use var expiration = Math.Floor(now / 1000) + ttlSec;
. The value of now
is already in seconds.
Since the timestamp in the bewit is in seconds, I'd suggest always using seconds as the time unit. This would also remove all the /1000
and *1000
code.
Please check the Timestamp validation in the Checkstamp check
in the GetAuthorizationHeader why divide by 1000, since the value is supposed to be seconds?
var normalizedTs = (ConvertToUnixTimestamp((ts.HasValue)
? ts.Value : DateTime.UtcNow)).ToString(); //removed / 1000
this causes issues in the CheckTimeStamp
private static bool CheckTimestamp(string ts, int timestampSkewSec)
{
double parsedTs;
if (double.TryParse(ts, out parsedTs))
{
var now = ConvertToUnixTimestamp(DateTime.Now);
// Check timestamp staleness
if (Math.Abs(parsedTs - now) > timestampSkewSec) //* 1000
{
return false;
}
else
{
Hey,
Started using HawkNet on a new project where I can't use HTTPS, and it does the trick. However, I would like to use the Web API 2 "Attribute routing" feature in my project, but unfortunately it seems that this style of routing doesn't support the custom message handlers that are needed to implement HawkNet (ie the the handler that returns the HawkCredential). Is there any other way ? Or should I just stick with the classic routing system ?
Thanks
When running Example.Owin against a non-secured controller (no Authorize attribute) and no signed http client, the exception The format of value '< null >' is invalid is thrown. This is happening in the file hawknet.owin\hawkauthenticationhandler.cs, line 156 due to an unsafe attempt to parse Request.Headers["authorization"]:
https://github.com/pcibraro/hawknet/blob/master/HawkNet.Owin/HawkAuthenticationHandler.cs#L156
var authorization = AuthenticationHeaderValue.Parse(Request.Headers["authorization"]);
I fixed it by modifying line 154 to:
https://github.com/pcibraro/hawknet/blob/master/HawkNet.Owin/HawkAuthenticationHandler.cs#L154
if (this.Options.IncludeServerAuthorization && Request.Headers.ContainsKey("authorization"))
Not sure if this breaks your security logic, if it doesn't do you think this fix could be included in the Nuget package?
Thanks
Camilo
compared to java implementation there are places where ascii encoding is being used for encoding message and key. The java implementation has 100% utf encoding for key and message text
var messageBytes = Encoding.UTF8.GetBytes(normalized);
vs
var messageBytes = Encoding.ASCII.GetBytes(normalized);
There is no formatting applied to the payload before hashing and thus can be missed by an implementer
string.Format("hawk.1.payload\n{0}\n{1}\n", headerContentType.ToLower(), payload);
if (!mac.Equals(attributes["mac"]))
{
throw new SecurityException("Bad mac");
}
This is a well known implementation mistake. You should use a constant time comparison.
We currently use hawknet on asp.net 4.8 and am switching to .Net Core 6 and Blazor and as part of this the httprequest has been depreciated so we have switched to httpclient instead but I am struggling to get the code to work and the issue seems to be down to the hawk signing.
I don't suppose you have any sample code for httpclient do you as it would be a great help and worth having the updated example.
Thank you
Simon
(and make it public)
Would allow re-use of your implementation in other contexts.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.