Have you secured your IIS Web Server?

Cloud services have improved our lives and made our jobs easier – BUT they have also given hackers a worldwide platform of unlimited power with which to attack us… very sad but very true!

😲 😲 😲 😲 😲 😲 😲 😲

This makes it even more critical to secure our external-facing services as much as we can.

Hopefully you have a WAF in front of your web server, but if you are like me and have a small site that does not justify the associated costs of advanced protection, here are some basic steps to take on your Windows Server. Note that ‘Strict High Transport Security’ (step 4) is available from IIS 10 in 2019 Server.

If you’re an IT nerd like me, you just gotta be happy with a result like this from

SSL Labs test site

NOTE: When you run the test, remember to check the box if you do not want the result to be displayed on the page…


OK – to business! There are four initial steps we can perform on a personal or small business web server that is exposed to the interwebs… resulting in an A+ score from an SSL test. Note that this is ideally run from the web server with traffic allowed inbound on port 80 and 443 (you can redirect 80 to 443, but 80 does need to be open for LetsEncrypt to work without manual intervention – AFAIK 😃).

  1. Apply a Lets Encrypt certificate.

a) they are free!

b) they have a great reputation.

c) they are so easy to install it is not even funny!

Go to, click downloads and grab the latest version. Extract to c:\program files\win-acme. Run wacs.exe and follow the prompts… you can manually specify hostname, additional SAN names if required, or generate a wildcard. Use the default in memory validation; this creates a virtual directory (which is in memory and removed afterwards) in IIS for LetsEncrypt to connect to to verify the request. The app then automatically creates and applies a certificate that is valid for 3 months, then sets up a scheduled task to automatically renew the cert before expiry. Boom! 😁😁

Refer to

2. Secure the protocols…

Open PowerShell ISE (run as admin), paste and run the code below to confirm TLS 1.0 and 1.1 are disabled and TLS 1.2 is enabled for the system and .NET:

# disable TLS 1.0 and 1.1
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null

# enable TLS 1.2 for .NET
New-Item 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -name 'SystemDefaultTlsVersions' -value '1' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -name 'SchUseStrongCrypto' -value '1' -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -name 'SystemDefaultTlsVersions' -value '1' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -name 'SchUseStrongCrypto' -value '1' -PropertyType 'DWord' -Force | Out-Null

# enable TLS 1.2 for system
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -name 'Enabled' -value '1' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -name 'DisabledByDefault' -value 0 -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -name 'Enabled' -value '1' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -name 'DisabledByDefault' -value 0 -PropertyType 'DWord' -Force | Out-Null

Write-Host -ForegroundColor Green 'TLS 1.0 and 1.1 disabled. TLS 1.2 enabled.'

3. Disable insecure ciphers…

In a new ISE tab, paste the following code to disable weak ciphers (some commands may fail but that’s okay):

Disable-TlsCipherSuite -Name "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" | Out-Null
Disable-TlsCipherSuite -Name "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_256_GCM_SHA384" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_128_GCM_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_256_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_128_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_256_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_128_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_3DES_EDE_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_DSS_WITH_AES_256_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_DSS_WITH_AES_128_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_RC4_128_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_RC4_128_MD5" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_NULL_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_RSA_WITH_NULL_SHA" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_AES_256_GCM_SHA384" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_AES_128_GCM_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_AES_256_CBC_SHA384" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_AES_128_CBC_SHA256" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_NULL_SHA384" | Out-Null
Disable-TlsCipherSuite -Name "TLS_PSK_WITH_NULL_SHA256" | Out-Null

Write-Host -ForegroundColor Green "Weak ciphers disabled."

4. Enable HTTP Strict Transport Security… (Windows 2019+)

a) In IIS Manager, open the HTTP Response Headers section.

b) Click Add.

c) In the Name field, add “Strict-Transport-Security“.

d) In the Value field, add “max-age=31536000” (this corresponds to a one year period validity).

d) Click OK.

Oh and don’t forget to redirect port 80 to 443… even though the above step effectively forces the browser to use HTTPS, there is no harm doing it with the URL Rewrite feature (I use this so that I can also block connections to my WordPress admin page).

Here is the rule I use at the web site level to redirect any HTTP request to HTTPS:

EDIT 02/07/22: totes forgot about step 5 – CAA (Certificate Authority Authorization) records which I enabled on this site. I use who offer these records free with domain name registration. Most DNS providers should support CAA records, if not, well… switch providers I say! 😎 Here is a screenshot of my records:

Superbulous! Now run the test, grab a cup of tea and a biscuit and pat yourself on the back for being so awesome!! 😎 😎 😎

Over and out until next time! Cheers 🍻


Please do NOT disable Security Defaults!

If you aren’t licensed for and using Conditional Access policies, please do not disable the security defaults feature just because something isn’t working (e.g. scan to email). Microsoft introduced the defaults for a very good reason – they realised that tenants without Azure AD Premium P1 licensing and correctly configured CA policies were wide open to Phishing and Password Spray attacks, via connections to Exchange Online using basic authentication protocols such as POP, IMAP and SMTP.

Connections using basic authentication do not support and therefore bypass MFA. If you disable this setting you are effectively turning off many security features.

Let’s find a solution to these problems and leave our tenant protected ‘by default’.

Here’s how to allow certain things (e.g. Teams meeting room devices and printers) while leaving our tenant secure:

1. Add any external IPs of company locations to Trusted IPs under MFA settings. In most cases you would do this for all company owned office locations.

2. Set Password Reset Registration to No so that new users are not prompted to register.

3. If you need to send SMTP email through Exchange Online (e.g. from a printer), create an account with exchange license to use for sending.

4. Load Cloud Shell from top of the Azure Portal. Connect to Exchange:


5. Create an EOL Authentication Policy:

New-AuthenticationPolicy -Name “Allow Basic Auth SMTP” -AllowBasicAuthSmtp

6. Assign the policy to the user:

Get-User | Set-User -AuthenticationPolicy “Allow Basic Auth SMTP”

7. Optionally, force the policy to apply within 30 minutes:

Set-User -STSRefreshTokensValidFrom $([System.DateTime]::UtcNow)

Now your users and devices will be able to connect without MFA requirement from trusted offices, and you can set up Scan to Email functions to use the account you created.

Righto – time have a cup of tea and reward yourself for not chopping off a leg to fix an itch! 🤣🤣


Is your Hybrid Exchange Server SAFE or unknowingly EXPOSED?

Hello and happy 2022! 🥳 I hope the year has started off as well as it possibly can for you (aside from the holiday ending a bit too early of course!)… 🍺😭 …right back to work!

Now, is your Exchange environment safe!!?? Really? Is it?

I love this utility called ‘SMTP Diag Tool’. It’s great for seeing whether you can connect directly to port 25 of an Exchange server. In most cases, if you can, you can send unauthenticated email to accepted domains within the organisation, because that’s how email used to work right?

SMTP Diag Tool

Before we had a mail filtering service in front of Exchange, email was sent directly between servers (in fact if Exchange was configured to use SpamHaus and SpamCop lookups, it did a pretty good job itself!). But I digress…

In many cases lately I have found I can connect directly to, and send unchecked email to Exchange servers, most often using ‘mail’ or ‘webmail’ hostnames. This method bypasses MX records, mail filtering services and also tells you probably have port 443 wide open as well. Clients are shocked – bypassing my mail filter – really? Yep! Here are the scenarios:

  1. You have a mail filtering service, but nobody set up the firewall or Exchange rules to restrict inbound email to the services sending IP Addresses (applies to on-premises and 365).
  2. You have a Hybrid Exchange configuration, some or all mailboxes are in the cloud but you still have port 25 and 443 open to your on-premises server.
  3. And a less serious case but a definite gap to plug – you are cloud only or hybrid, but have not configured rules to stop other tenants from emailing you directly (this would need to be a targeted attack, but I’m sure the attackers will automate the creation of that attack in future if not already).

To fix this there are several options; the more configured the better!

  1. The best step to take is restricting incoming traffic at the firewall. If you can’t use ‘Internet databases’ as firewall objects, throw away your firewall and get a suitable FortiGate model.
  2. Use the ‘Microsoft-Azure’ and ‘Microsoft-Outlook’ objects to restrict incoming (and outbound if you can) 25 traffic to Microsoft’s servers (if you are hybrid and using Defender mail filtering) OR by restricting to your mail filtering service’s IP range for port 25 (if you are on-premises, or routing through it).
  3. Use the Exchange Hybrid Agent! Re-run the wizard and choose the Agent option (you may need to install it manually first if running 2010). This allows you to close port 443 inbound entirely, by performing free / busy lookups and mailbox moves through an Azure App Proxy.
  4. For port 443, you can also use an Azure Application Proxy to act as a gateway to your environment if you are not Hybrid and it must be contactable from the internet (IMO Everyone should be using this for internet facing services – then you can close all inbound ports on your firewall and let Microsoft do the work!! (security team = ✅)
  5. If you do have inbound 443, use IIS with the URL Rewrite module to block access to any virtual directories that are not required (you’re not still using ActiveSync I hope!!?).
  6. You can also modify the Default Receive Connector in Exchange only to accept from a filtering service, or 365, but this is harder to maintain and shouldn’t be needed if the firewall is configured correctly.
  7. For the cloud-only and Hybrid scenarios, make sure you have implemented the Exchange rules to ensure only mail using your MX record will be delivered, ensuring it traverses the Mail Filtering protection of Exchange Online: Advanced Office 365 Routing: Locking Down Exchange On-Premises when MX points to Office 365 – Microsoft Tech Community.

I also recommend performing the following tasks to ensure maximum security for your environment, first focusing on Hybrid Exchange seeing as that is the most common scenario.

  • AD Connect – if it’s been around for a while, make sure it is now on a 2016/2019 server with TLS 1.2 enabled. You must have these OS to install the latest version. Also make sure it is configured for Hybrid Exchange while you are there.
  • Hopefully you are on Exchange 2016 (since that is free for Hybrid and 2019 is not), if not plan and get it done ASAP. Don’t use 2 vCPU and 8GB memory for your Exchange Hybrid server, what are we, skinflints? 4 vCPU and 16GB are recommended and should be easily achievable in any environment. If not then I cry for you… 😭😭
  • Use Azure Automation with Server Update Management to automatically patch your on-premises servers, even if you don’t have Azure yet this is worth enabling it for… WSUS – Yuuuuk! 🤮🤮

Until next time – chur chur from Simon!


How to restrict partner access to Azure and 365

Most customers have accepted at least one, if not multiple invitations from Microsoft partners to provide licensing or support services. What they often dont know is that by default this allows the partner to assign full administrative access to any of it’s staff, to perform tasks in the customers 365 / Azure tenant. It’s an ‘all or nothing’ configuration which is, and should be of concern to many customers who read the fine print of the invitation they are accepting.

The recent hack on a large distributor highlights how dangerous leaving this ‘as is’ can be:

Mega-distie SYNNEX attacked and Microsoft cloud accounts it tends tampered • The Register

Microsoft are developing the Lighthouse solution to allow us to use more detailed permissions for support. But it’s not there yet, so I started testing another solution to ths problem.

Turns out you can do most things using B2B guest access and client targeted URLs (appending the clients custom domain to the admin URL) as below:

Helpdesk staff use:
Azure AD –
Exchange –

Admin staff use:
Azure –
Exchange –
SharePoint –
Intune –  
Security Center –

Unfortunately this doesn’t seem to apply for the 365 Admin Center (please comment if you found a way to do it!), which is where you would want Helpdesk staff to be performing User / Exchange tasks, rather than jumping between Azure AD and Exchange portals. But, at least it works, and achieves the goal for security conscious customers who are hesitant to accept the partner invitation.

Here is the process:

1. Customer accepts MSP invitation

2. Customer removes the admin / helpdesk agent privileges from the partners area of the customers portal (this keeps the association and you can still procure licensing for them, but removes the default partner permissions)

3. Create two or more groups in customers Azure AD; one for your Helpdesk and one for Admins (with assign roles to group enabled)

4. Assign the roles to the Helpdesk group (change to fit your needs or use custom roles):
User Administrator
Exchange Recipient Administrator

5. Assign roles to the Admin group as required (you can use more admin groups to assign roles to different support groups if required):

Intune Administrator
Authentication Administrator
Exchange Administrator
User Administrator
Guest Inviter
Application Administrator
Compliance Administrator
Global Reader
Conditional Access Administrator
Cloud App Security Administrator
License Administrator
Azure AD Joined Device Local Administrator
Groups Administrator
SharePoint Administrator
Privileged Role Administrator
Azure Information Protection Administrator
Security Administrator

6. Assign Azure subscription roles to the Admin groups as required:

7. You can also use these groups to assign permissions to certain Azure objects, using the IAM blade under the resource

8. Use ‘Bulk Invite’ in customers Azure AD users blade to invite all your support staff to the customer tenant as guests

9. Add the invited guest accounts to the groups you created as required

After a support staff member accepts the invitation, they can open the URLs mentioned above using their standard user account to perform tasks in a customer tenant.

Not perfect, but it does work and avoids using generic credentials to perform tasks in a customer tenant.