Web Q&A

Locking Pop-Up Blocker, Mixed Authentication, and More

Edited by Nancy Michell

Q How can I prevent users from disabling the Windows® XP SP2 pop-up blocker in Microsoft Internet Explorer?

Q How can I prevent users from disabling the Windows® XP SP2 pop-up blocker in Microsoft Internet Explorer?

A Setting the permissions to "deny" on this registry key will pre-vent the user from changing the pop-up blocker settings:

HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\New Windows

Internet Explorer will have the pop-up blocker on by default.

A Setting the permissions to "deny" on this registry key will pre-vent the user from changing the pop-up blocker settings:

HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\New Windows

Internet Explorer will have the pop-up blocker on by default.

Q I'd like to have one ASP.NET application that uses Windows authentication. When the user logs onto that app, he could click a link on the page that points to a second ASP.NET app that uses Basic authentication. Is there any way to pass the logged-on user's credentials to the second app using Basic authentication, so that the user doesn't get a second prompt for credentials?

Q I'd like to have one ASP.NET application that uses Windows authentication. When the user logs onto that app, he could click a link on the page that points to a second ASP.NET app that uses Basic authentication. Is there any way to pass the logged-on user's credentials to the second app using Basic authentication, so that the user doesn't get a second prompt for credentials?

A If you know the user name and password, it is possible to add the Basic credentials to the CredentialCache before making the WebRequest to the second application. However, it is not possible to get the user's password when they have logged in with Integrated Windows authentication.

A If you know the user name and password, it is possible to add the Basic credentials to the CredentialCache before making the WebRequest to the second application. However, it is not possible to get the user's password when they have logged in with Integrated Windows authentication.

If your second application will also accept Windows authentication, then you can add those to the CredentialCache instead, as long as delegation is supported. If both use Basic authentication, you can successfully call to the second Web application with the supplied credentials. Figure 1 shows some code that will allow you to pull the credentials from the first request and pass them on to the next. The sample shows how to add all three types of IIS authentication credentials. All can be used to make subsequent requests to another application on the same IIS server, but only the Basic credentials can be used to make a request to a different server. This is an added security feature that is built into IIS so that account could not be misused to access anything on other machines. See the "Authentication and impersonation types" and "Token types" sections of the article "Accessing Network Files from IIS Applications" for more details.

Figure 1 Retrieving Basic Credentials

// Get the type of authentication
string authType = Request.ServerVariables["AUTH_TYPE"];

// If the request is not anonymous, get incoming creds
if (authType != null && authType.Length > 0)  
{
    if (authType.Equals("Basic")) // add Basic credentials
    {
        // Get "Basic username:password" from the WWW-Authenticate header
        // and remove "Basic ".  "username:password" is base64-encoded.
        string auth = Request.Headers["Authorization"].Substring(6);
        string userinfo = Encoding.ASCII.GetString(
            Convert.FromBase64String(Auth));

        // Parse out the username and password
        int colonPos = userinfo.IndexOf(":");
        string username = userinfo.Substring(0, colonPos);
        string password = userinfo.Substring(colonPos+1);

        NetworkCredential basicCreds = 
            new NetworkCredential(username,password);
        CredentialCache credCache = new CredentialCache();
        MyCredentialCache.Add(theUrl, "Basic", basicCreds);
        myProxy.Credentials = credCache;
    }
    else // Set default credentials, often Windows integrated
    {
        myProxy.Credentials = CredentialCache.DefaultCredentials;
    }
}
// make outbound request

Q Is there any way I can get the application pools feature found in IIS 6.0 or some functionality like it if I'm using IIS 5.0? I believe the only way to get multiple instances of the ASP.NET worker process under IIS 5.0 is to have applications running under different versions of the runtime.

Q Is there any way I can get the application pools feature found in IIS 6.0 or some functionality like it if I'm using IIS 5.0? I believe the only way to get multiple instances of the ASP.NET worker process under IIS 5.0 is to have applications running under different versions of the runtime.

A IIS 6.0 allows you to isolate applications based on namespace so, for example, all requests to https://server/foo can go to one worker process, while all the requests to https://server/bar can go to another worker process. The decision about which worker process to route to is made in kernel mode (HTTP.SYS).

A IIS 6.0 allows you to isolate applications based on namespace so, for example, all requests to https://server/foo can go to one worker process, while all the requests to https://server/bar can go to another worker process. The decision about which worker process to route to is made in kernel mode (HTTP.SYS).

IIS 5.0 doesn't follow this model. Every request has to go through INETINFO.EXE, which hosts the W3SVC service. INETINFO.EXE looks at the configured script maps and figures out which ISAPI extension to route the request to. For instance, ASPX requests go to ASPNET_ISAPI.DLL. ASPNET_ISAPI.DLL implements a process model that forwards requests to ASPNET_WP.EXE processes. This is also based on namespace, but only requests mapped to ASPNET_ISAPI.DLL can take advantage of the ASP.NET process model. In addition, routing from INETINFO.EXE to ASPNET_WP.EXE and back is relatively expensive (it involves a process hop).

If you want multiple instances of ASP.NET worker processes, you can follow the advice from Knowledge Base article 812833, "FIX: You cannot run multiple processes of ASP.NET at the same time". While the article alludes to being a fix on Windows Server™ 2003, it is really an answer to users unable to load multiple ASP.NET worker processes, which is what you're trying to do on IIS 5.0.

Q I need to spawn a process in IIS, run some batch files, and then return the batch files results to the user. The Knowledge Base article at How to spawn a process that runs under the context of the impersonated user in Microsoft ASP.NET pages discusses how to spawn a process that runs under the context of the impersonated user in ASP.NET pages, but it's not helping me. What should I do?

Q I need to spawn a process in IIS, run some batch files, and then return the batch files results to the user. The Knowledge Base article at How to spawn a process that runs under the context of the impersonated user in Microsoft ASP.NET pages discusses how to spawn a process that runs under the context of the impersonated user in ASP.NET pages, but it's not helping me. What should I do?

A An example of how to spawn a process and read standard out- put (and so forth) is shown in the code in Figure 2. This code does not perform any impersonation, which may be a requirement for your scenario. If it is, you're going to have to follow the example in the Knowledge Base article you mentioned.

A An example of how to spawn a process and read standard out- put (and so forth) is shown in the code in Figure 2. This code does not perform any impersonation, which may be a requirement for your scenario. If it is, you're going to have to follow the example in the Knowledge Base article you mentioned.

Figure 2 Spawning a New Process

private bool RunProcess(
    string command, string arguments, string workingDir)
{
    const int timeout = 30000; // 30 seconds

    ProcessStartInfo psi = new ProcessStartInfo(command, arguments);
    psi.RedirectStandardOutput = true;
    psi.RedirectStandardError = true;
    psi.UseShellExecute = false;
    psi.CreateNoWindow = true;
    if (workingDir != null) psi.WorkingDirectory = workingDir; 

    bool retValue = false;
    using(Process p = System.Diagnostics.Process.Start(psi))
    {
        using(StreamReader stdOut = p.StandardOutput)
        {
            using(StreamReader stdErr = p.StandardError)
            {
                if (!p.WaitForExit(timeout))
                {
                    p.Kill(); // kill the process after 30 seconds
                }
                else if (p.ExitCode != 0)
                {
                    string output = stdOut.ReadToEnd();
                    string error = stdErr.ReadToEnd();
                    ... // use output and error info here for logging
                }
                else retValue = true;
            }
        }
    }
    return retValue;
}

Figure 2 Spawning a New Process

Q Is it possible to have ASP.NET service two Web requests for the same ASP.NET session in two separate worker processes? For instance, when the existing worker process is getting recycled while servicing a request for one session and at the same time another request for that same session arrives, will the second request be processed by the old worker process or will it be processed on the new one?

Q Is it possible to have ASP.NET service two Web requests for the same ASP.NET session in two separate worker processes? For instance, when the existing worker process is getting recycled while servicing a request for one session and at the same time another request for that same session arrives, will the second request be processed by the old worker process or will it be processed on the new one?

Also, does ASP.NET provide any guarantee that session state for the same session is not loaded into two worker processes simultaneously? Otherwise, this could leave the session in an inconsistent state.

A There is a locking mechanism, so the problem you envision should not occur (moreover, if the process is being recycled, it's unlikely that it will be tinkering with session variables at that time). For out-of-process session state, the locking mechanism allows concurrent access to the same session data only in the event that all concurrent requests manipulate session state in a read-only manner (such as <@ Page EnableSessionState="ReadOnly" ... > ).

A There is a locking mechanism, so the problem you envision should not occur (moreover, if the process is being recycled, it's unlikely that it will be tinkering with session variables at that time). For out-of-process session state, the locking mechanism allows concurrent access to the same session data only in the event that all concurrent requests manipulate session state in a read-only manner (such as <@ Page EnableSessionState="ReadOnly" ... > ).

However, if read-only session state is not specified, then in the scenario described, any new inbound requests for session state would actually be blocked by the ASP.NET runtime until the old, pending request finished executing the page and subsequently released its lock on the session data. Each time a request successfully gets an exclusive lock on a piece of session data, the lock is valid for the execution timeout for a request.

If a second request starts up and asks for the same piece of session data, it will encounter the already existing exclusive lock and do one of two things. If the lock is older than the execution timeout, the second request will force the lock to be released and the second request will take exclusive ownership of the session state. If the lock is still valid though, the second request will spin on a timer, attempting to acquire the exclusive session state lock roughly every 500 milliseconds.

The second request will continue spinning until the first request completes, or until the lock becomes older than the execution timeout, or until the second request itself exceeds the execution timeout. Normally the first request completes execution at some point in time before the request execution timeout—and then when the second request hits the next polling interval it detects that the lock has been released and the second request then exclusively locks and takes control of the session state data.

Q Using a few client-side lines of JScript®, I can add (or remove) rows and cells to an HTML table, but when submitting the page all those changes are lost (newly added rows for instance). I guess this happens because they are not part of the ViewState content.

Q Using a few client-side lines of JScript®, I can add (or remove) rows and cells to an HTML table, but when submitting the page all those changes are lost (newly added rows for instance). I guess this happens because they are not part of the ViewState content.

How can I persist those changes and save them between page postbacks, apart from moving all of that logic server side? I'm thinking of reading the value of the hidden __VIEWSTATE variable in JScript, decoding it, appending data to the string, re-encoding the ViewState, and so forth.

A Tampering with ViewState is something you do not want to do. Even though the encoding at this point is Base64, there is no guarantee it will stay that way in the future. For instance in the Microsoft® .NET Framework 2.0 the persistence format is very different from what the .NET Framework 1.1 uses.

A Tampering with ViewState is something you do not want to do. Even though the encoding at this point is Base64, there is no guarantee it will stay that way in the future. For instance in the Microsoft® .NET Framework 2.0 the persistence format is very different from what the .NET Framework 1.1 uses.

Also, if you have EnableViewStateMac turned on (everyone should), you will get Invalid ViewState exceptions on the server, since there is a hash added at the end that needs to match the right ViewState. (If you have this turned off, you are taking a big security risk, since someone could hijack the ViewState and attempt a script injection attack.)

So having said all that, it is better and safer to use an approach where you register your own hidden field and set its value to your own persistence format (maybe a comma-separated value). Then on the server side, check for that form field and, if it's there, add the HTML back in on the server side.

Send your questions and comments to  webqa@microsoft.com.

Thanks to the following Microsoft developers for their technical expertise: Greg Bybee, Carlo Cardella, David Chen, Dino Chiesa, Thomas Deml, Nick Duane, Chris Gariepy, Jimmy Leyow, Carlos Aguilar Mares, Mark Michelet, Dan Nemesek, John Nisi, Shahar Prish, Karthik Raman, Stefan Schackow, Adam Semel, Karl Tietze, Stephen Toub, and Radomir Zaric.