New Sitecore 8.2 & Sitecore 9 Security Patch

I guess I’m on a security hardening binge for 2019, since I’ll share a hot-off-the-presses security hardening measure from Sitecore today. I don’t want to say too much about the vulnerability, but this article explains in general terms and applying it to all Sitecore server roles for version 8.2 through any current 9 releases is emphasized as the best practice. I’d do it at the next opportunity.

I created a gist with the PowerShell necessary to apply this patch, just update line #8 with the path to your Sitecore website:

Our team has internal automation taking the above a bit further and using another layer of abstraction, and that’s secret Rackspace sauce I won’t share publicly,  but the snippet above should have your environment patched in just a few seconds.

One of the key elements to the patch involves an update to the /sitecore/admin/logs.aspx page which, if you dig into it, reveals a grip-load of additional C# validation logic and other stuff going on . . .

secpatch

There’s a lot to unpack in there if you’re curious, but suffice it to say that Sitecore’s keeping all your bases covered and isn’t trusting user input (using a broad interpretation of that principle).

Advertisements

Sitecore Commerce security hardening note

Let’s start the New Year off with a fun Sitecore Commerce note. Using the latest Sitecore Commerce available today, that is running Sitecore 9.0 update-2 with Sitecore Commerce update-3 (you have to cross-reference https://dev.sitecore.net/Downloads/Sitecore_Commerce.aspx and https://kb.sitecore.net/articles/804595 to really sort this out), we’re applying routine security hardening.

Now that Sitecore is truly built on a hybrid of “plain” .Net and .Net Core, this security hardening effort is more nuanced.

Sitecore is still updating their documentation for the Sitecore 9 space and one can end up at dead-ends like https://doc.sitecore.com/developers/90/platform-administration-and-architecture/en/security-guide-251908.html that leads you over to the .Net Framework documentation when there are better notes with 100% relevancy to Sitecore elsewhere on the Sitecore site. I persevered and eventually found Sitecore’s updated information like this on the hash algorithm https://doc.sitecore.com/developers/90/platform-administration-and-architecture/en/change-the-hash-algorithm-for-password-encryption.html. Still, this documentation overlooks the .Net Core details and given that this Sitecore Commerce project we’re working on will use the latest and greatest, we had to do our own research.

Fortunately, we have some history with this having published https://developer.rackspace.com/blog/Updated-Security-Hardening-For-Sitecore-8.2 or earlier versions going back several years. The PowerShell we’ve used for ages to automate this work, however, wasn’t going to cut it with this new Commerce and .Net Core dimension:

snippet

Instead, we need to do something like this to update the JSON configuration for the Sitecore Identity Server. While you could get fancy and parse the JSON, I used a more direct replace approach to knock this out quickly:

$siteNamePrompt = Read-Host "enter Identity Server website name"
$site = get-website -name $siteNamePrompt
$appSettingsPath = "{0}\wwwroot\appsettings.json" -f $site.physicalPath
Get-Content $appSettingsPath).replace("""PasswordHashAlgorithm"":""SHA1""},", """PasswordHashAlgorithm"":""SHA512""},") | Set-Content $appSettingsPath

The end result is  that SitecoreIdentityServer\wwwroot\appsettings.json file needs an updated PasswordHashAlgorithm value:

        “IDServerCertificateStoreLocation”: “LocalMachine”,
“IDServerCertificateStoreName”: “My”,
        “PasswordHashAlgorithm”: “SHA512”
}

Given the distributed nature of Sitecore 9 with Commerce, I think a discrete change like this just for the IdentityServer doesn’t warrant a lot of effort to integrate into the bigger security hardening Powershell script we use. It may be worthwhile to just update SIF at this point instead of applying security hardening after the Sitecore installation is complete. We’re also talking about SIF extension modules to run this type of logic after SIF is complete. For now, I’ll probably just keep this note handy for the foreseeable future and see whether Sitecore integrates the security hardening guidance directly into SIF in a future release (hint hint!) — or, over time we may collect a set of these best practice adjustments that deserves more effort to automate into a scripted deployment. For now, I think I’ve taken it as far as it deserves.

Encrypting Sitecore connection strings for Sitecore Commerce, Azure SQL, and beyond

There’s been a lot of Sitecore Commerce on my plate this Summer, and sadly one limitation of using that product for some customers is the requirement for SQL Server authentication instead of Active Directory and Windows Auth; I won’t get into why they need SQL auth at this point, but trust that in many use-cases this is a necessity.

In an effort to always deliver a secured platform for customers, at Rackspace we encrypt the App_Config/connectionStrings.config file to avoid having plaintext usernames and passwords on disk.    This is a link to our Rackspace GitHub “gist” performing such encryption with the ASP.Net tool aspnet_regiis.exe.  The logic is also there to un-encrypt, in case that’s necessary.

Encryption success
You can update the $configLocation variable at the top of the script to point to where your Sitecore installation is located; you then run this script using PowerShell, and you’ll get an output like this.

Once you’ve run the script, your connectionStrings.config file will resemble this:

Before you get too excited, for Sitecore Commerce in the current incarnation, there are several other plaintext passwords on disk in the \CommerceAuthoring\wwwroot\data\Environments and related .json files for both SQL and Sitecore.  The PowerShell I’ve shared doesn’t address those areas.  The Sitecore Commerce documentation does a good job of cataloging where to find these references, at least, but this still leaves a lot to be desired in terms of security.

I’m not going to go too far down this path, since I mostly wanted to post the PowerShell we use to automate SQL Server connection string encryption.  This technique can be useful for a variety of projects, not just for Sitecore Commerce — although this is the use case we’re repeatedly seeing right now.  If I have time, I’ll share some other Sitecore Commerce tips around Azure SQL friendly deployments (Sitecore’s documentation is a decent start, but lacking in some respects).

Here’s the script to encrypt/decrypt those Sitecore connectionStrings.config file:

The Sitecore Pie: strategic slicing for better implementations

The Sitecore Pie

pie
At some point I want to catalog the full set of features a Sitecore Content Delivery and Content Management server can manage in a project.  My goal would be to identify all the elements that can be split apart into independent services.  This post is not a comprehensive list of those features, but serves to introduce the concept.

Think of Sitecore as a big blueberry pie that can be sliced into constituent parts.  Some Sitecore sites can really benefit from slicing the pie into small pieces and letting dedicated servers or services manage that specific aspect of the pie.  Too often, companies don’t strategize around how much different work their Sitecore solution is doing.

An example will help communicate my point: consider IIS and how it serves as the execution context for Sitecore.   Many implementations will run logic for the following through the same IIS server that is handling the Sitecore request for rendering a web page.  These are all slices of the Sitecore pie for a Sitecore Content Delivery server:

  1. URL redirection through Sitecore
  2. Securing HTTP traffic with SSL
  3. Image resizing for requests using low-bandwidth or alternative devices
  4. Serving static assets like CSS, JS, graphics, etc
  5. Search indexing and query processing (if one is using Lucene)

If you wanted to cast a broader net, you could include HTTP Session state for when InProc mode is chosen, Geo-IP look-ups for certain CD servers, and others to this list of pie slices.  Remember, I didn’t claim this was an exhaustive list.  The point is: IIS is enlisted in all this other work besides processing content into HTML output for Sitecore website visitors.

Given our specific pie slices above, one could employ the following alternatives to relieve IIS of the processing:

  1. URL Redirection at the load balancer level can be more performant than having Sitecore process redirects
  2. Apply SSL between the load balancer and the public internet, but not between the IIS nodes behind your load balancer — caled “SSL Offloading” or “SSL Termination”
  3. There are services like Akamai that fold in dynamic image processing as part of their suite of products
  4. Serving static assets from a CDN is common practice for Sitecore
  5. Coveo for Sitecore is an alternative search provider that can take a lot of customer-facing search aspects and shift it to dedicated search servers or even Coveo’s Cloud.  One can go even further with Solr for Sitecore or still other search tiers if you’re really adventurous

My point is, just like how we hear a lot this election season about “let Candidate X be Candidate X” — we can let Sitecore be Sitecore and allow it to focus on rendering content created and edited by content authors and presenting it as HTML responses.  That’s what Sitecore is extremely valuable for.

Enter the Cache

I’ve engaged with a lot of Sitecore implementations who were examining their Sitecore pie and determining what slices belong where . . . and frequently we’d make the observation that the caching layer of Sitecore was tightly coupled with the rest of the Sitecore system and caching wasn’t a good candidate for slicing off.  There wasn’t a slick provider model for Sitecore caches, and while certain caches could be partially moved to other servers, it wasn’t clean, complete, or convenient.

That all changed officially with the initial release of Sitecore 8.2 last month.  Now there is a Sitecore.Caching.DefaultCacheManager class, a Sitecore.Caching.ICache interface, and other key extension points as part of the standard Sitecore API.  One can genuinely add the Sitecore cache to the list of pie slices one can consider for off-loading.

In my next post, I will explore using this new API to use Redis as the cache provider for Sitecore instead of the standard in-memory IIS cache.

Unified Security Hardening Script for Sitecore

I wanted to share here some good work we shared on the Rackspace developer blog last week about Sitecore security hardening.

If one is pursuing security hardening for Sitecore, the whole piece is worth reading, but it’s particularly targeting those looking for repeatable, scripted processes to apply the website configs and IIS changes that secure Sitecore to a general standard.

We get a ton of use out of this unified PowerShell script that applyies the standards from the Sitecore Security Hardening Guide: unified PowerShell script for Sitecore security hardening.

We’ve also posted to GitHub the necessary .config patches for the security hardening:

The “unified” PowerShell script follows a pattern of downloading the .config and copies it to proper directory in the Sitecore installation:

# this .config is what we're applying: https://gist.github.com/grant-killian/b64aa6cabd18e9b0097257ee4a2dc614
$downLoadURI = "https://gist.githubusercontent.com/grant-killian/b64aa6cabd18e9b0097257ee4a2dc614/raw"
$downLoadPath = "C:\localStaging\Rackspace.SecurityHardening.Step5.IncreaseLoginSecurity.config"
Invoke-WebRequest -Uri $downLoadURI -OutFile $downLoadPath
Copy-Item -Path $downLoadPath -Destination $rackspaceInclude #we use a "Z.Rackspace" directory under /app_config/include

This PowerShell isn’t the end of the process of securing a Sitecore implementation, but more just a start.  Sitecore publishes these recommendations as part of their best practices, and what we’ve done at Rackspace it taken those recommendations and scripted them for ease of deployment across a spectrum of environments.  We’ve made slight adjustments here and there to suit our perspective and experience, as there is rarely a one size fits all solution to this kind of work.

Securing the Sitecore Login Surface Area

We’re standardizing the security hardening routines across several Sitecore customers and it’s curious that Sitecore’s documentation on improving login security doesn’t cover the Sitecore Login.RememberLastLoggedInUserName setting.

For reference, in the sitecore.config file this setting is provided as follows:

<!–  REMEMBER LAST LOGGED IN USER NAME
Specifies whether Sitecore will remember the last logged in user name on the login page (stored encrypted in a cookie).
If you set this to false, the user name field on the login page will always be blank.
Default: true
–>
<setting name=”Login.RememberLastLoggedInUserName” value=”true”/>

Kevin Obee has a succinct Sitecore config patch for this, we combine this with some other settings to arrive at the following unified SecurityHardening.ImproveLoginSecurity.config patch at Rackspace:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
      <setting name="Login.DisableAutoComplete">
        <patch:attribute name="value">true</patch:attribute>
      </setting>
       <setting name="Login.DisableRememberMe">
        <patch:attribute name="value">true</patch:attribute>
      </setting>   
      <!-- not officially part of Sitecore's security hardening measures,
 but still a good security practice -->
       <setting name="Login.RememberLastLoggedInUserName">
        <patch:attribute name="value">false</patch:attribute>
      </setting>   
    </settings>
  </sitecore>

I should also point out, the first part of Sitecore’s documentation on hardening the login surface for Sitecore is about enforcing SSL.  At Rackspace, we don’t do this at the Sitecore application layer (that’s actually the last step in the chain of request processing for a site visit, so that’s the slowest spot to do it and it’s fairly brittle).  Instead, we enforce this earlier in the chain at the F5 layer (or which ever load balancer a customer is running with).  There are other steps to take like in conjunction with this, such as IP whitelisting for access etc.  Again, the load balancer is the right place to do this sort of work.

More on this soon . . . our team is compiling a unified set of guidance around applying a the basic set of Sitecore’s security measures — and then some :).

How Sitecore Phones Home (maybe)

A question came up today around the office: does Sitecore monitor their installation base via a phone-home mechanism of some kind?  It’s relevant in a number of ways.   One way being as we work on cloud installations and elastic models for Sitecore, what data might Sitecore have that we could leverage for tracking licensing compliance, utilization, etc.

There may be other ways Sitecore tracks their server activities, but I used Reflector on the Sitecore.sitecore.login namespace to find one very likely place where Sitecore implements a call-back to the mothership.  Here goes . . .

Using a clean Sitecore 8.1 rev 151207 installation, in the source for the sitecore/login/default.aspx page is the following mark-up.  Note the “display:none;” on line 1 that hides this div from view:

   1:      <div id="licenseOptions" style="display: none;">
   2:  <%--            <h2 class="form-signin-heading">License and browser information</h2>--%>
   3:              <div class="license-info-wrap">
   4:                <ul>
   5:                  <li>System information</li>
   6:                  <li>License holder <%# License.Licensee %></li>
   7:                  <li>License ID <%# License.LicenseID %></li>
   8:                  <li>Sitecore version <%# About.VersionInformation() %></li>
   9:                </ul>
  10:  
  11:                <iframe id="StartPage" runat="server" allowtransparency="true" frameborder="0" scrolling="auto"
  12:                      marginheight="0" marginwidth="0" style="display: none; height: 105px;"></iframe>
  13:  
  14:              </div>
  15:              <div class="login-link-wrap">
  16:                <a href="javascript:;" id="licenseOptionsBack" class="login-link">&lt; Back</a>
  17:              </div>
  18:  
  19:            </div>

If we remove the “display:none” and load the page, some interesting details about the environment are revealed.  On my local machine it appears like this:

displayed

That’s not particularly interesting, but if you return to the mark-up and examine the IFrame defined on line #11 named StartPage with the runat=server attribute, you might turn your attention to what’s going on server-side when this default.aspx page loads.

Reflector, or any decompiler, will show that Sitecore.sitecore.login.Default contains an OnInit method with various checks for authentication, databinding, and so on.  I’ll omit most of that method except for the one call that’s of interest here, as we look for a way our local Sitecore system could communicate back to Sitecore the company.

protected override void OnInit(EventArgs e)
{

this.RenderSdnInfoPage();

}

Diving into the RenderSdnInfoPage method, we have this:

   1:  private void RenderSdnInfoPage()
   2:  {
   3:      this.StartPage.Attributes["src"] = new UrlString(Settings.Login.SitecoreUrl) {
   4:          ["id"] = Sitecore.SecurityModel.License.License.LicenseID,
   5:          ["host"] = WebUtil.GetHostName(),
   6:          ["licensee"] = Sitecore.SecurityModel.License.License.Licensee,
   7:          ["iisname"] = WebUtil.GetIISName(),
   8:          ["st"] = WebUtil.GetCookieValue("sitecore_starttab", string.Empty),
   9:          ["sc_lang"] = Context.Language.Name,
  10:          ["v"] = About.GetVersionNumber(true)
  11:      }.ToString();
  12:      this.StartPage.Attributes["onload"] = "javascript:this.style.display='block'";
  13:  }

And now we’re in business!  Sitecore loads up various pieces of data from the local running Sitecore instance and appends it to the end of our URL that we inject into the src attribute of the IFrame.  That src attribute in a default Sitecore installation starts with http://sdn.sitecore.net/startpage.aspx (defined as the Login.SitecoreUrl configuration setting) and the rest of the URL is information communicated to Sitecore about this local instance.

For example, using my Firefox HTML Inspector, my generated IFrame source was as follows (know that I have altered identifying information about my license):

<i frame src=”http://sdn.sitecore.net/startpage.aspx?id=20150821010030&host=sc81rev151207&licensee=Not%20Really%20Sharing&iisname=b86449a111164a2cd9c37d771c094dce&st&sc_lang=en&v=8.1.151207” id=”StartPage” allowtransparency=”true” scrolling=”auto” marginheight=”0″ marginwidth=”0″ style=”display: block; height: 105px;” onload=”javascript:this.style.display=’block'” frameborder=”0″></i frame>

I think it’s a pretty safe bet that startpage.aspx on the sdn.Sitecore.net site has some logic running to catalog the querystring parameters and passively monitor what’s going on in the wild.