Exemplo n.º 1
0
        /// <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 GetUriDoesNotIncludeApplicationPathWithSuppliedBaseUri()
        {
            const string BaseRequestUri = "http://www.testsite.com";
            const string ApplicationPathRequestUri = "/MySuperDuperApplication";
            const string PathRequestUri = ApplicationPathRequestUri + "/Manage/Default.aspx";
            const string QueryRequestUri = "?Param=SomeValue";

            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.ApplicationPath).Returns(ApplicationPathRequestUri);
            mockRequest.SetupGet(req => req.Url).Returns(new Uri(BaseRequestUri + PathRequestUri + QueryRequestUri));
            mockRequest.SetupGet(req => req.RawUrl).Returns(PathRequestUri + QueryRequestUri);

            var mockResponse = new Mock<HttpResponseBase>();
            mockResponse.Setup(resp => resp.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(s => s);

            var settings = new Settings {
                Mode = Mode.On,
                BaseSecureUri = "https://secure.someotherwebsite.com/testsite/"
            };
            var evaluator = new HeadersSecurityEvaluator();
            var enforcer = new SecurityEnforcer(evaluator);

            // Act.
            var targetUrl = enforcer.GetUriForMatchedSecurityRequest(mockRequest.Object,
                                                                     mockResponse.Object,
                                                                     RequestSecurity.Secure,
                                                                     settings);

            // Assert.
            Assert.Equal(settings.BaseSecureUri + PathRequestUri.Remove(0, ApplicationPathRequestUri.Length + 1) + QueryRequestUri, targetUrl);
        }
        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">
        /// An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to 
        /// all application objects within an ASP.NET application.
        /// </param>
        public void Init(HttpApplication context)
        {
            if (context == null) {
                Logger.Log("No HttpApplication supplied.", Logger.LogLevel.Warn);
                return;
            }

            Logger.Log("Begin module initialization.");

            // Get the settings for the securitySwitch section.
            Logger.Log("Getting securitySwitch configuration section.", Logger.LogLevel.Info);
            _settings = WebConfigurationManager.GetSection("securitySwitch") as Settings;
            if (_settings == null || _settings.Mode == Mode.Off) {
                Logger.LogFormat("{0}; module not activated.", Logger.LogLevel.Info, _settings == null ? "No settings provided" : "Mode is Off");
                return;
            }

            Logger.Log("Creating RequestProcessor.");
            _requestProcessor = new RequestProcessor(_settings);

            // Hook the application's AcquireRequestState event.
            // * This ensures that the session ID is available for cookie-less session processing.
            // * I would rather hook sooner into the pipeline, but...
            // * It just is not possible (that I know of) to get the original URL requested when cookie-less sessions are used.
            //   The Framework uses RewritePath when the HttpContext is created to strip the Session ID from the request's
            //   Path/Url. The rewritten URL is actually stored in an internal field of HttpRequest; short of reflection,
            //   it's not obtainable.
            // WARNING: Do not access the Form collection of the HttpRequest object to avoid weird issues with post-backs from the application root.
            Logger.Log("Adding handler for the application's 'AcquireRequestState' event.");
            context.AcquireRequestState += ProcessRequest;

            Logger.Log("End module initialization.");
        }
		/// <summary>
		/// Parses the XML configuration section and returns the settings.
		/// </summary>
		/// <param name="parent">
		///		The configuration settings in a corresponding parent 
		///		configuration section.
		/// </param>
		/// <param name="configContext">
		///		An HttpConfigurationContext when Create is called from the ASP.NET 
		///		configuration system. Otherwise, this parameter is reserved and is 
		///		a null reference (Nothing in Visual Basic).
		/// </param>
		/// <param name="section">
		///		The XmlNode that contains the configuration information from the 
		///		configuration file. Provides direct access to the XML contents of 
		///		the configuration section.
		/// </param>
		/// <returns>
		///		Returns a Settings instance initialized with the 
		///		read configuration settings.
		///	</returns>
		public object Create(object parent, object configContext, XmlNode section) {
			// Create a Settings object for the settings in this section.
			Settings Settings = new Settings();

			// Read the general settings.
			ReadGeneralSettings(section, Settings);

			// Traverse the child nodes
			foreach (XmlNode Node in section.ChildNodes) {
				if (Node.NodeType == System.Xml.XmlNodeType.Comment)
					// Skip comment nodes (thanks to dcbrower on CodeProject for pointing this out).
					continue;
				else if (Node.Name.ToLower() == "directory")
					// This is a directory path node.
					Settings.Directories.Add(ReadDirectoryItem(Node));
				else if (Node.Name.ToLower() == "file")
					// This is a file path node.
					Settings.Files.Add(ReadFileItem(Node));
				else
					// Throw an exception for this unrecognized node.
					throw new SecuritySwitchSectionException(string.Format("'{0}' is not an acceptable setting.", Node.Name), Node);
			}

			// Return the settings.
			return Settings;
		}
		/// <summary>
		/// Determines the unsecure page that should be requested if a redirect occurs.
		/// </summary>
		/// <param name="settings">The Settings to use in determining.</param>
		/// <param name="ignoreCurrentProtocol">
		/// A flag indicating whether or not to ingore the current protocol when determining.
		/// </param>
		/// <returns>A string containing the absolute URL of the unsecure page to redirect to.</returns>
		/// <exception cref="ArgumentNullException"></exception>
		public static string DetermineUnsecurePage(Settings settings, bool ignoreCurrentProtocol) {
			if (settings == null)
				throw new ArgumentNullException("settings");
			
			string Result = null;
			HttpRequest Request = HttpContext.Current.Request;

			// Is this request secure?
			string RequestPath = Request.Url.AbsoluteUri;
			if (ignoreCurrentProtocol || RequestPath.StartsWith(SecureProtocolPrefix)) {
				// Is there a different URI to redirect to?
				if (string.IsNullOrEmpty(settings.UnencryptedUri))
					// Replace the protocol of the requested URL with "http".
					// * Account for cookieless sessions by applying the application modifier.
					Result = string.Concat(
						UnsecureProtocolPrefix,
						Request.Url.Authority,
						HttpContext.Current.Response.ApplyAppPathModifier(Request.Path),
						Request.Url.Query
					);
				else
					// Build the URL with the "http" protocol.
					Result = BuildUrl(false, settings.MaintainPath, settings.EncryptedUri, settings.UnencryptedUri);
			}

			return Result;
		}
        public void GetUriRequestReturnsNullIfOffloadedHeaderSecurityAlreadyMatchesSpecifiedSecurity()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.IsSecureConnection).Returns(false);

            var mockResponse = new Mock<HttpResponseBase>();
            var settings = new Settings();
            var evaluator = new HeadersSecurityEvaluator();
            var enforcer = new SecurityEnforcer(evaluator);

            // Act.
            mockRequest.SetupGet(req => req.Headers).Returns(new NameValueCollection {
                { "SSL_REQUEST", "on" },
                { "OTHER_HEADER", "some-value" }
            });
            settings.OffloadedSecurityHeaders = "SSL_REQUEST=";
            var targetUrlForAlreadySecuredRequest = enforcer.GetUriForMatchedSecurityRequest(mockRequest.Object,
                                                                                             mockResponse.Object,
                                                                                             RequestSecurity.Secure,
                                                                                             settings);

            mockRequest.SetupGet(req => req.Headers).Returns(new NameValueCollection {
                { "OTHER_HEADER", "some-value" }
            });
            var targetUrlForAlreadyInsecureRequest = enforcer.GetUriForMatchedSecurityRequest(mockRequest.Object,
                                                                                              mockResponse.Object,
                                                                                              RequestSecurity.Insecure,
                                                                                              settings);

            // Assert.
            Assert.Null(targetUrlForAlreadySecuredRequest);
            Assert.Null(targetUrlForAlreadyInsecureRequest);
        }
        public void ProcessRequest(HttpContextBase context)
        {
            var settings = new Settings();
            var requestProcessor = new RequestProcessor(settings);

            requestProcessor.Process(context, EvaluatorCallback);
        }
		/// <summary>
		/// Evaluates a given request against specified settings for the type of security action required
		/// to fulfill the request properly.
		/// </summary>
		/// <param name="request">The request to evaluate.</param>
		/// <param name="settings">The settings to evaluate against.</param>
		/// <param name="forceEvaluation">
		/// A flag indicating whether or not to force evaluation, despite the mode set.
		/// </param>
		/// <returns>A SecurityType value for the appropriate action.</returns>
		public static SecurityType Evaluate(HttpRequest request, Settings settings, bool forceEvaluation) {
			// Initialize the result to Ignore.
			SecurityType Result = SecurityType.Ignore;

			// Determine if this request should be ignored based on the settings' Mode.
			bool MustEvaluateRequest = (forceEvaluation || RequestMatchesMode(request, settings.Mode));
			DebugHelper.Output(MustEvaluateRequest ? "Evaluating request..." : "Evaluation of request skipped.");
			if (MustEvaluateRequest) {
				// Make sure the request shouldn't be ignored as a HTTP handler.
				if (settings.IgnoreHandlers == IgnoreHandlers.BuiltIn && !IsBuiltInHandlerRequest(request) ||
					settings.IgnoreHandlers == IgnoreHandlers.WithStandardExtensions && !IsStandardHandlerRequest(request) ||
					settings.IgnoreHandlers == IgnoreHandlers.None) {
					// Get the relative file path of the current request from the application root.
					string RelativeFilePath = HttpUtility.UrlDecode(request.Url.AbsolutePath).Remove(0, request.ApplicationPath.Length).ToLower(CultureInfo.CurrentCulture);
					if (RelativeFilePath.StartsWith("/"))
						// Remove any leading "/".
						RelativeFilePath = RelativeFilePath.Substring(1);

					// Get the relative directory of the current request by removing the last segment of the RelativeFilePath.
					string RelativeDirectory = string.Empty;
					int i = RelativeFilePath.LastIndexOf('/');
					if (i >= 0)
						RelativeDirectory = RelativeFilePath.Substring(0, i);

					// Determine if there is a matching file path for the current request.
					i = settings.Files.IndexOf(RelativeFilePath);
					if (i >= 0) {
						Result = settings.Files[i].Secure;
						DebugHelper.Output("Request matches file: {0} - {1}.", settings.Files[i].Secure, settings.Files[i].Path);
					} else {
						// Try to find a matching directory path.
						int j = -1;
						i = 0;
						while (i < settings.Directories.Count) {
							// Try to match the beginning of the directory if recursion is allowed (partial match).
							if ((settings.Directories[i].Recurse && RelativeDirectory.StartsWith(settings.Directories[i].Path, StringComparison.CurrentCultureIgnoreCase) ||
								RelativeDirectory.Equals(settings.Directories[i].Path, StringComparison.CurrentCultureIgnoreCase)) &&
								(j == -1 || settings.Directories[i].Path.Length > settings.Directories[j].Path.Length))
								// First or longer partial match found (deepest recursion is the best match).
								j = i;

							i++;
						}

						if (j > -1) {
							// Indicate a match for a partially matched directory allowing recursion.
							Result = settings.Directories[j].Secure;
							DebugHelper.Output("Request matches directory: {0} - {1}.", settings.Directories[j].Secure, settings.Directories[j].Path);
						} else {
							// No match indicates an insecure result.
							Result = SecurityType.Insecure;
							DebugHelper.Output("Request does not match anything.");
						}
					}
				}
			}

			return Result;
		}
        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 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 override bool IsSecureConnection(HttpRequestBase request, Settings settings)
        {
            Logger.Log("Checking for any header that matches one from OffloadedSecurityServerVariables...");

            // Parse the expected security variables and check for each against the server variables.
            NameValueCollection expectedSecurityVariables = HttpUtility.ParseQueryString(settings.OffloadedSecurityServerVariables);
            bool isSecure = FindAnyNameValueMatch(expectedSecurityVariables, request.ServerVariables);

            Logger.Log(isSecure
                       	? "Server variable match found; connection is secure."
                       	: "No match found; connection is presumed not secure.");

            return isSecure;
        }
        public void IsSecureConnectionReturnsTrueIfRequestIndicatesSecurity()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.IsSecureConnection).Returns(true);

            var settings = new Settings();
            var evaluator = new StandardSecurityEvaluator();

            // Act.
            var result = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.True(result);
        }
		/// <summary>
		/// Evaluates a given request against specified settings for the type of security action required
		/// to fulfill the request properly.
		/// </summary>
		/// <param name="request">The request to evaluate.</param>
		/// <param name="settings">The settings to evaluate against.</param>
		/// <param name="forceEvaluation">
		/// A flag indicating whether or not to force evaluation, despite the mode set.
		/// </param>
		/// <returns>A SecurityType value for the appropriate action.</returns>
		public static SecurityType Evaluate(HttpRequest request, Settings settings, bool forceEvaluation) {
			// Initialize the result to Ignore.
			SecurityType Result = SecurityType.Ignore;

			// Determine if this request should be ignored based on the settings' Mode.
			if (forceEvaluation || RequestMatchesMode(request, settings.Mode)) {
				// Get the relative file path of the current request from the application root.
				string RelativeFilePath = HttpUtility.UrlDecode(request.Url.AbsolutePath).Remove(0, request.ApplicationPath.Length).ToLower();
				if (RelativeFilePath.StartsWith("/"))
					// Remove any leading "/".
					RelativeFilePath = RelativeFilePath.Substring(1);

				// Get the relative directory of the current request by removing the last segment of the RelativeFilePath.
				string RelativeDirectory = string.Empty;
				int i = RelativeFilePath.LastIndexOf('/');
				if (i >= 0)
					RelativeDirectory = RelativeFilePath.Substring(0, i).ToLower();

				// Determine if there is a matching file path for the current request.
				i = settings.Files.IndexOf(RelativeFilePath);
				if (i >= 0)
					Result = settings.Files[i].Secure;
				else {
					// Try to find a matching directory path.
					int j = -1;
					i = 0;
					while (i < settings.Directories.Count) {
						// Try to match the beginning of the directory if recursion is allowed (partial match).
						if ((settings.Directories[i].Recurse && RelativeDirectory.StartsWith(settings.Directories[i].Path.ToLower()) ||
							RelativeDirectory.Equals(settings.Directories[i].Path.ToLower())) &&
								(j == -1 || settings.Directories[i].Path.Length > settings.Directories[j].Path.Length))
							// First or longer partial match found (deepest recursion is the best match).
							j = i;

						i++;
					}

					if (j > -1)
						// Indicate a match for a partially matched directory allowing recursion.
						Result = settings.Directories[j].Secure;
					else
						// No match indicates an insecure result.
						Result = SecurityType.Insecure;
				}
			}

			return Result;
		}
Exemplo n.º 13
0
        /// <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;
        }
        public void EnrichDoesntAddHeaderIfRequestNotSecure()
        {
            var mockResponse = new Mock<HttpResponseBase>();
            var mockRequest = new Mock<HttpRequestBase>();
            var mockSecurityEvaluator = new Mock<ISecurityEvaluator>();
            var enricher = new HstsResponseEnricher();

            var settings = new Settings {
                EnableHsts = true,
                HstsMaxAge = 42
            };
            mockSecurityEvaluator.Setup(e => e.IsSecureConnection(mockRequest.Object, settings)).Returns(false);

            enricher.Enrich(mockResponse.Object, mockRequest.Object, mockSecurityEvaluator.Object, settings);

            mockResponse.Verify(resp => resp.AddHeader(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
        }
        public void CreateReturnsPortSecurityEvaluatorIfSecurityPortHasValue()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            var mockContext = new Mock<HttpContextBase>();
            IDictionary items = new Dictionary<object, object>();
            mockContext.Setup(c => c.Items).Returns(items);
            mockContext.SetupGet(c => c.Request).Returns(mockRequest.Object);
            var settings = new Settings {
                SecurityPort = 81
            };

            // Act.
            var evaluator = SecurityEvaluatorFactory.Instance.Create(mockContext.Object, settings);

            // Assert.
            Assert.IsType<PortSecurityEvaluator>(evaluator);
        }
        public void EnrichAddsHstsHeaderWithMaxAge()
        {
            const int HstsMaxAge = 42;

            var mockResponse = new Mock<HttpResponseBase>();
            var mockRequest = new Mock<HttpRequestBase>();
            var mockSecurityEvaluator = new Mock<ISecurityEvaluator>();
            var enricher = new HstsResponseEnricher();

            var settings = new Settings {
                EnableHsts = true,
                HstsMaxAge = HstsMaxAge
            };
            mockSecurityEvaluator.Setup(e => e.IsSecureConnection(mockRequest.Object, settings)).Returns(true);

            enricher.Enrich(mockResponse.Object, mockRequest.Object, mockSecurityEvaluator.Object, settings);

            mockResponse.Verify(resp => resp.AddHeader("Strict-Transport-Security", string.Format("max-age={0:f0}", HstsMaxAge)));
        }
        public void IsSecureConnectionReturnsFalseIfNoHeaderMatchesAnOffloadHeader()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.ServerVariables).Returns(new NameValueCollection {
                { "SOME_VARIABLE", "some-value" }
            });

            var settings = new Settings {
                OffloadedSecurityServerVariables = "HTTP_X_FORWARD_PROTOCOL=HTTPS"
            };

            var evaluator = new ServerVariablesSecurityEvaluator();

            // Act.
            var result = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.False(result);
        }
        public void IsSecureConnectionReturnsFalseIfNoHeaderMatchesAnOffloadHeader()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.Headers).Returns(new NameValueCollection {
                { "SOME_HEADER", "some-value" }
            });

            var settings = new Settings {
                OffloadedSecurityHeaders = "SSL_REQUEST=on"
            };

            var evaluator = new HeadersSecurityEvaluator();

            // Act.
            var result = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.False(result);
        }
        public void CreateReturnsHeadersSecurityEvaluatorIfSecurityPortNotSpecifiedAndServerVariablesNotExpectedAndHeadersExpectedAndPresent()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.Headers).Returns(new NameValueCollection());
            var mockContext = new Mock<HttpContextBase>();
            IDictionary items = new Dictionary<object, object>();
            mockContext.Setup(c => c.Items).Returns(items);
            mockContext.SetupGet(c => c.Request).Returns(mockRequest.Object);

            var settings = new Settings {
                OffloadedSecurityHeaders = "SSL="
            };

            // Act.
            var evaluator = SecurityEvaluatorFactory.Instance.Create(mockContext.Object, settings);

            // Assert.
            Assert.IsType<HeadersSecurityEvaluator>(evaluator);
        }
        public void IsSecureConnectionReturnsTrueOnlyIfPortMatchesSecurityPort()
        {
            // Arrange.
            var uri = new Uri("http://www.mysite.com:81/admin/");
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.Url).Returns(uri);

            var settings = new Settings();
            var evaluator = new PortSecurityEvaluator();

            // Act.
            settings.SecurityPort = 80;
            var resultWithPortMisMatch = evaluator.IsSecureConnection(mockRequest.Object, settings);

            settings.SecurityPort = 81;
            var resultWithPortMatch = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.False(resultWithPortMisMatch);
            Assert.True(resultWithPortMatch);
        }
        public void IsSecureConnectionReturnsTrueIfHeaderMatchesAnOffloadHeader()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.ServerVariables).Returns(new NameValueCollection {
                { "SOME_VARIABLE", "some-value" },
                { "HTTP_X_FORWARD_PROTOCOL", "HTTPS" }
            });

            var settings = new Settings();
            var evaluator = new ServerVariablesSecurityEvaluator();

            // Act.
            settings.OffloadedSecurityServerVariables = "HTTP_X_FORWARD_PROTOCOL=HTTPS";
            var resultWithHeaderValueMatch = evaluator.IsSecureConnection(mockRequest.Object, settings);

            settings.OffloadedSecurityServerVariables = "HTTP_X_FORWARD_PROTOCOL=";
            var resultWithJustHeaderPresent = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.True(resultWithHeaderValueMatch);
            Assert.True(resultWithJustHeaderPresent);
        }
        public void IsSecureConnectionReturnsTrueIfHeaderMatchesAnOffloadHeader()
        {
            // Arrange.
            var mockRequest = new Mock<HttpRequestBase>();
            mockRequest.SetupGet(req => req.Headers).Returns(new NameValueCollection {
                { "SOME_HEADER", "some-value" },
                { "SSL_REQUEST", "on" }
            });

            var settings = new Settings();
            var evaluator = new HeadersSecurityEvaluator();

            // Act.
            settings.OffloadedSecurityHeaders = "SSL_REQUEST=on";
            var resultWithHeaderValueMatch = evaluator.IsSecureConnection(mockRequest.Object, settings);

            settings.OffloadedSecurityHeaders = "SSL_REQUEST=";
            var resultWithJustHeaderPresent = evaluator.IsSecureConnection(mockRequest.Object, settings);

            // Assert.
            Assert.True(resultWithHeaderValueMatch);
            Assert.True(resultWithJustHeaderPresent);
        }
		/// <summary>
		/// Reads general settings from the SecuritySwitch section into the given Settings instance.
		/// </summary>
		/// <param name="section">The XmlNode to read from.</param>
		/// <param name="settings">The Settings instance to set.</param>
		protected void ReadGeneralSettings(XmlNode section, Settings settings) {
			// Get the mode attribute.
			if (section.Attributes["mode"] != null) {
				string ModeValue = section.Attributes["mode"].Value.Trim();
				if (Enum.IsDefined(typeof(Mode), ModeValue))
					settings.Mode = (Mode)Enum.Parse(typeof(Mode), ModeValue);
				else
					throw new SecuritySwitchSectionException("Invalid value for the 'mode' attribute.", section);
			}

			// Get the encryptedUri attribute.
			if (section.Attributes["encryptedUri"] != null)
				settings.EncryptedUri = section.Attributes["encryptedUri"].Value;

			// Get the unencryptedUri attribute.
			if (section.Attributes["unencryptedUri"] != null)
				settings.UnencryptedUri = section.Attributes["unencryptedUri"].Value;

			// Validate that if either encryptedUri or unencryptedUri are set, both must be set.
			if (
				(settings.EncryptedUri.Length > 0 && settings.UnencryptedUri.Length == 0) ||
				(settings.UnencryptedUri.Length > 0 && settings.EncryptedUri.Length == 0))
				throw new SecuritySwitchSectionException("You must specify both 'encryptedUri' and 'unencryptedUri', or neither.", section);

			// Get the maintainPath attribute.
			if (section.Attributes["maintainPath"] != null) {
				string Value = section.Attributes["maintainPath"].Value.ToLower();
				settings.MaintainPath = (Value == "true" || Value == "yes" || Value == "on");
			}

			// Get the warningBypassMode attribute.
			if (section.Attributes["warningBypassMode"] != null) {
				string WarningBypassModeValue = section.Attributes["warningBypassMode"].Value.Trim();
				if (Enum.IsDefined(typeof(SecurityWarningBypassMode), WarningBypassModeValue))
					settings.WarningBypassMode = (SecurityWarningBypassMode)Enum.Parse(typeof(SecurityWarningBypassMode), WarningBypassModeValue);
				else
					throw new SecuritySwitchSectionException("Invalid value for the 'warningBypassMode' attribute.", section);
			}

			// Get the bypassQueryParamName attribute.
			if (section.Attributes["bypassQueryParamName"] != null)
				settings.BypassQueryParamName = section.Attributes["bypassQueryParamName"].Value;
		}
		/// <summary>
		/// Creates an instance of EvaluateRequestEventArgs with an instance of Settings.
		/// </summary>
		/// <param name="application">The HttpApplication for the current context.</param>
		/// <param name="settings">An instance of Settings used for the evaluation of the request.</param>
		public EvaluateRequestEventArgs(HttpApplication application, Settings settings)
			: base() {
			_application = application;
			_settings = settings;
		}
 /// <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>
 /// Creates an instance of EvaluateRequestEventArgs with the specified application and settings.
 /// </summary>
 /// <param name="context">The current context.</param>
 /// <param name="settings">An instance of Settings used for the evaluation of the request.</param>
 public EvaluateRequestEventArgs(HttpContextBase context, Settings settings)
 {
     ExpectedSecurity = null;
     Context = context;
     Settings = settings;
 }
		/// <summary>
		/// Requests the current page over a secure connection, if it is not already.
		/// </summary>
		/// <param name="settings">The Settings to use for this request.</param>
		public static void RequestSecurePage(Settings settings) {
			// Determine the response path, if any.
			string ResponsePath = DetermineSecurePage(settings, false);
			if (ResponsePath != null && ResponsePath != string.Empty)
				// Redirect to the secure page.
				HttpContext.Current.Response.Redirect(ResponsePath, true);
		}
		/// <summary>
		/// Requests the current page over an unsecure connection, if it is not already.
		/// </summary>
		/// <param name="settings">The Settings to use for this request.</param>
		public static void RequestUnsecurePage(Settings settings) {
			// Determine the response path, if any.
			string ResponsePath = DetermineUnsecurePage(settings, false);
			if (ResponsePath != null && ResponsePath != string.Empty) {
				HttpRequest Request = HttpContext.Current.Request;

				// Test for the need to bypass a security warning.
				bool Bypass;
				if (settings.WarningBypassMode == SecurityWarningBypassMode.AlwaysBypass)
					Bypass = true;
				else if (settings.WarningBypassMode == SecurityWarningBypassMode.BypassWithQueryParam &&
						Request.QueryString[settings.BypassQueryParamName] != null) {
					Bypass = true;

					// Remove the bypass query parameter from the URL.
					System.Text.StringBuilder NewPath = new System.Text.StringBuilder(ResponsePath);
					int i = ResponsePath.LastIndexOf(string.Format("?{0}=", settings.BypassQueryParamName));
					if (i < 0)
						i = ResponsePath.LastIndexOf(string.Format("&{0}=", settings.BypassQueryParamName));
					NewPath.Remove(i, settings.BypassQueryParamName.Length + Request.QueryString[settings.BypassQueryParamName].Length + 1);

					// Remove any abandoned "&" character.
					if (i >= NewPath.Length)
						i = NewPath.Length - 1;
					if (NewPath[i] == '&')
						NewPath.Remove(i, 1);

					// Remove any abandoned "?" character.
					i = NewPath.Length - 1;
					if (NewPath[i] == '?')
						NewPath.Remove(i, 1);

					ResponsePath = NewPath.ToString();
				} else
					Bypass = false;

				// Output a redirector for the needed page to avoid a security warning.
				HttpResponse Response = HttpContext.Current.Response;
				if (Bypass) {
					// Clear the current response.
					Response.Clear();

					// Add a refresh header to the response for the new path.
					Response.AddHeader("Refresh", string.Concat("0;URL=", ResponsePath));

					// Also, add JavaScript to replace the current location as backup.
					Response.Write("<html><head><title></title>");
					Response.Write("<!-- <script language=\"javascript\">window.location.replace(\"");
					Response.Write(ResponsePath);
					Response.Write("\");</script> -->");
					Response.Write("</head><body></body></html>");

					Response.End();
				} else
					// Redirect to the insecure page.
					Response.Redirect(ResponsePath, true);
			}
		}
 /// <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);