public static void AddStandardResponseHeaders(this HttpResponseMessage response, RequestHeaders requestHeaders) { //Right now we are just parroting back the values sent by the client. Might need to generate our //own values for these if none are provided. string clientId = requestHeaders.ClientRequestId; if (!String.IsNullOrWhiteSpace(clientId)) { response.Headers.Add("x-ms-request-id", clientId); } response.Headers.Add("x-ms-version", requestHeaders.Value("x-ms-version", StorageServiceVersions.Version_2014_02_14.ToVersionString())); response.Headers.Date = DateTimeOffset.UtcNow; }
public static bool IsAuthorized(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge) { // Quick request age check var authHeader = headers.Value <string>("Authorization"); string requestDateHeader = headers.Value <string>("x-ms-date"); string dateHeader = String.Empty; if (String.IsNullOrWhiteSpace(requestDateHeader)) { requestDateHeader = headers.Value <string>("Date"); dateHeader = requestDateHeader; } if (String.IsNullOrWhiteSpace(requestDateHeader)) { // One of the date headers is mandatory return(false); } if (!ignoreRequestAge) { DateTime requestDate; if (!DateTime.TryParse(requestDateHeader, out requestDate)) { return(false); } else if (requestDate < DateTime.Now.AddMinutes(-15)) { return(false); } } var parts = authHeader.Split(' ', ':'); if (parts.Length != 3) { return(false); } else if (parts[0] != SharedKeySignature.AlgorithmSharedKey && parts[0] != SharedKeySignature.AlgorithmSharedKeyLite) { return(false); } var account = parts[1]; var signature = parts[2]; if (!String.Equals(account, SharedKeySignature.AccountName, StringComparison.OrdinalIgnoreCase)) { return(false); } // We have to deal with multiple encodings (the spec is a bit ambiguous on what an 'encoded' path actually is). // Only run the validation if the encodings result in different strings var requestUriParts = request.UriParts; var pathsToCheck = new List <string>() { requestUriParts.OriginalUriPath }; var unencodedPath = requestUriParts.PublicUriPath; if (unencodedPath != pathsToCheck[0]) { pathsToCheck.Add(unencodedPath); } var alternateEncodingPaths = AlternateEncodeString(pathsToCheck[0]); if (alternateEncodingPaths != null) { pathsToCheck.AddRange(alternateEncodingPaths); } // For some verbs we can't tell if the Content-Length header was specified as 0 or that IIS/UrlRewrite/ASP.NET has constructed // the header value for us. The difference is significant to the signature as content length is included for SharedKey bool fullKeyAlgorithm = parts[0] == SharedKeySignature.AlgorithmSharedKey; bool runBlankContentLengthComparison = false; string method = request.HttpMethod.ToUpper(); var contentLength = headers.Value("Content-Length", String.Empty); if (fullKeyAlgorithm) { int length; if (!int.TryParse(contentLength, out length) || length <= 0) { // Preserve a Content-Length: 0 header for PUT methods runBlankContentLengthComparison = !method.Equals(WebRequestMethods.Http.Put, StringComparison.OrdinalIgnoreCase); } } var validationChecks = pathsToCheck.SelectMany(uriPath => new[] { runBlankContentLengthComparison?Tuple.Create(true, uriPath, String.Empty) : null, Tuple.Create(true, uriPath, contentLength), runBlankContentLengthComparison ? Tuple.Create(false, uriPath, String.Empty) : null, Tuple.Create(false, uriPath, contentLength), }) .Where(check => check != null) .ToArray(); var evaluationResult = validationChecks .FirstOrDefault(validatationCheck => VerifyRequestAuthorization(signature, validatationCheck.Item1, !fullKeyAlgorithm, method, validatationCheck.Item2, headers, queryParams, dateHeader, validatationCheck.Item3)); if (evaluationResult != null) { // Remember the Auth Scheme & Key for when we have to sign the response request.AuthenticationScheme = parts[0]; request.AuthenticationKey = evaluationResult.Item1 ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey; return(true); } DashTrace.TraceWarning("Failed to authenticate SharedKey request: {0}:{1}:{2}:{3}:{4}", parts[0], account, method, request.Url, signature); return(false); }
public static string GenerateSignature(bool usePrimaryKey, bool liteAlgorithm, string method, string uriPath, RequestHeaders headers, RequestQueryParameters queryParams, string requestDate, string contentLength) { // Signature scheme is described at: http://msdn.microsoft.com/en-us/library/azure/dd179428.aspx // and the SDK implementation is at: https://github.com/Azure/azure-storage-net/tree/master/Lib/ClassLibraryCommon/Auth/Protocol Func <string> stringToSignFactory = null; if (liteAlgorithm) { stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}", method, headers.Value("Content-MD5", String.Empty), headers.Value("Content-Type", String.Empty), requestDate, SharedKeySignature.GetCanonicalizedHeaders(headers), GetCanonicalizedResource(liteAlgorithm, uriPath, queryParams, SharedKeySignature.AccountName)); } else { stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n{9}\n{10}\n{11}\n{12}\n{13}", method, headers.Value("Content-Encoding", String.Empty), headers.Value("Content-Language", String.Empty), contentLength, headers.Value("Content-MD5", String.Empty), headers.Value("Content-Type", String.Empty), requestDate, headers.Value("If-Modified-Since", String.Empty), headers.Value("If-Match", String.Empty), headers.Value("If-None-Match", String.Empty), headers.Value("If-Unmodified-Since", String.Empty), headers.Value("Range", String.Empty), SharedKeySignature.GetCanonicalizedHeaders(headers), GetCanonicalizedResource(liteAlgorithm, uriPath, queryParams, SharedKeySignature.AccountName)); } return(SharedKeySignature.GenerateSignature(stringToSignFactory, usePrimaryKey)); }