Welcome to MSDN Blogs Sign in | Join | Help

Storing Secrets in SharePoint 2007

Once in a while you are challenged with storing secrets like username and passwords for instance for connecting to a web service in another domain or accessing remote resources like a database, a file or through a proxy.

This task can be accomplished in many ways - some more secure that others depending on how you define security. One way is to store the values in web.config. This makes it difficult to deploy and encrypting it can be difficult as you need to encrypt a whole section of the file and you then need to sync keys across the farm to ensure it can be used in a load balanced environment. Another way would just be to hardcode the values, but hardcoded values can easily be discovered with a tool like Reflector (unless it is properly obfuscated - but as well as great obfucators exist - great disassemblers also exist).

Another way to store the secrets are through using the .Net 2.0 class System.Security.SecureString and the built-in Microsoft.SharePoint.Administration.SPPersistedObject.
The problem with a normal System.String in relation to security is that it lives unencrypted on  the Managed Heap, and since String is immutable (it cannot be changed once instantiated) you cannot ‘delete’ a string, but will have to wait for GC  (Garbage Collection) to be performed. Over time, you may end up with many string instances if you perform manipulation with it
and these will be readable from a memory or crash dump. In SharePoint, the persistence of the secret is handled through the Microsoft.SharePoint.Administration.SPEncryptedString class which encapsulates the SecureString and handles the encryption and decryption.
The SPPersistedObject class provides a base class for all administration objects. It serializes all fields marked with the Persisted attribute to XML and writes the XML blob to the configuration database. The SPPersistedObject class contains code to serialize all its members that are base types, other persisted objects, and collections of persisted objects. Configuration data that is stored in persisted objects is automatically made available to every process on every server in the farm.

Below is the class used for encapsulating the secrets. The class could contain any information including non-secret information. To ensure that it is persisted, the fields should be decorated with the [Persisted] attribute. Inspiration for this code comes from the Content Deployment API and the way it stores and connects to remote farms.

using System;

using System.Collections.Generic;

using System.Text;

using System.Security;

using Microsoft.SharePoint.Administration;

using System.Globalization;

 

namespace Microsoft.ADC.SharePoint

{

    internal class SecureCredentials : SPPersistedObject

    {

        // Fields

        [Persisted]

        private SPEncryptedString encryptedPassword;

        [Persisted]

        private string userId;

 

        private SecureString password;

        private bool passwordSet;

       

 

        // Methods

        public SecureCredentials()

        {

        }

 

        private SecureCredentials(string name, SPPersistedObject parent)

            : base(name, parent)

        {

           

        }

 

        internal static SecureCredentials CreateNew()

        {

            return new SecureCredentials(string.Format(CultureInfo.InvariantCulture, "Secure Credential"), SPAdministrationWebApplication.Local.Parent);

        }

 

        internal static SecureCredentials GetInstance(Guid credentialId)

        {

            SecureCredentialsCollection credentials = new SecureCredentialsCollection();

            return credentials.GetValue<SecureCredentials>(credentialId);

        }

 

        public override void Update()

        {

            if (this.passwordSet)

            {

                if (this.encryptedPassword == null)

                {

                    this.encryptedPassword = new SPEncryptedString(null, this);

                }

                if (this.encryptedPassword != null)

                {

                    this.encryptedPassword.UpdateSecureStringValue(this.password);

                }

            }

            base.Update();

        }

 

        // Properties

        internal SecureString Password

        {

            get

            {

                if (this.passwordSet)

                {

                    return this.password;

                }

                while (this.encryptedPassword == null)

                {

                    return null;

                }

                return this.encryptedPassword.SecureStringValue;

            }

            set

            {

                this.password = value;

                this.passwordSet = true;

            }

        }

 

        internal string UserId

        {

            get

            {

                return this.userId;

            }

            set

            {

                this.userId = value;

            }

        }

    }

 

    internal class SecureCredentialsCollection : SPPersistedChildCollection<SecureCredentials>

    {

        // Methods

        public SecureCredentialsCollection()

            : base((SPWebService)SPAdministrationWebApplication.Local.Parent)

        {

        }

    }

}

Now both the two classes and most of the method and accessors have an internal accessibility level. This is fully intentional as the secure values otherwise could easily be compromised. When storing a value into the Config database using SPPersistedObject, the value includes the fully qualified assembly name of the persisted object. This means that in order to retrieve the object again, you would have to have the exact same object type and if the accessibility level where public, then anyone could write a simple console app and extract the secrets. When marked as internal only the classes within the same assembly can access the persisted objects class and use it. This means that you will have to have the classes for both storing the secret and retrieving and using the secret within the same assembly. Further, if someone could get access to the private key, then it would also be possible to create a new class and match the signature of the persisted class and retrieve the value. Of course the key for signing assemblies should be treated as a secret itself inside an organization and not be available to the public.

When you are persisting an object to SharePoint, you will get back a Guid as a reference. (The Guid is simply referring to the identity column in the database). This Guid needs to be stored somewhere in order to retrieve the value again and a good place to do this could be a (hidden) List in the Central Administration site. All you need to store is the Guid and some identifier to find the Guid from within the application. To set the secret follow this pattern:

SecureCredentials credential = SecureCredentials.CreateNew();

credential.UserId = @"Domain\User";

 

SecureString secureString = new SecureString();

//TODO: Set secureString

 

credential.Password = secureString;

credential.Update();

                       

Guid credentialId = credential.Id;

//TODO: Store the Id of the persisted object

To retrieve the value again, follow this pattern:

//TODO: Get credentialId from List or other location

Guid credentialId = new Guid("");

SecureCredentialsCollection scc = new SecureCredentialsCollection();

SecureCredentials sc = scc.GetValue<SecureCredentials>(credentialId);

 

string userId = sc.UserId;

//If the string is needed in clear text, it can be converted, but be aware that

//it now exists on the Managed Heap

string password = Marshal.PtrToStringBSTR(Marshal.SecureStringToBSTR(sc.Password));

How secure is it then

Well, nothing is 100% secure - security is an illusion and no matter how much you encrypt, you still need to have some sort of key or known secret to unlock the stored secret. Biometrics of course is pretty strong and something you have combined with something you know like a smartcard and a password is also pretty secure unless you lose your smartcard and have the password on a Post-It on the card. Security is of course also a matter of who you are trying to protect your secret from.

If a hacker where to compromise a server in the Farm, in order to retrieve the secret, he or she would have to disassemble the assembly holding the encrypted object, get a hold of the Private Key used to sign the assembly, locate the credentialId, and compile a new class to retrieve the values. Further, this would have to be executed on the server and I could think of many other thinks a hacker would do if the person would get access to a server.

Insiders are of course also a thread. In order to get a hold of the secret, they would need the Private Key for signing a new assembly. Alternatively they could also have built in a secret method in the get or set method for having the values displayed. Proper processes around development like code review and delay signing should deem up of for such attempts.

Since SecureString is only 100% secure if it is not transformed to a regular String, then there is also a potential risk during the entering of the secret and also retrieval for example if you would use it to authenticate against a Basic Authenticated web service. Some protection could come from protecting the traffic with SSL, but it would still exist in a short period of time on the Heap, and if you where to take a memory dump during this period of time, the password would be stored here. This is good to know if you ever send dumps for troubleshooting to for instance Microsoft.

Posted by madsd | 1 Comments
Filed under:

New to SharePoint Search?

Well, it is your lucky day - once again a set of great videos to get you started, was released.

To watch the videos, just follow this link: http://technet.microsoft.com/en-us/library/cc505994.aspx

/Mads

Posted by madsd | 1 Comments

New to SharePoint administration?

Then use these great videoes and whitepapers to get you started.

Topics include

  • Overview: Office SharePoint Server server farm architecture
  • Overview: Configuring server farms

  • Securing server farms

  • Configuring performance options

  • Backup, Restore, High Availability and Disaster Recovery

  • Operations and management

  • Capacity planning

  • Search architecture and configuration

http://technet2.microsoft.com/Office/en-us/library/f27b1c10-aa0f-421b-8c8f-0ed52be863d71033.mspx?mfr=true

Posted by madsd | 1 Comments
Filed under:

Adding bindingredirects through SPWebConfigModification

SPWebConfigModification works very well when you want to add or change something in web.config on your MOSS site. This is done in a consistent way and you are ensured that it is applied to both extended web application and also when bringing in a new frontend server in the farm. I will not go into details of how to use SPWebConfigModifications as there at numerous blog entries out there describing this.

However when you want to add an assemblybinding to your web.config file to handle versioning of your assemblies you might run into a problem as the runtime element of the web.config file is placed in another xml-namespace.

Before you go out and do something drastic like adding it manual, it is possible to do it through SPWebConfigModifications, but it requires some special XPath expressions.

Let’s say you wanted to add this entry:

<dependentAssembly>

  <assemblyIdentity name="CustomerAssembly" publicKeyToken="71e9bce111e9429c" culture="neutral" />

  <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />

</dependentAssembly>

You would then need these lines to add the above

SPWebConfigModification mod = new SPWebConfigModification();

 

mod.Path = "configuration/runtime/*[local-name()='assemblyBinding' and namespace-uri()='urn:schemas-microsoft-com:asm.v1']"; // filtering using the tag local name and namespace-uri

mod.Name = "*[local-name()='dependentAssembly'][*/@name='CustomerAssembly'][*/@publicKeyToken='71e9bce111e9429c'][*/@culture='neutral']";

mod.Value = "<dependentAssembly><assemblyIdentity name='CustomerAssembly' publicKeyToken='71e9bce111e9429c' culture='neutral' /><bindingRedirect oldVersion='1.0.0.0' newVersion='2.0.0.0' /></dependentAssembly>";

 

mod.Owner = "My Owner";

mod.Sequence = 0;

mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

                                   

webApp.WebConfigModifications.Add(mod);            

webApp.Update();

           

SPWebService.ContentService.ApplyWebConfigModifications();

This will also make it possible to remove the configuration again. Happy coding!

Posted by madsd | 1 Comments
Filed under:

SharePoint is not the holy grail

All of you SharePoint developers out there can read this and nod.

 Sales force and Management - please read and understand this excellent post by Patrick Tisseghem :)

Posted by madsd | 1 Comments
Filed under:

Versioning WebParts and SharePoint Solution Deployment caveats

The scenario is the following: Version one of a WebPart is developed and deployed to the SharePoint farm using Solution Deployment. The solution Manifest.xml contains the assembly reference to deploy the assembly to GlobalAssemblyCache and further more the SafeControl definition.

When deploying the solution, the assembly will be placed in the GAC and a SafeControl entry will be written to the web.config files of the selected WebApplications. You are now ready to create the WebPart definition and start using the little wonder.

The way SharePoint now loads your WebPart (very simplified) is like this. SharePoint reads the WebPart definition on the page. It the checks if this WebPart is registered in the SafeControl entries for the page. If this is true it loads the assembly and displays the page.

WebPart v1

After a while you realize that you made something (by design of course) that the users are not happy with and you need to change the code. During this upgrade you decide to change the version number of the assembly to 2.0.0.0. As the smart clever developer you are, you remember to include a publisher policy file that points request from the old version to version 2. The new WebPart is repackaged in the solution together with the compiled policy file and you upgrade it on the server, but when the users access the site with the updated WebPart, they will receive an error message saying that a WebPart on the page is not registered as safe.

The reason for this is that during the upgrade of the solution, the SafeControl entry was changed to v2.0.0.0 to reflect the new assembly version. When the user opens the page, the WebPart definition on the page still point on the first version and when SharePoint checks whether this WebPart is defined in the SafeControl entries, it will return false and thus SharePoint will throw the exception mentioned previously.

Ahaaa, you might think, then I can just add SafeControl entries for both version one and two in the Manifest file, but unfortunately the solution deploy job will override this and only deploy the latest version.

So what can you do about this?

1. Do not version your WebPart assemblies - use AssemblyFileVersion instead.

2. Create a Feature, that contains a Callout which uses the SPWebConfigModification object to add the SafeControl entry for the old assembly version. You can find examples on using SPWebConfigModification by searching for it on the world wide web :o)

When both SafeControl entries are added to the web.config then when the users access the page for the first time, the site will try to load version 1 and after clearing through the SafeControl layer it will hit the policy file and load version 2. Furthermore the WebPart definition on the page will be updated to version 2.

Posted by madsd | 1 Comments
Filed under:

Load test and performance monitoring across network boundaries

Visual Studio Team Edition for Software Testers has some excellent tools for performing load test. One of the nice features is the ability to measure performance counters on a remote server while you are performing load test and have this information correlated with the request load measurements from the load test.

This works fine (given you have the appropriate permissions) in an internal LAN network in a single domain. But if your Web Application or Web Service is placed in a DMZ environment then you will have a security issue when trying to read the performance counters on a remote DMZ server.

Facing this challenge is contacted my former colleague in TDC, Peter Eppler, whom I see as an Infrastructure guru and thought he would probably have a solution or a workaround for this. And so he had. After a few mail specifying the problem he came up with a command line statement to solve the problem by initiating an Inter-Process Communication (IPC) connection with Server Message Block (SMB).

Start a command prompt and type in the following: “net use \\[DmzServerName]\IPC$ /user:[DmzServerName]\[UserWithPermissionsToReadPerfCounterInDmz]

When prompted for password you will enter this and now there will be a connection established and you will be able to connect to the performance counter both through perfmon and with the Load Test tool. (This should also work if you need to read a remote EventLog!)

Posted by madsd | 0 Comments
Filed under:

Dual boot on Windows Vista

If you want to have dual boot on your pc, for instance if you want to have Windows 2003 R2 installed side by side to run demos and requiring server products that do not run well in a VPC, then there is a few thing you need to be aware of.

First of all, Windows Vista has introduces a new bootloader called Boot Configuration Data (BCD). You can read more about BCD here: http://msdn2.microsoft.com/en-us/library/aa362639.aspx. This also means that the old boot.ini is not used anymore.

If you started by installing the "legacy" OS you should have no problems but if you installed Vista first and then your “legacy” OS after worth, then the “legacy” OS will overrule the BCD and you will not be able to boot into Vista. Adding Vista to boot.ini won’t help either as Vista cannot load from boot.ini.

So what you need do if you have brought yourselves into this mess is to boot up in the “legacy” OS and insert the Vista install DVD. Open a command prompt and browse to the boot folder of the install DVD. Here you will call the command “bootsect /NT60 ALL”. This will recreate the BCD and the next time you boot Vista will load!

But what about my “legacy” OS you might think! Well, here you need to modify the BDC and to do that I will recommend a tool called VistaBootPRO 3.1 http://www.vistabootpro.org/. This enables you to edit the BCD through a nice GUI and all you need to do is add an entry and be sure to point the boot drive to where Vista boot from and not the “legacy” OS.

And just for the record: I have intentionally quoted legacy as I don’t find neither XP nor 2003 R2 to be legacy OS…

Posted by madsd | 1 Comments

Logging to the Windows EventLog without administrative privileges in .Net

This was a question I had from one of the customers I work for. My first reply was to use the static method on the built in .Net Framework class System.Diagnostics.EventLog.WriteEntry. This is easy and straight forward, but it has one disadvantage: If you are not an administrator the first time you write to the EventLog you will get a permission error. This is because the method attempts to register the source that you use to write the event in the Registry by writing an an entry here HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application. This will throw an Exception like System.Security.SecurityException: Requested registry access is not allowed.

 

One solution is of course to implement an installer class or to manually create the EventSource but both these actions require admin rights. Se http://support.microsoft.com/default.aspx?scid=kb;EN-US;329291 for more details.

 

But what if don’t want to register eventsources or is not allowed to by strict sysadmins (Don’t blame them – it is their job to be pissed every time you come with new fancy application to shake their system). Well, one solution is to reuse one of the previously installed EventSources like COM, .Net Runtime or Office – but this can get confusing for Operation Management systems like MOM. (Another great way to get the administrators mad…)

 

Another solution is to us an unmanaged Win32 API call. First you need to register some dlls and add a couple of consts

 

[DllImport("advapi32.dll")]
private static extern IntPtr RegisterEventSource(string lpUNCServerName, string lpSourceName);

[DllImport("advapi32.dll")]
private static extern bool DeregisterEventSource(IntPtr hEventLog);

[DllImport("advapi32.dll", EntryPoint = "ReportEventW", CharSet = CharSet.Unicode)]

private static extern bool ReportEvent(

            IntPtr hEventLog,

            ushort wType,

            ushort wCategory,

            int dwEventID,

            IntPtr lpUserSid,

            ushort wNumStrings,

            uint dwDataSize,

            string[] lpStrings,

            byte[] lpRawData

            );

public const ushort EVENTLOG_INFORMATION_TYPE = 0x0004;

public const ushort EVENTLOG_WARNING_TYPE = 0x0002;

public const ushort EVENTLOG_ERROR_TYPE = 0x0001;

 

Then you can wrap the necessary method calls in a static method

 

public static void WriteEventLogTextEntryApi(string text, ushort logType, int logEventId, byte[] rawData)

{

//Temporary registry of eventsource

IntPtr hEventLog = RegisterEventSource(null, "EventSourceName");

uint dataSize = (uint)(rawData != null ? rawData.Length : 0);

 

//Write event to eventlog

ReportEvent(hEventLog, logType, 0, logEventId, IntPtr.Zero, 1, dataSize, new string[] { text }, rawData);

 

//Remove temporary registration

DeregisterEventSource(hEventLog);

}  

 

And finally you log an event by calling the static method

 

WriteEventLogTextEntryApi("An error occured", EVENTLOG_ERROR_TYPE, 1, null);

 

The only drawbacks of this solution are of course the call to unmanaged APIs and then the fact that when you view the event in the Eventlog, this text will always be display before the errortext because the Eventviewer cannot find the eventsource.

 

The description for Event ID ( 1 ) in Source ( EventSourceName ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following information is part of the event

Posted by madsd | 1 Comments

Hello Seattle, TechReady3 and Excel Services

This week I am attending the Microsoft TechReady3 convention. Is it an internal convention like PDC or TechEd. We arrived in Seattle yesterday an the first thing that we met when we got out of the plane was a dazzeling heat of about 34 degrees celsius. Good thing I brought my shorts :o).

Due to the 9 hour time difference, I woke up wide awake at 4 a.m. and what else where there to do than watching a couple of webcasts until the alarm went off at seven.

One of them was about Excel Services in the Office Server 2007 suite, and this is an exiting new product that allows you to present Excel sheets, single Pivot tables or Graphs on at webpage. Also it allows you to have business logic and calculations done by the Business Analysist and have this logic built in to Windows and Web applications through Web Services. This way, the developer can focus on the application functionality and not having to "translate" Excel logic into C# code...

One thing you do have to have in mind when looking at the new features is, that it requires Excel 2007 and you have to save it in the new .xlsx format. Furthermore it does not support VBA and Macros as it is all handled though managed code.

I will be back during the week with more updates from all the interesting sessions I will be attending.

Posted by madsd | 1 Comments

Welcome to my blog and my first blog entry

Well, it's time to write my first blog entry. Meanwhile, my computer is transforming in the background from an ugly workgroup server to a beautiful domain controller. Preparing to host a MOSS 2007 installation and give me countless hours of entertainment in the future to come.

The rest of my blog will probably not be as poetic as this one. But my intention is that it will not be strictly professional and that there will be room for funny cross links, strange episodes and other non work related topics.

And as english is my second language please have me excused for occationally misspellings and typos. But do let me know if something is totally bogus.

My first cross link will be to one of my former colleagues, who also work with SharePoint 2007. Currently his blog contains notes from a traning session he is attending in London. (Unfortuneately it is written in danish, but for those 5 mill. who can read it, there is a lot of usefull stuff there): http://bisbjerg.wordpress.com

Posted by madsd | 0 Comments
 
Page view tracker