/// <summary> /// Sends the message after adding the HMAC signature if a HMAC client id header is present. /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Only try and sign if we have a client id var clientId = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault(); if (string.IsNullOrEmpty(clientId)) { return(base.SendAsync(request, cancellationToken)); } // Can we sign it var secret = secretRepository.ClientSecret(clientId); if (secret == null) { Logger.WarnFormat("No secret for client id {0}: {1}", clientId, request.RequestUri); } else { // Need the date present in UTC for the signature calculation request.Headers.Date = DateTimeOffset.UtcNow; // Get the canonical representation. var representation = representationBuilder.BuildRequestRepresentation(request); // Compute the signature var signature = signatureCalculator.Signature(secret, representation, scheme); if (string.IsNullOrEmpty(signature)) { Logger.WarnFormat("Invalid signature for client id {0}: {1}", clientId, request.RequestUri); } else { // Valid signature so add the authentication header Logger.InfoFormat("HMAC signed for client id {0}: {1}", clientId, request.RequestUri); var header = new AuthenticationHeaderValue(HmacAuthentication.AuthenticationSchemePrefix + scheme, signature); request.Headers.Authorization = header; } } return(base.SendAsync(request, cancellationToken)); }
/// <summary> /// Validates whether we are using HMAC and if so is the signature valid. /// </summary> /// <param name="request"></param> /// <returns></returns> public async Task <bool> IsValid(HttpRequestMessage request) { // TODO: Generalize so we can using any HMAC scheme. if (request.Headers.Authorization == null || request.Headers.Authorization.Scheme != HmacAuthentication.AuthenticationScheme) { // No authorization or not our authorization schema, so no Logger.DebugFormat("Not our authorization schema: {0}", request.RequestUri); return(false); } var isDateValid = request.IsMessageDateValid(ClockDrift); if (!isDateValid) { // Date is not present or valid Logger.DebugFormat("Invalid date: {0}", request.RequestUri); return(false); } var userName = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault(); if (string.IsNullOrEmpty(userName)) { // No user name Logger.DebugFormat("No client id: {0}", request.RequestUri); return(false); } var secret = secretRepository.ClientSecret(userName); if (secret == null) { // Can't find a secret for the user, so no Logger.DebugFormat("No secret for client id {0}: {1}", userName, request.RequestUri); return(false); } if (!await request.Content.IsMd5Valid()) { // MD5 is invalid, so no Logger.DebugFormat("Invalid MD5 hash: {0}", request.RequestUri); return(false); } // Construct the representation var representation = representationBuilder.BuildRequestRepresentation(request); if (representation == null) { // Something broken in the representation, so no Logger.DebugFormat("Invalid canonical representation: {0}", request.RequestUri); return(false); } // Compute the signature // TODO: Pass the encryption algorithm used e.g. SHA256 var signature = signatureCalculator.Signature(secret, representation); // Have we seen it before if (objectCache.Contains(signature)) { // Already seen, so no to avoid replay attack Logger.WarnFormat("Request replayed {0}: {1}", signature, request.RequestUri); return(false); } // Validate the signature var result = request.Headers.Authorization.Parameter == signature; if (result) { // Store valid signatures to avoid replay attack objectCache.Set(signature, userName, DateTimeOffset.UtcNow.AddMinutes(ValidityPeriod), CacheRegion); } // Return the signature validation. return(result); }
/// <summary> /// Validates whether we are using HMAC and if so is the signature valid. /// </summary> /// <param name="request"></param> /// <returns></returns> /// <remarks> /// We log all failures a Info, except for replay attacks which are logged as Warn as they may be a symptom of an attack. /// </remarks> public async Task <bool> IsValid(HttpRequestMessage request) { if (request.Headers.Authorization == null) { // No authorization or not our authorization schema, so no Logger.InfoFormat("No authorization: {0}", request.RequestUri); return(false); } if (!request.Headers.Authorization.Scheme.StartsWith(HmacAuthentication.AuthenticationSchemePrefix)) { // No authorization or not our authorization schema, so no Logger.InfoFormat("Not our authorization schema: {0}", request.RequestUri); return(false); } var isDateValid = request.IsMessageDateValid(ClockDrift); if (!isDateValid) { // Date is not present or valid Logger.InfoFormat("Invalid date: {0}", request.RequestUri); return(false); } var userName = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault(); if (string.IsNullOrEmpty(userName)) { // No user name Logger.InfoFormat("No client id: {0}", request.RequestUri); return(false); } var secret = secretRepository.ClientSecret(userName); if (secret == null) { // Can't find a secret for the user, so no Logger.InfoFormat("No secret for client id {0}: {1}", userName, request.RequestUri); return(false); } var contentValid = await request.Content.IsMd5Valid().ConfigureAwait(false); if (!contentValid) { // MD5 is invalid, so no Logger.InfoFormat("Invalid MD5 hash: {0}", request.RequestUri); return(false); } // Construct the representation var representation = representationBuilder.BuildRequestRepresentation(request); if (representation == null) { // Something broken in the representation, so no Logger.InfoFormat("Invalid canonical representation: {0}", request.RequestUri); return(false); } // Compute the signature var scheme = request.Headers.Authorization.Scheme.Substring(HmacAuthentication.AuthenticationSchemePrefix.Length); var signature = signatureCalculator.Signature(secret, representation, scheme); // Have we seen it before if (cache.Contains(signature)) { // Already seen, so no to avoid replay attack Logger.WarnFormat("Request replayed {0}: {1}", signature, request.RequestUri); return(false); } // Validate the signature var result = request.Headers.Authorization.Parameter == signature; if (!result) { // Signatures differ, so no Logger.InfoFormat("Signatures differ {0}: {1}", signature, request.RequestUri); } else { // Store valid signatures to avoid replay attack cache.Add(signature, DateTimeOffset.UtcNow.AddMinutes(ValidityPeriod)); } // Return the signature validation. return(result); }