Beispiel #1
0
        public static bool IsAuthorized(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            // Quick request age check
            var authHeader = headers.Value<string>("Authorization");
            DateTimeOffset requestVersion = headers.RequestVersion;
            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.
            // The ambiguity has been resolved in version 2015-02-21
            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)
                {
                    if (requestVersion >= StorageServiceVersions.Version_2015_02_21)
                    {
                        // This version made it explicit what to do with 0 content-length
                        contentLength = String.Empty;
                        runBlankContentLengthComparison = false;
                    }
                    else
                    {
                        // Preserve a Content-Length: 0 header for PUT methods
                        runBlankContentLengthComparison = !method.Equals(WebRequestMethods.Http.Put, StringComparison.OrdinalIgnoreCase);
                    }
                }
            }
            var stringsToCheck = pathsToCheck.SelectMany(uriPath => new[] {
                    runBlankContentLengthComparison ? Tuple.Create(uriPath, String.Empty) : null,
                    Tuple.Create(uriPath, contentLength),
                })
                .Where(check => check != null)
                .Select(pathWithContentLength => GetStringToSign(!fullKeyAlgorithm, method, pathWithContentLength.Item1, headers, queryParams, dateHeader, pathWithContentLength.Item2))
                .SelectMany(stringToSign => new[] { Tuple.Create(true, stringToSign), Tuple.Create(false, stringToSign) });
            var evaluationResult = stringsToCheck
                .FirstOrDefault(validatationCheck => VerifyRequestAuthorization(signature, validatationCheck.Item1, validatationCheck.Item2));
            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;
        }
Beispiel #2
0
        public static async Task<bool> IsAuthorizedAsync(IHttpRequestWrapper request, RequestHeaders headers, RequestQueryParameters queryParams, bool ignoreRequestAge)
        {
            var requestUriParts = request.UriParts;
            string resourceType = String.Empty;
            DateTimeOffset? start = queryParams.Value(ParamStartTime, DateTimeOffset.UtcNow);
            DateTimeOffset? expiry = queryParams.Value(ParamExpiryTime, DateTimeOffset.MinValue);
            DateTimeOffset sasVersion = queryParams.Value(ParamVersion, StorageServiceVersions.Version_2009_09_19);
            bool accountSas = queryParams.Contains(ParamResourceTypeEx);
            if (accountSas)
            {
                resourceType = queryParams.Value<string>(ParamResourceTypeEx).ToLowerInvariant();
            }
            else
            {
                resourceType = queryParams.Value<string>(ParamResourceType).ToLowerInvariant();
            }
            if (expiry == DateTimeOffset.MinValue)
            {
                expiry = null;
            }
            SharedAccessBlobPermissions permissions = PermissionsFromString(queryParams.Value(ParamPermissions, String.Empty)); 
            // Determine validity of the structure first
            if (requestUriParts.IsAccountRequest)
            {
                if (sasVersion < StorageServiceVersions.Version_2015_04_05 || !resourceType.Contains('s'))
                {
                    // SAS keys are not valid for account operations before 2015-04-05
                    return false;
                }
            }
            else if (requestUriParts.IsContainerRequest)
            {
                if (resourceType != "c")
                {
                    return false;
                }
            }
            else if (requestUriParts.IsBlobRequest)
            {
                if (resourceType.IndexOfAny(new[] { 'c', 'b', 'o' }) == -1)
                {
                    return false;
                }
            }
            if (!accountSas)
            {
                var storedPolicyId = queryParams.Value<string>(ParamStoredPolicy);
                if (!String.IsNullOrWhiteSpace(storedPolicyId))
                {
                    // Validate that we're not duplicating values for both stored access policy & url
                    // Allow stored policy to the specified from a different container for test purposes - this isn't a security violation as it must come from the same account.
                    var aclContainer = headers.Value("StoredPolicyContainer", String.Empty);
                    if (String.IsNullOrEmpty(aclContainer))
                    {
                        aclContainer = requestUriParts.Container;
                    }
                    var storedPolicy = await GetStoredPolicyForContainer(aclContainer, storedPolicyId);
                    if (storedPolicy == null)
                    {
                        return false;
                    }
                    if (storedPolicy.SharedAccessStartTime.HasValue)
                    {
                        start = storedPolicy.SharedAccessStartTime;
                    }
                    if (storedPolicy.SharedAccessExpiryTime.HasValue)
                    {
                        if (expiry.HasValue)
                        {
                            return false;
                        }
                        expiry = storedPolicy.SharedAccessExpiryTime;
                    }
                    if (queryParams.Contains(ParamPermissions))
                    {
                        return false;
                    }
                    permissions = storedPolicy.Permissions;
                }
            }
            else
            {
                if (!queryParams.Value<string>(ParamServices, String.Empty).Contains('b'))
                {
                    // SAS must include blob service
                    return false;
                }
            }
            if (!expiry.HasValue || permissions == SharedAccessBlobPermissions.None)
            {
                return false;
            }
            else if (!ignoreRequestAge && (start.Value > DateTimeOffset.UtcNow || expiry.Value < DateTimeOffset.UtcNow))
            {
                return false;
            }
            else if (!IsProtocolMatched(request, queryParams.Value<string>(ParamProtocol)))
            {
                return false;
            }
            else if (!IsSourceAddressInRange(request, queryParams.Value<string>(ParamSourceIP)))
            {
                return false;
            }

            // Verify the assigned permissions line up with the requested operation
            StorageOperationTypes requestOperation = StorageOperations.GetBlobOperation(request);
            switch (requestOperation)
            {
                case StorageOperationTypes.GetBlob:
                case StorageOperationTypes.GetBlobMetadata:
                case StorageOperationTypes.GetBlobProperties:
                case StorageOperationTypes.GetBlockList:
                case StorageOperationTypes.GetPageRanges:
                case StorageOperationTypes.GetBlobServiceProperties:
                case StorageOperationTypes.GetBlobServiceStats:
                case StorageOperationTypes.GetContainerProperties:
                case StorageOperationTypes.GetContainerMetadata:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Read))
                    {
                        return false;
                    }
                    break;

                case StorageOperationTypes.AbortCopyBlob:
                case StorageOperationTypes.CopyBlob:
                case StorageOperationTypes.LeaseBlob:
                case StorageOperationTypes.PutBlob:
                case StorageOperationTypes.PutBlock:
                case StorageOperationTypes.PutBlockList:
                case StorageOperationTypes.PutPage:
                case StorageOperationTypes.SetBlobMetadata:
                case StorageOperationTypes.SetBlobProperties:
                case StorageOperationTypes.SnapshotBlob:
                case StorageOperationTypes.SetBlobServiceProperties:
                case StorageOperationTypes.CreateContainer:
                case StorageOperationTypes.SetContainerMetadata:
                case StorageOperationTypes.LeaseContainer:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Write))
                    {
                        return false;
                    }
                    break;

                case StorageOperationTypes.DeleteBlob:
                case StorageOperationTypes.DeleteContainer:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.Delete))
                    {
                        return false;
                    }
                    break;

                case StorageOperationTypes.ListBlobs:
                case StorageOperationTypes.ListContainers:
                    if (!permissions.IsFlagSet(SharedAccessBlobPermissions.List))
                    {
                        return false;
                    }
                    break;

                default:
                    // All other operations are not supported by SAS uris
                    return false;
            }
            Func<string> stringToSignFactory = null;
            if (!accountSas)
            {
                Func<string> baseStringToSign = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}",
                                                                    queryParams.Value<string>(ParamPermissions),
                                                                    queryParams.Value<string>(ParamStartTime),
                                                                    queryParams.Value<string>(ParamExpiryTime),
                                                                    GetCanonicalizedResource(requestUriParts, resourceType, sasVersion),
                                                                    queryParams.Value<string>(ParamStoredPolicy));
                Func<string> ipAndProtocolSnippet = () => String.Format("{0}\n{1}\n",
                                                                    queryParams.Value<string>(ParamSourceIP),
                                                                    queryParams.Value<string>(ParamProtocol));
                Func<string> v2012_02_12StringToSign = () => String.Format("{0}\n{1}{2}",
                                                                            baseStringToSign(),
                                                                            sasVersion >= StorageServiceVersions.Version_2015_04_05 ? ipAndProtocolSnippet() : String.Empty,
                                                                            sasVersion.ToVersionString());

                if (sasVersion < StorageServiceVersions.Version_2012_02_12)
                {
                    stringToSignFactory = baseStringToSign;
                }
                else if (sasVersion == StorageServiceVersions.Version_2012_02_12)
                {
                    stringToSignFactory = v2012_02_12StringToSign;
                }
                else
                {
                    stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}",
                                                                v2012_02_12StringToSign(),
                                                                queryParams.Value<string>(ParamCacheControl),
                                                                queryParams.Value<string>(ParamContentDisposition),
                                                                queryParams.Value<string>(ParamContentEncoding),
                                                                queryParams.Value<string>(ParamContentLang),
                                                                queryParams.Value<string>(ParamContentType));
                }
            }
            else
            {
                stringToSignFactory = () => String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n",
                                                                SharedKeySignature.AccountName,
                                                                queryParams.Value<string>(ParamPermissions),
                                                                queryParams.Value<string>(ParamServices),
                                                                queryParams.Value<string>(ParamResourceTypeEx),
                                                                queryParams.Value<string>(ParamStartTime),
                                                                queryParams.Value<string>(ParamExpiryTime),
                                                                queryParams.Value<string>(ParamSourceIP),
                                                                queryParams.Value<string>(ParamProtocol),
                                                                sasVersion.ToVersionString());
            }
            string signature = queryParams.Value<string>(ParamSignature);
            var usingPrimaryKey = new[] { true, false };
            string stringToSign = stringToSignFactory();
            int matchIndex = Array.FindIndex(usingPrimaryKey, usePrimaryKey => VerifySignature(signature, usePrimaryKey, stringToSign));
            if (matchIndex != -1)
            {
                // We can't sign the redirection response when the request uses a SAS key - preserve the matching key, however
                request.AuthenticationScheme = String.Empty;
                request.AuthenticationKey = usingPrimaryKey[matchIndex] ? SharedKeySignature.PrimaryAccountKey : SharedKeySignature.SecondaryAccountKey;
                return true;
            }
            return false;
        }
Beispiel #3
0
 static string GetStringToSign(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
     if (liteAlgorithm)
     {
         return 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
     {
         return 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));
     }
 }