public Task <IVirtualFileAsync> GetFileAsync(string virtualPath, NameValueCollection queryString) { RemoteRequestEventArgs request = ParseRequest(virtualPath, queryString); if (request == null) { throw new FileNotFoundException(); } if (request.SignedRequest && c.get("remotereader.allowAllSignedRequests", false)) { request.DenyRequest = false; } //Check the whitelist if (request.DenyRequest && IsWhitelisted(request)) { request.DenyRequest = false; } //Fire event if (AllowRemoteRequest != null) { AllowRemoteRequest(this, request); } if (request.DenyRequest) { throw new ImageProcessingException(403, "The specified remote URL is not permitted."); } return(Task.FromResult <IVirtualFileAsync>(new RemoteSiteFile(virtualPath, request, this))); }
private bool IsWhitelisted(RemoteRequestEventArgs request) { var rr = c.getNode("remotereader"); var domain = new Uri(request.RemoteUrl).Host; if (rr == null || string.IsNullOrEmpty(domain)) { return(false); } foreach (Node n in rr.childrenByName("allow")) { bool onlyWhenSigned = n.Attrs.Get("onlyWhenSigned", false); if (onlyWhenSigned && !request.SignedRequest) { continue; } bool hostMatches = false, regexMatches = false; string host = n.Attrs.Get("domain"); if (!string.IsNullOrEmpty(host)) { if (host.StartsWith("*.", StringComparison.OrdinalIgnoreCase)) { hostMatches = domain.EndsWith(host.Substring(1), StringComparison.OrdinalIgnoreCase); } else { hostMatches = domain.Equals(host, StringComparison.OrdinalIgnoreCase); } if (!hostMatches) { continue; //If any filter doesn't match, skip rule } } string regex = n.Attrs.Get("regex"); if (!string.IsNullOrEmpty(regex)) { var r = new Regex("^" + regex + "$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); regexMatches = r.IsMatch(request.RemoteUrl); if (!regexMatches) { continue; //If any filter doesn't match, skip rule } } //If all specified filters match, allow the request. //This *is* supposed to be || not &&, because we already deal with non-matching filters via 'continue'. if (hostMatches || regexMatches) { return(true); } } return(false); }
/// <summary> /// Parses the specified path and querystring. Verifies the HMAC signature for querystring specified paths, parses the /// human-friendly syntax for that syntax. Verifies the URL is properly formed. Returns an object containing the remote URL, /// querystring remainder, and a flag stating whether the request was signed or not. Incorrectly signed requests immediately throw an exception. /// </summary> /// <param name="virtualPath"></param> /// <param name="query"></param> /// <returns></returns> public RemoteRequestEventArgs ParseRequest(string virtualPath, NameValueCollection query) { query = new NameValueCollection(query); if (!IsRemotePath(virtualPath)) { return(null); } RemoteRequestEventArgs args = new RemoteRequestEventArgs(); args.SignedRequest = false; args.QueryString = query; if (!string.IsNullOrEmpty(query[Base64UrlKey])) { string data = query[Base64UrlKey]; string hmac = query[HmacKey]; query.Remove(Base64UrlKey); query.Remove(HmacKey); if (String.IsNullOrEmpty(hmac)) { args.SignedRequest = false; args.DenyRequest = true; } else if (SignData(data).Equals(hmac)) { args.SignedRequest = true; } else { throw new ImageProcessingException("Invalid request! This request was not properly signed, or has been tampered with since transmission."); } args.RemoteUrl = PathUtils.FromBase64UToString(data); } else { args.RemoteUrl = "http://" + ReplaceInLeadingSegment(virtualPath.Substring(remotePrefix.Length).TrimStart('/', '\\'), "_", "."); args.RemoteUrl = Uri.EscapeUriString(args.RemoteUrl); } if (!SkipUriValidation && !Uri.IsWellFormedUriString(args.RemoteUrl, UriKind.Absolute)) { throw new ImageProcessingException("Invalid request! The specified Uri is invalid: " + args.RemoteUrl); } return(args); }
public RemoteSiteFile(string virtualPath, RemoteRequestEventArgs request, RemoteReaderPlugin parent) { this.virtualPath = virtualPath; this.request = request; this.parent = parent; }
private bool IsWhitelisted(RemoteRequestEventArgs request) { var rr = c.getNode("remotereader"); var domain = new Uri(request.RemoteUrl).Host; if (rr == null || string.IsNullOrEmpty(domain)) return false; foreach (Node n in rr.childrenByName("allow")) { bool onlyWhenSigned = NameValueCollectionExtensions.Get(n.Attrs, "onlyWhenSigned", false); if (onlyWhenSigned && !request.SignedRequest) continue; bool hostMatches = false, regexMatches = false; string host = n.Attrs.Get("domain"); if (!string.IsNullOrEmpty(host)) { if (host.StartsWith("*.", StringComparison.OrdinalIgnoreCase)) hostMatches = domain.EndsWith(host.Substring(1), StringComparison.OrdinalIgnoreCase); else hostMatches = domain.Equals(host, StringComparison.OrdinalIgnoreCase); if (!hostMatches) continue;//If any filter doesn't match, skip rule } string regex = n.Attrs.Get("regex"); if (!string.IsNullOrEmpty(regex)) { var r = new Regex("^" + regex + "$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); regexMatches = r.IsMatch(request.RemoteUrl); if (!regexMatches) continue;//If any filter doesn't match, skip rule } //If all specified filters match, allow the request. //This *is* supposed to be || not &&, because we already deal with non-matching filters via 'continue'. if (hostMatches || regexMatches) return true; } return false; }
/// <summary> /// Parses the specified path and querystring. Verifies the hmac signature for querystring specified paths, parses the /// human-friendly syntax for that syntax. Verifies the URL is properly formed. Returns an object containing the remote URL, /// querystring remainder, and a flag stating whether the request was signed or not. Incorrectly signed requests immediately throw an exception. /// </summary> /// <param name="virtualPath"></param> /// <param name="query"></param> /// <returns></returns> public RemoteRequestEventArgs ParseRequest(string virtualPath, NameValueCollection query) { query = new NameValueCollection(query); if (!IsRemotePath(virtualPath)) return null; RemoteRequestEventArgs args = new RemoteRequestEventArgs(); args.SignedRequest = false; args.QueryString = query; if (!string.IsNullOrEmpty(query[Base64UrlKey])) { string data = query[Base64UrlKey]; string hmac = query[HmacKey]; query.Remove(Base64UrlKey); query.Remove(HmacKey); if (String.IsNullOrEmpty(hmac)) { args.SignedRequest = false; args.DenyRequest = true; } else if (SignData(data).Equals(hmac)) { args.SignedRequest = true; } else { throw new ImageProcessingException("Invalid request! This request was not properly signed, or has been tampered with since transmission."); } args.RemoteUrl = PathUtils.FromBase64UToString(data); } else { args.RemoteUrl = "http://" + ReplaceInLeadingSegment(virtualPath.Substring(remotePrefix.Length).TrimStart('/', '\\'), "_", "."); args.RemoteUrl = Uri.EscapeUriString(args.RemoteUrl); } if (!Uri.IsWellFormedUriString(args.RemoteUrl, UriKind.Absolute)) throw new ImageProcessingException("Invalid request! The specified Uri is invalid: " + args.RemoteUrl); return args; }