/// <summary> /// Evaluates the specified request for the need to switch its security. /// </summary> /// <param name="request">The request to evaluate.</param> /// <param name="settings">The settings to use for evaluation.</param> /// <return> /// A RequestSecurity value indicating the security the evaluated request should be under. /// </return> public RequestSecurity Evaluate(HttpRequestBase request, Settings settings) { // Test if the request matches the configured mode. if (!RequestMatchesMode(request, settings.Mode)) { Logger.Log("Request does not match mode and should be ignored."); return RequestSecurity.Ignore; } if (settings.IgnoreAjaxRequests && IsAjaxRequest(request)) { Logger.Log("Request is an AJAX request that should be ignored."); return RequestSecurity.Ignore; } // Find any matching path setting for the request. Logger.Log("Checking for a matching path for this request..."); string requestPath = request.RawUrl; if (!string.IsNullOrEmpty(requestPath)) { foreach (PathSetting pathSetting in settings.Paths) { // Get an appropriate path matcher and test the request's path for a match. IPathMatcher matcher = PathMatcherFactory.Create(pathSetting.MatchType); if (matcher.IsMatch(requestPath, pathSetting.Path, pathSetting.IgnoreCase)) { Logger.LogFormat("Matching path found; security is {0}.", pathSetting.Security); return pathSetting.Security; } } } // Any non-matching request should default to Insecure. Logger.Log("No matching path found; security defaults to Insecure."); return RequestSecurity.Insecure; }
public void Enrich(HttpResponseBase response, HttpRequestBase request, ISecurityEvaluator securityEvaluator, Settings settings) { if (!securityEvaluator.IsSecureConnection(request, settings) || !settings.EnableHsts) { return; } // Add the needed STS header. response.AddHeader("Strict-Transport-Security", string.Format("max-age={0:f0}", settings.HstsMaxAge)); }
/// <summary> /// Determines whether or not a request is an AJAX request. /// </summary> /// <param name="request">The request to test.</param> /// <returns> /// <c>true</c> if the specified request is an AJAX request; otherwise, <c>false</c>. /// </returns> private static bool IsAjaxRequest(HttpRequestBase request) { if (request == null) { throw new ArgumentNullException("request"); } // * Avoid accessing HttpRequestBase.Form at this point. There is a weird issue that forces the Framework to ignore the target of a // post-back if you access the Form collection during a request to the root of the application (i.e. http://mydomain.com/). // This issue does not appear if an actual page name is used in the URL (i.e. http://mydomain.com/Default.aspx). return (request.QueryString != null && request.QueryString[XRequestedWithHeaderKey] == AjaxRequestHeaderValue || request.Headers != null && request.Headers[XRequestedWithHeaderKey] == AjaxRequestHeaderValue); }
/// <summary> /// Gets any URI for the specified request that ensures it is being accessed by the proper protocol, if a match is found in the settings. /// </summary> /// <param name="request">The request to ensure proper access for.</param> /// <param name="response">The response to use if a redirection or other output is necessary.</param> /// <param name="security">The security setting to match.</param> /// <param name="settings">The settings used for any redirection.</param> /// <returns>A URL that ensures the requested resources matches the specified security; or null if the current request already does.</returns> public string GetUriForMatchedSecurityRequest(HttpRequestBase request, HttpResponseBase response, RequestSecurity security, Settings settings) { string targetUrl = null; // Evaluate the request's security. Logger.Log("Determining if the connection is secure."); bool isSecureConnection = _securityEvaluator.IsSecureConnection(request, settings); if (security == RequestSecurity.Secure && !isSecureConnection || security == RequestSecurity.Insecure && isSecureConnection) { Logger.Log("Calculating the target URI to switch to."); // Determine the target protocol and get any base target URL from the settings. string targetProtocolScheme; string baseTargetUrl; if (security == RequestSecurity.Secure) { targetProtocolScheme = Uri.UriSchemeHttps; baseTargetUrl = settings.BaseSecureUri; } else { targetProtocolScheme = Uri.UriSchemeHttp; baseTargetUrl = settings.BaseInsecureUri; } if (string.IsNullOrEmpty(baseTargetUrl)) { // If there is no base target URI, just switch the protocol scheme of the current request's URI. // * Account for cookie-less sessions by applying the application modifier. targetUrl = targetProtocolScheme + Uri.SchemeDelimiter + request.Url.Authority + response.ApplyAppPathModifier(request.RawUrl); } else { // Build the appropriate URI based on the specified target URL. var uri = new StringBuilder(baseTargetUrl); // - Use the full request path, but remove any sub-application path. uri.Append(request.RawUrl); if (request.ApplicationPath.Length > 1) { uri.Remove(baseTargetUrl.Length, request.ApplicationPath.Length); } // Normalize the URI. uri.Replace("//", "/", baseTargetUrl.Length - 1, uri.Length - baseTargetUrl.Length); targetUrl = uri.ToString(); } } return targetUrl; }
/// <summary> /// Determines whether the specified request is over a secure connection. /// </summary> /// <param name="request">The request to test.</param> /// <param name="settings">The settings used for determining a secure connection.</param> /// <returns> /// <c>true</c> if the specified request is over a secure connection; otherwise, <c>false</c>. /// </returns> public bool IsSecureConnection(HttpRequestBase request, Settings settings) { bool isPortMatch = (request.Url.Port == settings.SecurityPort); Logger.LogFormat("Checking if the request port matches the SecurityPort; {0}.", isPortMatch ? "it does" : "no match"); return isPortMatch; }
/// <summary> /// Determines whether the specified request is over a secure connection. /// </summary> /// <param name="request">The request to test.</param> /// <param name="settings">The settings used for determining a secure connection.</param> /// <returns> /// <c>true</c> if the specified request is over a secure connection; otherwise, <c>false</c>. /// </returns> public bool IsSecureConnection(HttpRequestBase request, Settings settings) { Logger.LogFormat("Connection {0} secure.", request.IsSecureConnection ? "is" : "is not"); return request.IsSecureConnection; }
/// <summary> /// Determines whether the specified request is over a secure connection. /// </summary> /// <param name="request">The request to test.</param> /// <param name="settings">The settings used for determining a secure connection.</param> /// <returns> /// <c>true</c> if the specified request is over a secure connection; otherwise, <c>false</c>. /// </returns> public abstract bool IsSecureConnection(HttpRequestBase request, Settings settings);
/// <summary> /// Tests the given request to see if it matches the specified mode. /// </summary> /// <param name="request">An HttpRequestBase to test.</param> /// <param name="mode">The Mode used for the test.</param> /// <returns> /// Returns true if the request matches the mode as follows: /// <list type="disc"> /// <item>If mode is On.</item> /// <item>If mode is set to RemoteOnly and the request is from a computer other than the server.</item> /// <item>If mode is set to LocalOnly and the request is from the server.</item> /// </list> /// </returns> private static bool RequestMatchesMode(HttpRequestBase request, Mode mode) { switch (mode) { case Mode.On: return true; case Mode.RemoteOnly: return !request.IsLocal; case Mode.LocalOnly: return request.IsLocal; default: return false; } }