I have created a web forms .net application which has jut got one page with four input fields.
I am storing the input parameters in the view state to be available between post backs.
This application is now deployed onto live server which is actually load balanced between two servers.
When I load the page, it works fine. But this application is going to be used by
10,000 users.
There is no machine key on the web.config file for this application. I read in a documentation when a application
is loaded on a web farm(when load balanced between two servers), a machine key is required to preserve
view state information.
So I tried to generated a machine key on my local host with the following settings
Encryption method: SHA1
Decryption method: auto
Validation key:
Automatically generate at runtime (unchecked)
Generate a unique key for each application (unchecked)
Decryption key:
Automatically generate at runtime (unchecked)
Generate a unique key for each application (unchecked)
I used the generated keys on the web.config of live server, but the application fails to load trying to load the login
page which is not my start page.
So my questions are:
1) Do I really need a machine key as the application is loading without it?
2) When thousands of users use the application page at the same time, is it alright without machine key?
3) If machine key is required, how is it generated and what encryption method, validation key and decryption key
parameters need to be used?
I dont know if you need a machine key. But here's how you generate one: Here is a little console program, which will create a machine key tag for your web.config. You can just copy the text from the console to your web.config like this:
<system.web>
...
<machineKey ... />
</system.web>
And the program. DISCLAIMER: Code not by me, I merely adapted it:
public class KeyCreator
{
const int VALIDATION_KEY_LENGTH = 24;
const int DECRYPTION_KEY_LENGTH = 64;
public static void Main(string[] args)
{
string decryptionKey = CreateKey(VALIDATION_KEY_LENGTH);
string validationKey = CreateKey(DECRYPTION_KEY_LENGTH);
Console.WriteLine("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" validation=\"SHA1\"/>", validationKey, decryptionKey);
Console.ReadKey();
}
static string CreateKey(int numBytes)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[numBytes];
rng.GetBytes(buff);
return BytesToHexString(buff);
}
static string BytesToHexString(byte[] bytes)
{
StringBuilder hexString = new StringBuilder(64);
for (int counter = 0; counter < bytes.Length; counter++)
{
hexString.Append(String.Format("{0:X2}", bytes[counter]));
}
return hexString.ToString();
}
}
I have generated the machine key as below on my local IIS and applied to the config file. I have then published that to live folder and it seems to be working fine.
Encryption method: SHA1
Decryption method: auto
Validation key:
Automatically generate at runtime (unchecked)
Generate a unique key for each application (unchecked)
Related
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.
I have a query regarding BTDF SSO config setting. I am beginner with BizTalk.
I am looking for SSO storage where credentials are stored and retrieved from SSO. I have built-in app located at C:\Program Files (x86)\Deployment Framework for BizTalk 6.0\Framework\DeployToolsork\DeployTools
Could anyone tell me how to store and retrieve from existing SSO config like SSOSettingsEditor which is the default provided by BTDF.
Using BTDF, you can store your configurations as provided in SettingsFileGenerator.xml in BizTalk SSODB. BTDF automatically store your configuration if IncludeSSO property is set to true in btdfproj file.
If you have provided your credential details in SettingsFileGenerator.xml file then only you will find them in SSODB.
You should use SSOSettingsEditor to retrieve or make changes to the configurations. In SSOSettingsEditor, type in your application name and press enter.
Refer to link: BTDF IncludeSSO
BTDF provides a library for modifying SSO Settings that it uses. The method is uses is slightly different from the default Microsoft sample SSO client, so take care regarding which one you're using.
Per that link, the class provides these methods:
namespace SSOSettingsFileManager
{
public static class SSOSettingsManager
{
public static void WriteSetting(string affiliateApplication, string propertyName, string propertyValue);
}
}
It should be fairly straightforward to call that method once you've added a reference to the SSOSettingsFileReader.dll in whatever C# project you have generating your password or updating it, i.e.
string newPassword = GenerateMyPassword();
SSOSettingsFileManager.SSOSettingsManager.WriteSetting("MyApplicationName", "Password", newPassword;);
You could also look at the source of how he's doing it if you want to implement the method yourself.
In ASP.NET can you set the machineKey settings programmatically?
The web app that we use stores sensitive info encrypted in a database, so if we could put the decryptionKey there it would be handy.
No; the machineKey element must be set via config. However, web.config can itself be encrypted, which helps minimize risk of cryptographic key disclosure if an attacker ever gets access to the config file. (This same process can be used to protect SQL connection strings and pretty much any other sensitive config element you wish.) See http://msdn.microsoft.com/en-us/library/dtkwfdky(v=VS.100).aspx for a walkthrough on enabling this.
Yes, you can. I got success using this code in ConsoleApplication:
private static void ChangeWebConfig(string validationKey, string decryptionKey, string webConfigPath)
{
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = webConfigPath;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
MachineKeySection section = (MachineKeySection)config.GetSection("system.web/machineKey");
section.ValidationKey = validationKey;
section.DecryptionKey = decryptionKey;
config.Save();
}
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();
}
I have a classic ASP page - written in JScript - that's using Scripting.FileSystemObject to save files to a network share - and it's not working. ("Permission denied")
The ASP page is running under IIS using Windows authentication, with impersonation enabled.
If I run the following block of code locally via CScript.exe:
var objNet = new ActiveXObject("WScript.Network");
WScript.Echo(objNet.ComputerName);
WScript.Echo(objNet.UserName);
WScript.Echo(objNet.UserDomain);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var path = "\\\\myserver\\my_share\\some_path";
if (fso.FolderExists(path)) {
WScript.Echo("Yes");
} else {
WScript.Echo("No");
}
I get the (expected) output:
MY_COMPUTER
dylan.beattie
MYDOMAIN
Yes
If I run the same code as part of a .ASP page, substituting Response.Write for WScript.Echo I get this output:
MY_COMPUTER
dylan.beattie
MYDOMAIN
No
Now - my understanding is that the WScript.Network object will retrieve the current security credentials of the thread that's actually running the code. If this is correct - then why is the same user, on the same domain, getting different results from CScript.exe vs ASP? If my ASP code is running as dylan.beattie, then why can't I see the network share? And if it's not running as dylan.beattie, why does WScript.Network think it is?
Your problem is clear. In the current implementation you have only impersonation of users and no delegation. I don't want to repeat information already written by Stephen Martin. I only want to add at least three solutions. The classical way of delegation which Stephen Martin suggests is only one way. You can read some more ways here: http://msdn.microsoft.com/en-us/library/ff647404.aspx#paght000023_delegation. I see three practical ways of you solving your problem:
Convert the impersonation token of the user to a token with delegation level of impersonation or to a new primary token. You can do this with respect of DuplicateToken or DuplicateTokenEx.
Use S4U2Self (see http://msdn.microsoft.com/en-us/magazine/cc188757.aspx and http://msdn.microsoft.com/en-us/library/ms998355.aspx) to receive a new token from the old one with respect of one simple .NET statement WindowsIdentity wi = new WindowsIdentity(identity);
You can access another server with respect of one fixed account. It can be a computer account on an account of the application pool of the IIS. It can be another fixed defined account which one will only use for access to the file system.
It is important to know which version of Windows Server you have on the server where IIS is running and which Domain Function Level you have in Active Directory for your Domain (you see this in "Active Directory Domain and Trusts" tool if you select your domain and choose "Raise Domain Functional Level"). It is also interesting to know under which account the application pool of the IIS runs.
The first and the third way will always work. The third way can be bad for your environment and for the current permission in the file system. The second one is very elegant. It allows control of which servers (file server) are accessed from IIS. This way has some restrictions and it needs some work to be done in Active Directory.
Because you use classic ASP, a small scriptable software component must be created to support your implementation.
Which way do you prefer?
UPDATED based on the question from comment: Because you use classic ASP you can not use a Win32 API directly, but you can write a small COM component in VB6 or in .NET which use APIs which you need. As an example you can use code from http://support.microsoft.com/kb/248187/en. But you should do some other things inside. So I explain now which Win32 API can help you to do everything what you need with tokens and impersonation.
First of all a small explanation about impersonation. Everything works very easy. There are always one primary token under which the process runs. To any thread another token (thread token) can be assigned. To do this one needs to have a token of a user hUserToken and call API ImpersonateLoggedOnUser(hUserToken);.
To go back to the original process token (for the current thread only) you can call RevertToSelf() function. The token of user will be received and already impersonated for you by IIS, because you so configured your Web Site. To go back to the original process token you should implement calling of the function RevertToSelf() in your custom COM component. Probably, if you need to do nothing more in the ASP page, it will be enough, but I recommend you be more careful and save current users token in a variable before operation with files. Then you make all operations with file system and at the end reassign users token back to the current thread. You can assign an impersonation token to a thread with respect of SetThreadToken(NULL,hUserToken);. To give (save) current thread token (user token in your case) you can use OpenThreadToken API. It must work.
UPDATED 2: Probably the usage of RevertToSelf() function at the end of one ASP page would be already OK for you. The corresponding C# code can be so:
Create a new Project in C# of the type "Class Library" with the name LoginAdmin. Paste the following code inside
using System;
using System.Runtime.InteropServices;
namespace LoginAdmin {
[InterfaceTypeAttribute (ComInterfaceType.InterfaceIsDual)]
public interface IUserImpersonate {
[DispId(1)]
bool RevertToSelf ();
}
internal static class NativeMethods {
[DllImport ("advapi32.dll", SetLastError = true)]
internal static extern bool RevertToSelf ();
}
[ClassInterface (ClassInterfaceType.AutoDual)]
public class UserImpersonate : IUserImpersonate {
public UserImpersonate () { }
public bool RevertToSelf () {
return NativeMethods.RevertToSelf();
}
}
}
Check in project properties in "Build" part "Register for COM interop". In "Signing" part of the project check Sign the assembly and in "Choose a strong name key file" choose <New...>, then type any filename and password (or check off "protect my key..."). At the end you should modify a line from AssemblyInfo.cs in Properties part of the project:
[assembly: ComVisible (true)]
After compiling this project you get two files, LoginAdmin.dll and LoginAdmin.tlb. The DLL is already registered on the current computer. To register if on the other computer use RegAsm.exe.
To test this COM DLL on a ASP page you can do following
<%# Language="javascript" %>
<html><body>
<% var objNet = Server.CreateObject("WScript.Network");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var objLoginAdmin = Server.CreateObject("LoginAdmin.UserImpersonate");
var isOK = objLoginAdmin.RevertToSelf();
if (isOK)
Response.Write("RevertToSelf return true<br/>");
else
Response.Write("RevertToSelf return false<br/>");
Response.Write("One more time after RevertToSelf()<br/>");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var fso = Server.CreateObject("Scripting.FileSystemObject");
var path = "\\\\mk01\\C\\Oleg";
if (fso.FolderExists(path)) {
Response.Write("Yes");
} else {
Response.Write("No");
}%>
</body></html>
If the account used to run the IIS application pool has access to the corresponding network share, the output will be look like following
Current user: Oleg
Current user's domain: WORKGROUP
RevertToSelf return true
One more time after RevertToSelf()
Current user: DefaultAppPool
Current user's domain: WORKGROUP
Yes
Under impersonation you can only access securable resources on the local computer you cannot access anything over the network.
On Windows when you are running as an impersonated user you are running under what is called a Network token. This token has the user's credentials for local computer access but has no credentials for remote access. So when you access the network share you are actually accessing it as the Anonymous user.
When you are running a process on your desktop (like CScript.exe) then you are running under an Interactive User token. This token has full credentials for both local and remote access, so you are able to access the network share.
In order to access remote resources while impersonating a Windows user you must use Delegation rather then Impersonation. This will involve some changes to your Active directory to allow delegation for the computer and/or the users in your domain. This can be a security risk so it should be reviewed carefully.