 <?xml-stylesheet type="text/css" href="https://www.esdm.co.uk/Data/style/rss1.css" ?> <?xml-stylesheet type="text/xsl" href="https://www.esdm.co.uk/Data/style/rss1.xsl" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
  <channel>
    <title>The knowledge base blog</title>
    <link>https://www.esdm.co.uk/knowledge</link>
    <description />
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>mojoPortal Blog Module</generator>
    <language>en-GB</language>
    <ttl>120</ttl>
    <atom:link href="https://www.esdm.co.uk/Blog/RSS.aspx?p=138~108~185" rel="self" type="application/rss+xml" />
    <itunes:owner />
    <itunes:explicit>no</itunes:explicit>
    <item>
      <title>Using cloudscribe with Identity Server 4 with a SQL Server store on .Net Core 2 MVC securing APIs</title>
      <description><![CDATA[<p>Well that’s a bit of a mouthful of a title – but it does describe what I was trying to do, and I didn’t find it easy. It’s not that complicated when you know how, but there are an awful lot of options and it wasn’t clear (to me at least) which ones to pick, so I hope this saves others some time.</p>

<h4>Aim</h4>

<p>I wanted a <a href="https://www.cloudscribe.com/">cloudscribe</a> site running allowing users to be able to logon as usual using cookies, but I also wanted to have an API that could be secured for clients external to the site without having to use the cookie login and I wanted to use Identity Server 4 with a SQL data store.</p>

<h3>Starting setup</h3>

<h4>Server</h4>

<p>I used Joe Audette's excellent cloudscribe Visual Studio template <a href="https://www.cloudscribe.com/blog/2017/09/11/announcing-cloudscribe-project-templates">https://www.cloudscribe.com/blog/2017/09/11/announcing-cloudscribe-project-templates</a> and selected use MSSQL Server and the Include Identity Server integration option. Also selecting the 2 options in the “Expert Zone” gave me an example API to test with.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image002_8.jpg"><img alt="clip_image002" border="0" height="244" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image002_thumb_7.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image002" width="227" /></a></p>

<p>This gave me the basic website and was the one I wanted to add the secured API into. The VS project has a weather forecast API as an example.</p>

<h4>Client</h4>

<p>I then setup a separate MVC project using a basic template to act as the client application. This was all done using Visual Studio 2017 and .Net Core 2.</p>

<h3>Server application</h3>

<p>By default, the weather forecast API is accessible to all users. Try: http://localhost:35668/api/SampleData/WeatherForecasts</p>

<p>You can secure this by adding the [Authorize] statement to the API on the SampleDataController.cs page e.g.</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
[Authorize]
[HttpGet(<span style="color: rgb(0, 96, 128);">"[action]"</span>)]
<span style="color: rgb(0, 0, 255);">public</span> IEnumerable&lt;WeatherForecast&gt; WeatherForecasts()
{
    var rng = <span style="color: rgb(0, 0, 255);">new</span> Random();</pre>
</div>

<p>but you will find this presents the standard cloudscribe logon screen to access it – not exactly what’s wanted for an API.</p>

<p>In order to solve this we need to use JWT authorisation alongside the standard cookie authentication, but tell the API to only secure using the JWT authorisation . This is done by filtering the authentication scheme used by the Authorize statement as below (you will probably have to add the following assemblies to your code)</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authorization;
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;</pre>
</div>

<p>and then add the filter to the authorize statement.</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet(<span style="color: rgb(0, 96, 128);">"[action]"</span>)]
<span style="color: rgb(0, 0, 255);">public</span> IEnumerable&lt;WeatherForecast&gt; WeatherForecasts()
{
    var rng = <span style="color: rgb(0, 0, 255);">new</span> Random();</pre>
</div>

<p>We have now told the API to authenticate using JWTBearer but we haven’t yet added JWT authentication to our applications pipeline. So in the startup.cs page we need to add in some assemblies:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;</pre>
</div>

<p>and then add the JWT service into the ConfigureServices method. (I added the statement below just above services.AddCors(options =&gt;)</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
services.AddAuthentication(options =&gt;
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})

.AddJwtBearer(options =&gt;
{
    options.Authority = <span style="color: rgb(0, 96, 128);">"http://localhost:35668"</span>; <span style="color: rgb(0, 128, 0);">//No trailing /</span>
    options.Audience = <span style="color: rgb(0, 96, 128);">"api2"</span>; <span style="color: rgb(0, 128, 0);">//Name of api</span>
    options.RequireHttpsMetadata = <span style="color: rgb(0, 0, 255);">false</span>;

});</pre>
</div>

<p>Where:</p>

<p>.Authority is the address of the website with the api (note no trailing slash)</p>

<p>.Audience is the name you have given to the api in the Identity server 4 security setup (see more details below)</p>

<p>And then we need to tell our pipeline to use Authentication. So add the app.UseAuthentication() into the end of the ConfigureServices method just above the UseMVC call</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
app.UseAuthentication();
UseMvc(app, multiTenantOptions.Mode == cloudscribe.Core.Models.MultiTenantMode.FolderName);</pre>
</div>

<p>Now if you try and access the api -&nbsp;http://localhost:35668/api/SampleData/WeatherForecasts&nbsp;- you should get an unauthorised message - even if you are logged onto the cloudscribe site using cookie authentication.</p>

<h3>Identity Server 4 configuration (through cloudscribe)</h3>

<p>Identity server has many options – which can be bewildering to start with. Full documentation is here: <a href="https://identityserver4.readthedocs.io/">https://identityserver4.readthedocs.io/</a></p>

<p>For our purposes here – I’m outlining the bare minimum that we need to setup security for our API, either using:</p>

<p>· A client credential using a secret</p>

<p>· A username and password</p>

<h4>API resources</h4>

<p>Under the admin menu in cloudscribe select security settings / API resources and create a new API record giving it a name (e.g. api2) making sure it matches the name you entered as the .Audience in the startup.cs .</p>

<p>Then we need to add a single scope record – called “allowapi2” in this example.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image004_5.jpg"><img alt="clip_image004" border="0" height="155" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image004_thumb_5.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image004" width="244" /></a></p>

<h4>Client resources</h4>

<p>Under the admin menu in cloudscribe select security settings / API Clients and create a new client (I’ve called it client2 – remember this name for when we make the call from the client application). Edit the new client record and add:</p>

<p>· Allowed Scope record – e.g. allowapi2 – this must match the scope we entered for the api and is used to specify which apis this client can access</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image006_3.jpg"><img alt="clip_image006" border="0" height="86" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image006_thumb_3.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image006" width="214" /></a></p>

<p>· Client Secrets – the value is the SHA56 value of the secret we wish to use (in this example secret2) <s>– at the moment the cloudscribe interface doesn’t do this conversion for us so we have to do it manually somewhere&nbsp;(e.g. I used string s = "secret2".ToSha256();)&nbsp; &nbsp;</s></p>

<p><s>I added the secret using the web page and then pasted the converted secret direct into the relevant field in the record in the csids_ClientSecrets table in the database - but I think it would work equally well just pasting the converted value into the web page.</s></p>

<p><s>.ToSha256() is a string extension method&nbsp;in the IdentityModel assembly - this seems to do more than simply convert to sha256 - see&nbsp;<a href="https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Net45/Extensions/HashStringExtensions.cs">https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Net45/Extensions/HashStringExtensions.cs</a>.</s></p>

<p><s>It’s important that we set the secret type as well – in our example here it must be “SharedSecret”</s></p>

<p>Joe Audette has updated his nuget packages so saving a client secret now gives you a range of options for the secret type - in our example we need to pick "SharedSecret" and select to encrypt using Sha256 (see Joe's post in comments below for other options) which should make things easier.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image008_1.jpg"><img alt="clip_image008" border="0" height="68" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image008_thumb_1.jpg" style="margin: 0px; display: inline; background-image: none;" title="clip_image008" width="244" /></a></p>

<p>· Allowed Grant types – we are entering “password” and “client_credentials”. These determine how we can authenticate from the client app as we see below in the next section. Password means that authentication can use a username / pwd combination (i.e. a cloudscribe login). Client_credentials means we can login using a client secret and don’t have to be a known user on the site.</p>

<p><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image010_1.jpg"><img alt="clip_image010" border="0" height="81" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image010_thumb_1.jpg" style="display: inline; background-image: none;" title="clip_image010" width="200" /></a></p>

<h3>Client application</h3>

<p>To connect securely to the API using a client connection with a secret use:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
var tokenClient = <span style="color: rgb(0, 0, 255);">new</span> TokenClient(disco.TokenEndpoint, <span style="color: rgb(0, 96, 128);">"client2"</span>, <span style="color: rgb(0, 96, 128);">"secret2"</span>);</pre>
</div>

<p>To connect using a username and password use:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
var tokenResponsePassword = await tokenClient.RequestResourceOwnerPasswordAsync(<span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"allowapi2"</span>);</pre>
</div>

<p>Note that the user name is the user name not the email which can be used to login interactively.</p>

<p>The whole method in the controller looked something like this – the rest of the code is deserializing the JSON return from the API and putting it into an object that can be displayed on a view page</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">using</span> System;
<span style="color: rgb(0, 0, 255);">using</span> System.Collections.Generic;
<span style="color: rgb(0, 0, 255);">using</span> System.Diagnostics;
<span style="color: rgb(0, 0, 255);">using</span> System.Threading.Tasks;
<span style="color: rgb(0, 0, 255);">using</span> Microsoft.AspNetCore.Mvc;
<span style="color: rgb(0, 0, 255);">using</span> ESDM.Models;
<span style="color: rgb(0, 0, 255);">using</span> System.Net.Http;
<span style="color: rgb(0, 0, 255);">using</span> Newtonsoft.Json;
<span style="color: rgb(0, 0, 255);">using</span> IdentityModel.Client;
<span style="color: rgb(0, 0, 255);">using</span> IdentityServerClient.Models;
<span style="color: rgb(0, 0, 255);">using</span> IdentityModel;</pre>
</div>

<p>and then</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 128, 0);">//Hosted web API REST Service base url  </span>
<span style="color: rgb(0, 0, 255);">string</span> Baseurl = <span style="color: rgb(0, 96, 128);">"http://localhost:35668"</span>;

<span style="color: rgb(0, 0, 255);">public</span> async Task&lt;ActionResult&gt; Index()
{
    List&lt;WeatherForecast&gt; ans = <span style="color: rgb(0, 0, 255);">new</span> List&lt;WeatherForecast&gt;();
     <span style="color: rgb(0, 0, 255);">using</span> (var client = <span style="color: rgb(0, 0, 255);">new</span> HttpClient())
    {
        <span style="color: rgb(0, 128, 0);">// discover endpoints from metadata</span>
        var disco = await DiscoveryClient.GetAsync(Baseurl);
        var tokenClient = <span style="color: rgb(0, 0, 255);">new</span> TokenClient(disco.TokenEndpoint, <span style="color: rgb(0, 96, 128);">"client2"</span>, <span style="color: rgb(0, 96, 128);">"secret2"</span>);
        var tokenResponse = await tokenClient.RequestClientCredentialsAsync(<span style="color: rgb(0, 96, 128);">"allowapi2"</span>);

<span style="color: rgb(0, 128, 0);">//Example getting alternative token if you want to use username / pwd </span>
        var tokenResponsePassword = await tokenClient.RequestResourceOwnerPasswordAsync(<span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"admin"</span>, <span style="color: rgb(0, 96, 128);">"allowapi2"</span>);

        <span style="color: rgb(0, 128, 0);">// call api - change for tokenResponsePassword if you want to use username / pwd</span>
        client.SetBearerToken(tokenResponse.AccessToken);

        var response = await client.GetAsync(Baseurl + <span style="color: rgb(0, 96, 128);">"/api/SampleData/WeatherForecasts"</span>);
        <span style="color: rgb(0, 0, 255);">if</span> (response.IsSuccessStatusCode)
        {
            var content =  response.Content.ReadAsStringAsync().Result;
            ans = JsonConvert.DeserializeObject&lt;List&lt;WeatherForecast&gt;&gt;(content);
        }
        <span style="color: rgb(0, 0, 255);">return</span> View(ans);
    }</pre>
</div>

<p>The model for the forecast data was:</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
<span style="color: rgb(0, 0, 255);">namespace</span> ESDM.Models
{
    <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">partial</span> <span style="color: rgb(0, 0, 255);">class</span> WeatherForecast
    {
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> DateFormatted { get; set; }
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> TemperatureC { get; set; }
        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">string</span> Summary { get; set; }

        <span style="color: rgb(0, 0, 255);">public</span> <span style="color: rgb(0, 0, 255);">int</span> TemperatureF
        {
            get
            {
                <span style="color: rgb(0, 0, 255);">return</span> 32 + (<span style="color: rgb(0, 0, 255);">int</span>)(TemperatureC / 0.5556);
            }
        }
    }
}
</pre>
</div>

<p>And my view contained</p>

<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">
@model IEnumerable&lt;ESDM.Models.WeatherForecast&gt;
&lt;div&gt;
    &lt;ul&gt;
        @<span style="color: rgb(0, 0, 255);">foreach</span> (var forecast <span style="color: rgb(0, 0, 255);">in</span> Model)
        {
            &lt;li&gt;@forecast.Summary&lt;/li&gt;
        }
    &lt;/ul&gt;
&lt;/div&gt;</pre>
</div>
<br /><a href='https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis'></a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis'>...</a>]]></description>
      <link>https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</link>
      <author>()</author>
      <comments>https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/using-cloudscribe-with-identity-server-4-with-a-sql-server-store-on-net-core-2-mvc-securing-apis</guid>
      <pubDate>Fri, 03 Nov 2017 22:14:00 GMT</pubDate>
    </item>
    <item>
      <title>Making our mojoPortal sites even more secure</title>
      <description><![CDATA[<p>I’ve just run a couple of our mojoPortal web sites through the test on <a href="https://asafaweb.com" title="https://asafaweb.com">https://asafaweb.com</a> and thought I’d write up some notes on how to get the best possible test results (and therefore presumably the most secure sites!). We are starting from a point where all the basic security measures are in place – folder permissions, custom machine key, running the site under SSL.</p>

<p>The tests that did not immediately pass were:</p>

<h3>Request validation: Fail</h3>

<p>OK so we find &lt;pages validateRequest="false" … in web.config.</p>

<p>But we are assured this is deliberate and acceptable in mojoPortal because, as Joe Audette explained,</p>

<blockquote>
<p>validateRequest is false because we do our own validation of inputs and setting it to true causes problems/errors when posting back html content from a wysiwyg editor.</p>
</blockquote>

<h3>Excessive headers: Warning</h3>

<p>So our IIS server and site are revealing several pieces of information about the technologies behind the site, and we are <a href="http://www.troyhunt.com/2012/02/shhh-dont-let-your-response-headers.html" target="_blank">better concealing these http headers</a>.</p>

<p>Specifically we have:</p>

<ul>
	<li><strong>Server</strong> which can be turned off with <a href="http://www.iis.net/downloads/microsoft/urlscan" target="_blank">URLscan</a> by setting RemoveServerHeader=1 in UrlScan.ini (there are other ways, but this is by far the simplest).</li>
	<li><strong>X-Powered-By </strong>which can be turned off in IIS Manager: select the web site, then open HTTP Response Headers and remove this one.</li>
	<li><strong>X-AspNet-Version </strong>which can be suppressed by adding this attribute to the httpRuntime setting in web.config: &lt;httpRuntime … <strong>enableVersionHeader="false"</strong> /&gt;</li>
</ul>

<h3>Secure cookies: Fail</h3>

<p>In line with <a href="https://www.mojoportal.com/use-ssl" target="_blank">the mojoPortal documentation on SSL</a> we already had the attribute in bold:</p>

<p>&lt;forms name=".mysitecookiename" protection="All" timeout="50000000" path="/" cookieless="UseCookies" <strong>requireSSL="true"</strong> /&gt;</p>

<p>but we were still failing the test until we also added this into the system.web section:</p>

<p><strong>&lt;httpCookies httpOnlyCookies="true" requireSSL="true" /&gt;</strong></p>

<p>Joe Audette said:</p>

<blockquote>
<p>Adding the &lt;httpCookies element as you have just means that any other cookies issued for the web site would also be kept secure, but as far as I know any other cookies we are creating are only for cosmetic purposes such as the one to toggle the collapse state of the admin toolbar etc.</p>
</blockquote>

<p>So I think we were secure anyway, but it’s nice to pass the test.</p>

<h3>Clickjacking: Warning</h3>

<p>This was easily blocked by preventing our site from being run in an iframe. This is achieved in IIS Manager: select the web site, then open HTTP Response Headers, and add a new one as follows:</p>

<p><img alt="Adding an HTTP response header to block loading the site in an iframe" border="0" height="185" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/clip_image002_4.jpg" style="border: 0px currentColor; border-image: none; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Adding an HTTP response header to block loading the site in an iframe" width="326" /></p>

<p>If we are using an iframe within the site, for something like a keep-alive, another value for this setting is "SAMEORIGIN" - this allows iframes in the site to host content that is within the same domain. If we want our site to be displayed in an iframe within a known other site, we can use "ALLOW-FROM <em>uri</em>".</p>

<h3>View state MAC: Not tested</h3>

<p>Not tested apparently because the viewstate is encrypted in mojoPortal, but it turns out versions of mojoPortal before 2.4.0.2 had <strong>enableViewStateMac="false"</strong> which is not what we want. This has been removed, meaning it is set to true by default, in version 2.4.0.2, but if running an earlier version we should ensure this setting has <em>only</em> the following attributes:</p>

<p><code>&lt;pages validateRequest="false" viewStateEncryptionMode="Auto" maxPageStateFieldLength="500" controlRenderingCompatibilityVersion="4.0" clientIDMode="AutoID"&gt;</code></p>

<p>So on sites running mojoPortal 2.4.0.2 or later, no change needed.</p>

<h3>Summary</h3>

<p>So we needed to make two changes in web.config, remove one http header and add another one. Plus install and configure&nbsp;URLscan if it's not already present. These are one-off changes for a server and/or site except the web.config settings. mojoPortal upgrades generally involve overwriting the web.config, so it’s important to keep separate notes about what default settings need to be changed again after an upgrade. We always keep these notes for each site in a UpgradeNotes.config file in the root folder of the site.</p>

<h3>*** UPDATE ***</h3>

<p>Further testing of some sites with ZAP and&nbsp;<a href="https://securityheaders.com" target="_blank">https://securityheaders.com</a> identified two more http headers (in italics below) that should be set to improve security. Overall the header settings can be wrapped up by including this section in system.webServer:</p>

<p><code>&nbsp; &lt;system.webServer&gt;<br />
...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;httpProtocol&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;customHeaders&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;remove name="X-Powered-By" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="X-Frame-Options" value="SAMEORIGIN" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <em>&lt;add name="X-XSS-Protection" value="1" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add name="X-Content-Type-Options" value="nosniff" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;add name="Referrer-Policy" value="no-referrer-when-downgrade" /&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;add name="strict-transport-security" value="max-age=31536000; includeSubDomains" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/customHeaders&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/httpProtocol&gt;&nbsp; </em> </code></p>

<p><code>...</code></p>

<p>And it also pointed&nbsp;&nbsp;out that auto-complete was not disabled on the login screen (which leaves accounts vulnerable where more than one user may share a machine). This can be suppressed with the following in user.config:</p>

<p><code>&lt;add key="DisableAutoCompleteOnLogin" value="true"/&gt;</code></p>

<p>&nbsp;</p>
<br /><a href='https://www.esdm.co.uk/making-our-mojoportal-sites-even-more-secure'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/making-our-mojoportal-sites-even-more-secure'>...</a>]]></description>
      <link>https://www.esdm.co.uk/making-our-mojoportal-sites-even-more-secure</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/making-our-mojoportal-sites-even-more-secure</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/making-our-mojoportal-sites-even-more-secure</guid>
      <pubDate>Thu, 10 Apr 2014 18:31:00 GMT</pubDate>
    </item>
  </channel>
</rss>