/// <summary> /// Initializes the module by hooking the application's BeginRequest event if indicated by the config settings. /// </summary> /// <param name="application">The HttpApplication this module is bound to.</param> public void Init(HttpApplication application) { // Get the settings for the secureWebPages section SecureWebPageSettings Settings = (SecureWebPageSettings)ConfigurationManager.GetSection("CommerceFramework/SSL"); if (Settings != null && Settings.Mode != SecureWebPageMode.Off) { // Store the settings in application state for quick access on each request application.Application["SecureWebPageSettings"] = Settings; // Add a reference to the private Application_BeginRequest handler for the // application's BeginRequest event application.BeginRequest += (new EventHandler(this.Application_BeginRequest)); } }
/// <summary> /// Requests the current page over a secure connection, if it is not already. /// </summary> /// <param name="hostPath">The host path to redirect to if needed.</param> /// <param name="maintainApplicationPath"> /// A flag indicating whether or not to maintain the current application path if /// a redirect is necessary. /// </param> public static void RequestSecurePage(SecureWebPageSettings settings) { // Is this request secure? string RequestPath = HttpContext.Current.Request.Url.ToString(); if (RequestPath.StartsWith(UnsecureProtocolPrefix)) { // Is there a host path to redirect to? if (settings.EncryptedUri == null || settings.EncryptedUri.Length == 0) { // Replace the protocol of the requested URL with "https" RequestPath = RequestPath.Replace(UnsecureProtocolPrefix, SecureProtocolPrefix); } else { // Build the URL with the "https" protocol RequestPath = BuildUrl(true, settings.MaintainPath, settings.EncryptedUri, settings.UnencryptedUri); } // Redirect to the secure page HttpContext.Current.Response.Redirect(RequestPath, true); } }
/// <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 SecureWebPageSettings instance initialized with the /// read configuration settings. /// </returns> public object Create(object parent, object configContext, XmlNode section) { // Create a SecureWebPageSettings object for the settings in this section SecureWebPageSettings Settings = new SecureWebPageSettings(); // 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 SecureWebPageSectionException(string.Format("'{0}' is not an acceptable setting.", Node.Name), Node); } } // Return the settings return(Settings); }
/// <summary> /// Reads general settings from the secureWebPages section into the given SecureWebPageSettings instance. /// </summary> /// <param name="section">The XmlNode to read from.</param> /// <param name="settings">The SecureWebPageSettings instance to set.</param> protected void ReadGeneralSettings(XmlNode section, SecureWebPageSettings settings) { // Get the mode attribute if (section.Attributes["mode"] != null) { switch (section.Attributes["mode"].Value.ToLower()) { case "on": settings.Mode = SecureWebPageMode.On; break; case "remoteonly": settings.Mode = SecureWebPageMode.RemoteOnly; break; case "localonly": settings.Mode = SecureWebPageMode.LocalOnly; break; case "off": settings.Mode = SecureWebPageMode.Off; break; default: throw new SecureWebPageSectionException("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 SecureWebPageSectionException("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) { switch (section.Attributes["warningBypassMode"].Value.ToLower()) { case "alwaysbypass": settings.WarningBypassMode = SecurityWarningBypassMode.AlwaysBypass; break; case "bypasswithqueryparam": settings.WarningBypassMode = SecurityWarningBypassMode.BypassWithQueryParam; break; case "neverbypass": settings.WarningBypassMode = SecurityWarningBypassMode.NeverBypass; break; default: throw new SecureWebPageSectionException("Invalid value for the 'warningBypassMode' attribute.", section); } } // Get the bypassQueryParamName attribute if (section.Attributes["bypassQueryParamName"] != null) { settings.BypassQueryParamName = section.Attributes["bypassQueryParamName"].Value; } }
/// <summary> /// Handle the application's BeginRequest event by requesting the current /// page securely, if specified. /// </summary> /// <param name="source">The source of the event.</param> /// <param name="e">EventArgs passed in.</param> private void Application_BeginRequest(Object source, EventArgs e) { // Cast the source as an HttpApplication instance HttpApplication Application = (HttpApplication)source; // Retrieve the settings from application state SecureWebPageSettings Settings = (SecureWebPageSettings)Application.Application["SecureWebPageSettings"]; // Determine if this request should be ignored based on the settings' Mode if (RequestMatchesMode(Application.Request, Settings.Mode)) { // Intialize bool MatchFound = false; SecurityType Secure = SecurityType.Insecure; // Get the relative file path of the current request from the application root string RelativeFilePath = Application.Request.Url.AbsolutePath.Remove(0, Application.Request.ApplicationPath.Length).ToLower(); if (!RelativeFilePath.StartsWith("/")) { // Add a leading "/" RelativeFilePath = "/" + RelativeFilePath; } // Get the relative directory of the current request by removing the last segment of the RelativeFilePath string RelativeDirectory = RelativeFilePath.Substring(0, RelativeFilePath.LastIndexOf('/') + 1); // Determine if there is a matching file path for the current request int i = Settings.Files.IndexOf(RelativeFilePath); if (i >= 0) { MatchFound = true; Secure = Settings.Files[i].Secure; } // Try to find a matching directory path, if no file was found i = 0; while (!MatchFound && i < Settings.Directories.Count) { if (Settings.Directories[i].Recurse) { // Match the beginning of the directory if recursion is allowed MatchFound = (RelativeDirectory.StartsWith(Settings.Directories[i].Path)); } else { // Match the entire directory MatchFound = (RelativeDirectory == Settings.Directories[i].Path); } if (MatchFound) { Secure = Settings.Directories[i].Secure; } i++; } bool design = CommonHelper.CheckDesignMode(Application.Context); // Test for match for a secure connection if (MatchFound && Secure == SecurityType.Secure && !design) { SSLHelper.RequestSecurePage(Settings); } else if (Secure != SecurityType.Ignore) { SSLHelper.RequestUnsecurePage(Settings); } } }
/// <summary> /// Requests the current page over an insecure connection, if it is not already. /// </summary> /// <param name="hostPath">The host path to redirect to if needed.</param> /// <param name="maintainApplicationPath"> /// A flag indicating whether or not to maintain the current application path if /// a redirect is necessary. /// </param> public static void RequestUnsecurePage(SecureWebPageSettings settings) { // Is this request secure? HttpRequest Request = HttpContext.Current.Request; string RequestPath = Request.Url.ToString(); if (RequestPath.StartsWith(SecureProtocolPrefix)) { // Is there a different URI to redirect to? if (settings.UnencryptedUri == null || settings.UnencryptedUri.Length == 0) { // Replace the protocol of the requested URL with "http" RequestPath = RequestPath.Replace(SecureProtocolPrefix, UnsecureProtocolPrefix); } else { // Build the URL with the "http" protocol RequestPath = BuildUrl(false, settings.MaintainPath, settings.EncryptedUri, settings.UnencryptedUri); } // 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(RequestPath); int i = RequestPath.IndexOf(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(NewPath.Length - 1, 1); } RequestPath = NewPath.ToString(); } else { Bypass = false; } // Output a redirector for the needed page to avoid a security warning if (Bypass) { HttpResponse Response = HttpContext.Current.Response; Response.Clear(); // Refresh header Response.AddHeader("Refresh", string.Concat("0;URL=", RequestPath)); // JavaScript to replace the current location Response.Write("<html><head><title></title>"); Response.Write("<!-- <script type=\"text/javascript\">window.location.replace(\""); Response.Write(RequestPath); Response.Write("\");</script> -->"); Response.Write("</head><body></body></html>"); Response.End(); return; } // Redirect to the insecure page HttpContext.Current.Response.Redirect(RequestPath, true); } }