Encrypting Web.config and installing - asp.net

I am new to the encryption process and have tried unsuccessfully to install an encrypted web.config file onto a hosting companies server. I am using Microsoft Visual Web Developer 2010 Express.
I have followed the steps located in Walkthrough: Encrypting Configuration Information Using Protected several times.
Please Note regarding the walkthrough, I do not have any machineKeys in my web.config file, so I skipped that encryption step.
When I Ran the aspnet_regiis -pef connectionStrings "c:\Users......\mywebsite.com"
Return is:
Encrypting configuration section ...
Succeeded!
2) I then FTP my web.config file and the site gets the below error: Note: The Line 8 is highlighted)
Server Error in '/' Application.
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: Failed to decrypt using provider 'RsaProtectedConfigurationProvider'. Error message from the provider: Bad Data.
Source Error:
Line 6:
Line 7:
Line 8:
Line 10:
Source File: C:\HostingSpaces*username**mywebsite.com*\wwwroot\web.config Line: 8
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1
I know there must be some piece missing but I have searched and have not found anything. I emailed the hosting company to find out if they need to do anything regarding encrypting web sites and they have not responded yet.
What I would expect is that there is a key that resides elsewhere which takes the encrypted value and decrypts it using an algorhythm. If this is so, where would I get that key and where would it go.
Any help is greatly appreciated and somewhat surprised I cannot find any issues similar to this on the web.
Thanks Much.

I don't have a direct answer to your question, but here's a simple technique to encrypt web.config. It may not be the best way, but it might be enough to get you started. This technique encrypts web.config during application start-up.
VERY IMPORTANT: make sure this code only runs in production. If you run it during development, you'll encrypt your source web.config and you won't be able to get it back.
private static void EncryptConfig() {
System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath);
foreach (string sectionName in new[] { "connectionStrings", "appSettings" }) {
ConfigurationSection section = config.GetSection(sectionName);
if (!section.SectionInformation.IsProtected) {
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
}
}
config.Save();
}
You can then call this method in Application_Start()
protected void Application_Start() {
if (IsProduction) {
EncryptConfig();
}
}
This solution isn't perfect because when you deploy your web.config to your production server, it won't be encrypted. Because the encryption happens during runtime, it will only be encrypted once your application starts. When the first request comes in, web.config will be encrypted. When the second request comes in, your app will need to restart because asp.net will detect that web.config was changed. And then from that point on, your app will operate normally with an encrypted web.config. The benefit of this technique is that the encryption happens automatically. Whenever you deploy a new web.config file, it will automatically be encrypted during start-up.
Important: Make sure that EncryptConfig() only runs in production so that you don't encrypt your source web.config.

Jonny O - Thanks. This worked so easily. CP
I added the global.asax file and here are the code snippets that went into this file (global.asax.cs).
Granted much of this is duplicated from above, but it is my entire solution. Thanks again.
using System.Web.Configuration;
using System.Configuration;
using System.Web.Hosting;
protected void Application_Start(object sender, EventArgs e)
{
//Test to see if this app is being started on the development machine (e.g. in the debugger)
//This code will encript web.config the first time this program runs.
//Therefore, it is important to have a backup copy of the non-encrypted web.config as this
//code below will encrypt it, which is what we want to happen on the production server.
if (! System.Diagnostics.Debugger.IsAttached )
{
EncryptConfig(); //See below
}
}
/// <summary>
/// This technique of encrypting the web.config file was learned from this forum post:
/// http://stackoverflow.com/questions/5602630/encrypting-web-config-and-installing
/// </summary>
private static void EncryptConfig()
{
System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath);
foreach (string sectionName in new[] { "connectionStrings", "appSettings" })
{
ConfigurationSection section = config.GetSection(sectionName);
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
}
}
config.Save();
}

Related

Invalid encryption key after website published

So i managed to encrypt my connectionstrings on my localhost, and everything was fine.. it could read without any problems.
Now after i have published my project on my webhote, the story is quite different.
I get following error:
Key not valid for use in specified state. (Exception from HRESULT: 0x8009000B)
Im wondering if i should decrypt it locally and thereafter encrypt it after it has been published to my webhotel? i have seen another thread where people suggest, that a machinekey should be added. but where would i place it, and where would i find it?
the whole stacktrace can be seen here
I used the following cmd to encrypt:
aspnet_regiis -pef "connectionStrings" "PATH" -prov "DataProtectionConfigurationProvider"
So i figured that the following error is yielded when the machinekey doesnt correspond with the key that has been used to encrypt the string in the first place (my localhost).
Therefore i had to make the following method:
private void ProtectSection(string sectionName, string provider)
{
Configuration config = WebConfigurationManager.OpenWebConfiguration("~/");
ConfigurationSection section = config.GetSection(sectionName);
if (section != null && !section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection(provider);
config.Save();
}
}
And call it in my global.asax file..
By doing so, i first uploaded my web.config unprotected and then asked my web hotel encrypt my connection string when the website was run the first time. Obviously it therefore used it's own machinekey, and nothing therefore conflicted.

Issue with CompileAssemblyFromSource when called from a WCF app

I have a WCF service that exposes a method. When a client calls this method, the following occurs:
the method does some processing
it tries to load an assembly if its already there
if the above dll isn't there, it generates C# code, compiles it using CSharpCodeProvider's CompileAssemblyFromSource api
It then loads the just generated assembly
Now the problem. The first time the method it called, it generates the assembly at point 3), and when it tries to return the assembly reference via CompilerResults.CompiledAssembly it throws a file not found exception. However, I can clearly see that the assembly has been generated at the specified location and I can open it with other applications.
If I call the method again via a client, it is able to load the assembly (it was successfully generated as a result of the previous call) and goes ahead to do the remaining set of tasks. Only when the assembly isnt there and it generates it and goes ahead to load it immediately, I get that exception. Any ideas? I have tried playing around with the web.config, changing impersonation to true/false. I have a seperate app pool to run this web application and I tried changing the identity of the app pool from local service to local system, even gave my windows logon credentials which has admin rights but no luck.
Any help would be much appreciated.
Are you sure it's generating the assembly? I have the same problem, except I can't find the generated .dll. I first suspected it was unable to write to the folder, so it now calls CreateDirectory and drops a text file to demonstrate that the folder is writeable.
Anyway, same problem, no success. Is it really the case that nobody else has had this issue??
I'm going to remote debug the server & see if I can step through Microsoft's PDBs...
-- EDIT --
No need to step through Microsoft's code. I had a look at the Errors collection of the CompilerResults and there was 1 item in there: "Metadata file 'c:\Windows\System32\aaclient.dll' could not be opened -- 'An attempt was made to load a program with an incorrect format. '"
When I get Directory.GetCurrentDirectory() to pick up the other DLLs it's usign the Windows System32 directory...
-- EDIT --
Resolved this by adding references from the executing assembly's folder:
CompilerParameters compilerParameters = new CompilerParameters
{
OutputAssembly = Path.Combine(GeneratedAssembliesFolder, string.Format("{0}.Generated.dll", typeName))
};
string executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string[] dllFiles = Directory.GetFiles(executingDirectory, "*.dll");
compilerParameters.ReferencedAssemblies.AddRange(dllFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
IEnumerable<string> exeFiles =Directory.GetFiles(executingDirectory, "*.exe").Where(f => !f.Contains(".vshost."));
compilerParameters.ReferencedAssemblies.AddRange(exeFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
For greater robustness one aught to add checks for the binaries being valid managed code assemblies. This code could also be shortened by using a Linq .Union between the two GetFiles calls.
To find a suitable folder to write to:
private static string generatedAssembliesFolder;
private static string GeneratedAssembliesFolder
{
get
{
if (generatedAssembliesFolder == null)
{
string[] candidateFolders = new[]
{
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine),
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
foreach (string candidateFolder in candidateFolders)
{
try
{
if (!Directory.Exists(candidateFolder)) Directory.CreateDirectory(candidateFolder);
string testFileName = Path.Combine(candidateFolder, Path.GetRandomFileName());
File.WriteAllBytes(testFileName, new byte[0]);
File.Delete(testFileName);
}
catch (Exception ex)
{
continue;
}
generatedAssembliesFolder = candidateFolder;
break;
}
}
return generatedAssembliesFolder;
}
}
Thanks user1796307 for your input. I had resolved this issue, but forgot to update it. Sharing it for everyone's benefit. It was the .NET Fusion at play. It caches assembly load path and won't even try to load an assembly if a previous attempt to load from same location had failed. In other words:
if (Assembly.Load("C:\xyz.dll") == null)
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will still fail
}
The solution is to change it as:
if (!File.Exists("C:\xyz.dll")
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will now work
}

Encrypt Connection String in Web.Config Fails

I would like to encrypt the connection string of my web.config. Here I have found a nice example on how to do this. I implemented this and on my development machine this runs find.
However if I upload it to the provider, it does not work with the following error:
[SecurityException: Request failed.]
System.Configuration.DpapiProtectedConfigurationProvider.Encrypt(XmlNode node)
In this blog I have read, that this is because of the web probably runs in medium trust and therefore WebConfigurationManager.OpenWebConfiguration can not be used. Instead of this, WebConfigurationManager.GetSection should be used. However, if I get the section as proposed, the call to ProtectSection fails with the following error message:
System.InvalidOperationException: This operation does not apply at runtime
Can anyone lead me to a solution, how I can encode (and decode) the connection string in the web.config file (at runtime)?
Update
Not a real answer to the question, but the hoster gave full trust to the web and now, all worked fine. I leave the quesion open, maybe someone posts a solution to the original question and helps with this people having the same problem but not getting full trust.
From http://msdn.microsoft.com/en-us/library/89211k9b%28v=vs.80%29.aspx
static void ToggleWebEncrypt()
{
// Open the Web.config file.
Configuration config = WebConfigurationManager.
OpenWebConfiguration("~");
// Get the connectionStrings section.
ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;
// Toggle encryption.
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
}
else
{
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
}
// Save changes to the Web.config file.
config.Save();
}
UPDATE
Also, ensure that your service account has write permissions to the Web.config. Also, be aware that granting write permissions to your service account on the Web.config increases somewhat the security footprint of your application. Only do so if you understand and accept the risks.

How to troubleshoot "System.Web.HttpException (0x80004005): File does not exist"?

Apologies if this has already been answered on this site, I searched but did not find this exact scenario.
I'm adding log4net to a WCF service. I added a handler in the Application_Error event, and it is catching a file not found error on every request.
I've seen this with web sites, and usually the error can be traced down to not having a "favicon" file in the root directory, or to a missing image referenced in a css stylesheet.
However, this is a WCF service, there is no CSS stylesheet, and adding a favicon to the root did not solve the problem.
Does anyone else have a good way to troubleshoot this?
Some clues:
I haven't deployed this yet to the real IIS server, I'm running it locally.
The error does not happen when I am running in DEBUG inside Visual Studio, only when I access the service from a web browser (IE or Chrome)
I added the url and file path to the error message, and this is what they are:
URL: http://localhost:3994/
FilePath: /
Error: System.Web.HttpException (0x80004005): File does not exist.
Edit: the above values are what show up in the logged exception:
protected void Application_Error(object sender, EventArgs e)
{
var objErr = Server.GetLastError().GetBaseException();
if (objErr is System.Web.HttpException)
{
var filePath = Context.Request.FilePath;
var url = ((HttpApplication) sender).Context.Request.Url;
Log.Error("URL: " + url + "; FilePath: " + filePath, objErr);
} else
Log.Error("Application Error", objErr);
}
Any help would be greatly appreciated.
The reason is likely that the service has not been specified. When a web server (the local dev one as well) recieve a request for a folder they look inside that folder for the default page (usually called: index.htm, index.html, default.asp, default.aspx, etc) and present that (unless you are using a REST based service description). When you run from VS the debug will take you straight to the actual service.
In this case because you have built a service you need to specify the location of the exact service i.e. http://localhost:3994/service.svc.
Also: If you start a debug session and then change the URL to http://localhost:3994/ you should be able to check this is the case with the debugger.

ASP.NET: WebResource.axd call 404 error: how to know which assembly/resource is missing or responsible?

I get a 404 HTTP status error (not found) on a specific WebResource.axd call inside an ASP.NET 3.5 (AJAX) web application. I guess the error is thrown because a specific referenced assembly is missing in the bin folder/GAC. But I don't know which, since the page which requests the resource is very complex (I'm using third-party controls and ASP.NET Ajax.)
Is it possible to know from the encrypted "d" querystring parameter of the query, like:
.../WebResource.axd?d=...
which assembly should create the content and is possibly missing?
Note: There are other WebRequest.axd calls which execute with success.
One of the reasons for this issue is that the registered embedded resource path is incorrect or the resource is not there. Make sure the resource file is added as a Embedded Resource.
Asp.net uses the WebResourceAttribute which you must give the path to the resource.
The resource file needs to be added as a Embedded Resource to the project and the path to it would be the full namespace plus the file name.
So you have the following project resource "my.js" in the project "MyAssembly" the resource path would be "MyAssembly.my.js".
To check what file the web resource handler is not finding you can decrypt the hash code provided on the WebResource.axd url. Please see the example below an how to do that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
byte[] encryptedData = HttpServerUtility.UrlTokenDecode("encoded hash value");
Type machineKeySection = typeof(System.Web.Configuration.MachineKeySection);
Type[] paramTypes = new Type[] { typeof(bool), typeof(byte[]), typeof(byte[]), typeof(int), typeof(int) };
MethodInfo encryptOrDecryptData = machineKeySection.GetMethod("EncryptOrDecryptData", BindingFlags.Static | BindingFlags.NonPublic, null, paramTypes, null);
try
{
byte[] decryptedData = (byte[])encryptOrDecryptData.Invoke(null, new object[] { false, encryptedData, null, 0, encryptedData.Length });
string decrypted = System.Text.Encoding.UTF8.GetString(decryptedData);
decryptedLabel.Text = decrypted;
}
catch (TargetInvocationException)
{
decryptedLabel.Text = "Error decrypting data. Are you running your page on the same server and inside the same application as the web resource URL that was generated?";
}
}
}
}
Original code example by Telerik UI for ASP.NET AJAX Team Link: http://blogs.telerik.com/aspnet-ajax/posts/07-03-27/debugging-asp-net-2-0-web-resources-decrypting-the-url-and-getting-the-resource-name.aspx
This should return the URL path that aspt.net believes the embedded resource is at.
I just spent hours on a similar issue. Due to the great article pointed out by Diadistis I was able to decrypt the WebResource url and find out that my WebResource was translated into a wrong assembly pointer, recognizable by the garbage in front of your resource name. After many struggles I found out that this was because I was using the Page.ClientScript.GetWebResourceUrl in a class deriving from another class which resided outside of the assembly my resource was in. Confusing thing was that my class WAS in the same assembly, though the class deriving from was NOT. The this.GetType() parameter many articles state is a must, turned out not to be so much of a must at all in my situation. Actually, it needed to be replaced with a typeof() and it worked! Hope this may prevent others from getting the same headache as I got from this bugger.
In my case, the source of the 404 error was that the date and time of the machine running IIS were wrong (from the past).
Is your project missing any references?
Are there any references set to CopyLocal=False (common with Infragistics or GAC'ed refs) that dont make it to the destination?
A utility like reflector or dependency walker will tell you if your main assembly is missing any dependencies that are not immediately obvious.
Does the Application_Error handler in global.asax have a catch that is producing any error info (FileNotFoundExceptions)?
Did you set custom errors to 'remote only' and browse the site from the local machine?
This same issue occurs if there is a request filtering rule that detects the specified string in the query string. In my case, the query string for the AXD file was generated with a double dash that the rule detected and caused a 404 not found error for the AXD file request.

Resources