Hardening Remote Desktop

Locking down Remote Desktop: accounts, Network Level Authentication, lockout policy, and restricting who can reach port 3389.

Windows9 min read

On this page
  1. Give Administrator a password nothing else uses
  2. Create a separate admin account
  3. Require Network Level Authentication
  4. Set an account lockout policy
  5. Restrict Remote Desktop to addresses you control
  6. Find the address you are connecting from
  7. Scope the rule in Windows Firewall
  8. What the panel can and cannot do here
  9. Changing the Remote Desktop port
  10. If you lock yourself out
  11. Related

Remote Desktop on a public address is the single most attacked thing on a Windows server. Port 3389 is scanned constantly, the attempts are automated, and they start within hours of the server getting an address. This page is the work that takes your server out of that funnel: a password nothing can guess, an account name nothing can assume, authentication that happens before a desktop is ever built, and a rule that says which addresses may knock at all.

An automated attempt on port 3389From anywhere, trying Administrator first, with passwordsfrom breach listsBlock IP, in the panelFor one address you have seen hammering, before your serverOptionalThat address, before it coststhe server anythingWindows Firewall scopeThe Remote Desktop rules answer only your addressesLargest effectEvery other address: neverreaches a password promptAttempts that cannot sign in:no session, no sign-in screenAttempts on AdministratorUnlimited guessingGuesses from breach listsNetwork Level AuthenticationThe client signs in before a session is builtChecked at that sign-inThe account nameAdministrator disabled or renamed, so a username must beguessed tooThe lockout policy10 failures within 15 minutes lock the account for 15minutes. Older builds exempt the built-in Administrator.The passwordLong, random, and used nowhere elseA desktop sessionOnly for someone who signs in correctlyAn automated attempt on port 3389From anywhere, trying Administrator first,with passwords from breach listsBlock IP, in the panelFor one address you have seen hammering,before your serverOptionalThat address, before it costs the serveranythingWindows Firewall scopeThe Remote Desktop rules answer only youraddressesLargest effectEvery other address: never reaches apassword promptNetwork Level AuthenticationThe client signs in before a session isbuiltAttempts that cannot sign in: nosession, no sign-in screenChecked at that sign-inThe account nameAdministrator disabled or renamed, so ausername must be guessed tooAttempts on AdministratorThe lockout policy10 failures within 15 minutes lock theaccount for 15 minutes. Older builds exemptthe built-in Administrator.Unlimited guessingThe passwordLong, random, and used nowhere elseGuesses from breach listsA desktop sessionOnly for someone who signs in correctly
What an automated attempt on Remote Desktop meets once this page is done, and what each step stops. The firewall scope turns most attempts away before any prompt; the account name, the lockout and the password are all checked at the Network Level Authentication sign-in, before a session exists. Illustration.

If you have not connected yet, start at Connecting to your server. If this server is new, do First steps first, because the password change belongs there and everything below assumes it is done.

Give Administrator a password nothing else uses

The account the attempts target by name is Administrator, and the passwords they try come from breach lists. That makes two properties matter, and only two: the password is long and random, and it has never been used anywhere else.

PowerShell
$pw = Read-Host -Prompt "New password" -AsSecureString
Set-LocalUser -Name Administrator -Password $pw

Generate it in a password manager and never type it from memory. Reusing the password from another server means one compromised machine hands over the rest, which is the usual way a single incident becomes a bad week.

Create a separate admin account

The default account is guessable by name on every Windows machine in the world. Signing in as a named account of your own removes that starting point, and it gives you a second way in while you change things.

  1. Create the account

    PowerShell
    $pw = Read-Host -Prompt "Password for the new account" -AsSecureString
    New-LocalUser -Name "cxadmin" -Password $pw -FullName "Operations" -PasswordNeverExpires

    Pick a name that is not admin, administrator, user, or the company name. The point of the exercise is that it cannot be guessed.

  2. Give it the rights it needs

    PowerShell
    Add-LocalGroupMember -Group "Administrators" -Member "cxadmin"
    Add-LocalGroupMember -Group "Remote Desktop Users" -Member "cxadmin"

    Members of Administrators can already sign in over Remote Desktop, so the second command is not strictly required for an admin account. Add it anyway if you later drop the account out of Administrators, and use Remote Desktop Users alone for any account that should be able to connect without administering the machine.

  3. Prove it works before you touch the default account

    Open a second Remote Desktop session as the new account, from scratch, with the password typed in. Do not skip this. The next step removes your fallback, and the only safe moment to do that is when you have already used the replacement.

  4. Disable or rename the built-in Administrator

    Disabling it is the stronger option, because a disabled account cannot be signed in to no matter what the password is:

    PowerShell
    Disable-LocalUser -Name Administrator

    Renaming it is the weaker option, and it is the one to pick when something on the machine expects the account to exist:

    PowerShell
    Rename-LocalUser -Name Administrator -NewName "cxroot"

    Undo either with Enable-LocalUser -Name Administrator or by renaming it back, from your new account or from the console.

Renaming does not change the account's underlying identifier, so software already running on the machine still recognises it. What renaming buys you is narrow and worth stating plainly: automated sign-in attempts from the internet have to guess a username as well as a password, and almost none of them do.

Require Network Level Authentication

Network Level Authentication makes the client prove who it is before the server builds a session for it. Without it, every connection attempt gets a desktop session and a sign-in screen, which costs the server real work and gives an attacker something to hammer. With it, an unauthenticated attempt is turned away at the front door.

It is on by default on current Windows Server images. Check rather than assume:

PowerShell
$rdp = "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
Get-ItemProperty -Path $rdp | Select-Object UserAuthentication, SecurityLayer

UserAuthentication should be 1. SecurityLayer should be 2, which requires TLS for the connection rather than the older RDP security layer. Set either if it is wrong:

PowerShell
Set-ItemProperty -Path $rdp -Name UserAuthentication -Value 1
Set-ItemProperty -Path $rdp -Name SecurityLayer -Value 2

In the GUI the same switch is in System Properties. Run sysdm.cpl, open the Remote tab, and tick Allow connections only from computers running Remote Desktop with Network Level Authentication (recommended).

Very old clients cannot do NLA. In practice that means a client older than Windows 7, or a third-party client that has not been updated in a decade. Update the client rather than turning this off.

Set an account lockout policy

A lockout policy puts a ceiling on guessing. Without one, an attacker gets unlimited attempts at your password; with one, they get ten and then wait.

PowerShell
net accounts /lockoutthreshold:10 /lockoutduration:15 /lockoutwindow:15

That reads as: ten failed attempts within a 15 minute window locks the account for 15 minutes. Confirm what is in force with net accounts on its own, or open secpol.msc and look under Account Policies, then Account Lockout Policy.

Two things to understand before you rely on it.

On older Windows builds the built-in Administrator account is exempt from lockout. Newer builds ship a setting that includes it, but the exemption is another reason to sign in as a named account of your own rather than as Administrator. A lockout policy protects the accounts it applies to, and on an older image that may not include the one being attacked.

A lockout policy is also a way to be locked out. Anyone who can reach port 3389 can trigger the threshold on an account whose name they know, deliberately or as a side effect of scanning. That is not a reason to skip it, it is a reason to also restrict who can reach the port, which is the next section, and to keep the console available.

Restrict Remote Desktop to addresses you control

This is the change with the largest effect on this page. If port 3389 only answers the handful of addresses you actually connect from, the automated attempts never reach a password prompt at all.

Find the address you are connecting from

Ask the server. It can see exactly where your current session comes from, which is more reliable than what your router reports:

PowerShell
Get-NetTCPConnection -LocalPort 3389 -State Established |
  Select-Object RemoteAddress, RemotePort

The RemoteAddress value is the address to put in the rule. If you have already moved Remote Desktop to another port, use that port number instead.

Scope the rule in Windows Firewall

Windows ships a group of rules that open Remote Desktop. Scoping that group is better than adding a rule of your own, because a new allow rule would sit alongside the existing open ones rather than replacing them.

Look at what is there first:

PowerShell
Get-NetFirewallRule -DisplayGroup "Remote Desktop" |
  Format-Table Name, DisplayName, Enabled, Profile

Then scope every rule in the group to the addresses you use:

PowerShell
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress "203.0.113.10"

For more than one source, pass an array. A CIDR range is accepted, which is how you cover an office or a VPN exit:

PowerShell
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress @("203.0.113.10", "203.0.113.0/24")

Confirm what is now in force:

PowerShell
Get-NetFirewallRule -DisplayGroup "Remote Desktop" | Get-NetFirewallAddressFilter

To undo it, put the scope back to everything:

PowerShell
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress Any

-DisplayGroup "Remote Desktop" is the English name for the group. On a localised Windows install, find the rules by pattern instead:

PowerShell
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*Remote Desktop*" }

More on rule syntax, profiles, and outbound rules is on Using Windows Firewall.

What the panel can and cannot do here

The panel filters at the network edge, before traffic reaches your server, so it is the right place to stop an abusive source outright. On the Firewall tab, Source IPs holds two lists. Block IP drops everything from a single address at the edge, with an optional expiry, and it bypasses your port rules. That is the panel-side answer to one host hammering your Remote Desktop port: block it and it stops arriving at all.

The second list, Whitelist, is not an access restriction. It marks a source as known-good so it bypasses rate limits and behavioural filtering, which is the opposite direction of travel. Adding your own address there does not close the port to anyone else.

So the two controls do different jobs, and you want both: the Windows Firewall scope decides who may reach Remote Desktop at all, and the panel's block list takes a specific bad source out of the picture earlier, before it costs your server anything. Full detail on Blocking and allowing IPs.

Windows Firewall scopeOn the server: the RemoteDesktop rulesDecides who may reach RemoteDesktop at all. Every addressnot in the scope is turnedaway.Closes 3389 to everyone elseBlock IPIn the panel: Firewall tab,Source IPsDrops everything from oneaddress before it reachesyour server, with an optionalexpiry. Bypasses port rules.Takes one bad source outWhitelistIn the panel: Firewall tab,Source IPsMarks an address asknown-good, so it skips ratelimits and behaviouralfiltering.Closes nothing to anyoneWindows Firewall scopeOn the server: the Remote Desktop rulesDecides who may reach Remote Desktop atall. Every address not in the scope isturned away.Closes 3389 to everyone elseBlock IPIn the panel: Firewall tab, Source IPsDrops everything from one address before itreaches your server, with an optionalexpiry. Bypasses port rules.Takes one bad source outWhitelistIn the panel: Firewall tab, Source IPsMarks an address as known-good, so it skipsrate limits and behavioural filtering.Closes nothing to anyone
Three controls that sound alike. Only the Windows Firewall scope restricts who may reach Remote Desktop; Block IP removes one address you name, before your server; Whitelist goes the other way and lets an address skip filtering, so it closes nothing. Illustration.

Changing the Remote Desktop port

Moving Remote Desktop off 3389 is worth doing for one honest reason: it takes your logs from thousands of failed attempts a day to almost none, which makes a real attack visible instead of buried. It is not a security control. Anything that scans the whole port range finds the new port in seconds, and a scan of all 65,535 ports takes very little time. Do it for the quiet, and keep the password, NLA, lockout, and source scoping doing the actual work.

  1. Pick a port that is free

    Choose a high port and check nothing is on it:

    PowerShell
    Get-NetTCPConnection -State Listen | Sort-Object LocalPort | Select-Object LocalAddress, LocalPort
  2. Open the new port first

    Add the rule before you move the service, scoped the same way as the old one:

    PowerShell
    New-NetFirewallRule -DisplayName "Remote Desktop on 34567" -Direction Inbound -Protocol TCP -LocalPort 34567 -Action Allow -RemoteAddress "203.0.113.10"
  3. Change the port

    PowerShell
    $rdp = "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
    Set-ItemProperty -Path $rdp -Name PortNumber -Value 34567
  4. Restart, and connect on the new port

    The setting is read when the Remote Desktop service starts, so restart the server:

    PowerShell
    Restart-Computer

    Then connect with the port on the end of the address:

    PowerShell
    mstsc /v:203.0.113.10:34567
  5. Close the old port only after the new one works

    Once a fresh session on the new port succeeds, remove the old opening:

    PowerShell
    Disable-NetFirewallRule -DisplayGroup "Remote Desktop"

If you have an inbound rule for TCP 3389 in the panel, add one for the new port as well, otherwise the port's rate limits and protocol checks do not apply to it. See Port rules.

If you lock yourself out

Open Console access. It is a screen attached to the machine and it does not depend on Remote Desktop, on Windows Firewall, or on the network configuration inside the server, which is precisely the situation you are in.

From the console, sign in locally and undo the change:

PowerShell
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress Any
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
Enable-LocalUser -Name Administrator

If the console gives you a sign-in prompt you cannot get past, the password itself is the problem rather than the firewall, and Resetting the root password is the route back, with the caveat above about disabled and renamed accounts. If none of that gets you in, open a ticket and say what you changed and when, because that is what makes it fixable.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.