What is SSH Hardening and Why is it Essential?
During our recent incident response engagements across several Tier-IV data centers in Bengaluru and Mumbai, we identified a recurring pattern: compromised Linux instances were almost exclusively running default SSH configurations. The discovery of CVE-2024-3094 (the XZ Utils backdoor) and CVE-2024-6387 (regreSSHion) has shifted SSH security from a "best practice" to a critical survival requirement for Indian SOCs. We observed that many organizations still rely on the default OpenSSH setup provided by their distribution, which is designed for compatibility rather than security.
Defining SSH Hardening
We define SSH hardening as the process of reducing the attack surface of the Secure Shell service by disabling legacy protocols, enforcing strong cryptographic primitives, and implementing strict access controls. It is not a "set and forget" task. We treat it as a continuous configuration management process. By hardening the developer terminal, we can prevent sophisticated threats like the PHANTOMPULSE RAT from gaining a foothold. In the context of the XZ backdoor, hardening also involves auditing the supply chain of the binaries we run. We found that even if a binary is not directly backdoored, its dependencies—like liblzma—can introduce remote code execution (RCE) vectors into the sshd process via libsystemd.
Common Vulnerabilities in Default SSH Configurations
Standard deployments often leave the following vectors wide open:
- Password-based Authentication: This remains the primary vector for brute-force attacks. We see millions of hits daily from botnets targeting Indian IP ranges, specifically looking for
admin,root, andusercredentials. - Weak Ciphers: Default configs often support
SHA-1orCBCmode ciphers, which are vulnerable to plaintext recovery attacks or birthday attacks. - Root Login: Permitting direct root access allows an attacker who bypasses authentication to have immediate, total control over the system.
- Information Leakage: SSH banners often reveal the exact version of the OS and OpenSSH, allowing attackers to tailor exploits for specific CVEs like regreSSHion.
The Role of Hardening in Server Security
Hardening acts as a compensatory control when zero-day vulnerabilities emerge. For instance, we noted that systems with MaxStartups throttled and LoginGraceTime reduced were significantly harder to exploit using the regreSSHion race condition. In India, where the DPDP Act 2023 now mandates "reasonable security safeguards" to prevent personal data breaches, failing to patch RCE vulnerabilities and harden a primary entry point like SSH could lead to significant legal and financial liabilities, with penalties reaching up to ₹250 Crore for non-compliance.
Comprehensive SSH Hardening Guidelines for Secure Access
We recommend a "Zero Trust" approach to SSH. Assume the network is compromised and the attacker has your IP address. For organizations looking to eliminate the risks of exposed ports, utilizing a browser based SSH client can help enforce strict access controls without the need for traditional VPNs. The following controls should be considered the baseline for any production environment.
Disabling Root Login via SSH
We never allow direct root login. Instead, we use a standard user account and escalate privileges via sudo. This creates an audit trail in /var/log/auth.log or journalctl that links actions to a specific human user. To implement this, we modify /etc/ssh/sshd_config:
# Check for existing PermitRootLogin settingsgrep -i "PermitRootLogin" /etc/ssh/sshd_config
Set to 'no' to force the use of a standard user
sudo sed -i 's/^#PermitRootLogin./PermitRootLogin no/' /etc/ssh/sshd_config sudo sed -i 's/^PermitRootLogin./PermitRootLogin no/' /etc/ssh/sshd_config
Enforcing SSH Key-Based Authentication
We have deprecated RSA keys in favor of Ed25519 due to its superior security profile and performance. RSA keys smaller than 3072 bits are increasingly susceptible to advances in computing power. We generate keys with a high iteration count for the KDF (Key Derivation Function) to slow down offline brute-force attacks on the private key file.
# Generate a high-entropy Ed25519 keyssh-keygen -t ed25519 -a 100 -C "$(hostname)-$(date +'%Y-%m-%d')"
The -a 100 flag increases the number of KDF rounds, making the private key more resistant to cracking.
Disabling Insecure Password Authentication
Once keys are deployed, we disable password authentication entirely. This single step eliminates 99% of automated attacks. We've seen SOC dashboards in Mumbai go from thousands of "Failed password" alerts per hour to zero after implementing this change.
# Disable password-based loginssudo sed -i 's/^#PasswordAuthentication./PasswordAuthentication no/' /etc/ssh/sshd_config sudo sed -i 's/^PasswordAuthentication./PasswordAuthentication no/' /etc/ssh/sshd_config
Also disable empty passwords and challenge-response
sudo sed -i 's/^PermitEmptyPasswords./PermitEmptyPasswords no/' /etc/ssh/sshd_config sudo sed -i 's/^ChallengeResponseAuthentication./ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
Restricting User Access and Using AllowGroups
We apply the Principle of Least Privilege (PoLP) by explicitly whitelisting who can log in. If a user is not in the ssh-users group, the daemon should reject the connection before even checking for a key. This prevents service accounts or system users (like www-data or postgres) from being used as entry points.
# Create a dedicated group for SSH accesssudo groupadd ssh-users sudo usermod -aG ssh-users
Add the restriction to sshd_config
echo "AllowGroups ssh-users" | sudo tee -a /etc/ssh/sshd_config
The Ultimate SSH Hardening Checklist
For Indian SOCs managing large-scale infrastructure, we use this checklist to ensure consistency across cloud providers (AWS Mumbai, Azure Central India) and on-premise hardware.
Changing the Default SSH Port (Port 22)
While often dismissed as "security by obscurity," we find that moving SSH to a non-standard port (e.g., 2222 or 4433) drastically reduces log noise. This allows our monitoring tools to focus on targeted attacks rather than the constant background radiation of internet-wide scanners. However, ensure that your local firewall (ufw, firewalld) and cloud Security Groups are updated first.
# Change port to 4433sudo sed -i 's/^#Port 22/Port 4433/' /etc/ssh/sshd_config sudo sed -i 's/^Port 22/Port 4433/' /etc/ssh/sshd_config
Update UFW (if applicable)
sudo ufw allow 4433/tcp sudo ufw deny 22/tcp
Configuring Idle Timeout Intervals
To prevent abandoned sessions from being hijacked, we enforce an idle timeout. If there is no activity for 5 minutes, the server terminates the connection. We set ClientAliveCountMax to 0 to ensure that the server doesn't keep the connection alive indefinitely.
# Set 5-minute timeout
echo "ClientAliveInterval 300" | sudo tee -a /etc/ssh/sshd_config echo "ClientAliveCountMax 0" | sudo tee -a /etc/ssh/sshd_config
Limiting Maximum Authentication Attempts
We restrict the number of failed attempts per connection. This mitigates "low and slow" brute-force attacks that might attempt multiple passwords within a single TCP session. We typically set this to 3.
# Limit attempts
sudo sed -i 's/^#MaxAuthTries./MaxAuthTries 3/' /etc/ssh/sshd_config sudo sed -i 's/^MaxAuthTries./MaxAuthTries 3/' /etc/ssh/sshd_config
Disabling X11 Forwarding and Port Forwarding
Unless specifically required for legacy GUI applications (which we strongly advise against), X11 forwarding should be disabled to prevent attackers from snooping on local X11 displays. Similarly, we disable agent forwarding and TCP forwarding to prevent lateral movement within the network.
X11Forwarding no
AllowTcpForwarding no AllowStreamLocalForwarding no GatewayPorts no PermitTunnel no
Advanced SSH Audit and Hardening Guide
Post-XZ and post-regreSSHion, we must go beyond basic configuration. We need to audit the cryptographic negotiation process and the binaries themselves.
Auditing Current SSH Configurations
The first step in our audit is checking for the XZ backdoor. We use the following command to identify if the installed version of xz is within the vulnerable range (5.6.0 and 5.6.1) and if sshd is linked to liblzma.
# Check XZ versionxz --version | grep -E '5\.6\.[01]'
Check if sshd is linked to liblzma (the vector for the XZ backdoor)
ldd $(which sshd) | grep liblzma
If liblzma appears in the output, the server is potentially vulnerable to CVE-2024-3094, depending on the distribution's patching status. We recommend immediate updates and checking the NIST NVD for the latest remediation guidance.
Implementing Strong Ciphers and MACs
We explicitly define the allowed Key Exchange (Kex), Cipher, and MAC (Message Authentication Code) algorithms. We remove everything that doesn't support AEAD (Authenticated Encryption with Associated Data) or "Encrypt-then-MAC" (EtM) constructions. This protects against the Terrapin attack (CVE-2023-48795).
# Add to /etc/ssh/sshd_config
KexAlgorithms [email protected],diffie-hellman-group-exchange-sha256 Ciphers [email protected],[email protected],[email protected] MACs [email protected],[email protected]
Using SSH Audit Tools for Compliance
We use the ssh-audit tool to verify our hardening efforts. It provides a detailed report on which algorithms are weak and suggests improvements. This is a critical step for demonstrating compliance with ISO 27001 or the DPDP Act's security requirements.
# Run ssh-audit against the local serverInstall via pip if not available: pip install ssh-audit
ssh-audit localhost:22
We look for "green" results across the board. Any "red" or "yellow" results indicating the use of diffie-hellman-group1-sha1 or arcfour are flagged for immediate remediation in our SOC dashboards.
Monitoring SSH Logs for Brute Force Attacks
We monitor logs in real-time to identify IP addresses that are aggressively targeting our infrastructure. In India, we frequently observe large-scale scanning from botnets. Integrating these logs into a centralized SIEM allows for automated threat detection and rapid response.
# Identify top 10 IP addresses attempting failed logins
journalctl -u ssh --since "24 hours ago" | grep "Failed password" | awk '{print $11}' | sort | uniq -c | sort -nr | head -n 10
Automating Security with an SSH Hardening Script
Manual configuration is prone to error, especially when managing hundreds of nodes across different Indian states. We use automation to ensure a consistent security posture.
Benefits of Using a Hardening Script
Automation ensures that every server, whether it's a dev instance or a production database, follows the same security baseline. We've found that using scripts reduces the "time-to-secure" for new instances from hours to seconds. It also prevents the accidental omission of critical settings like PermitEmptyPasswords no.
Top Open-Source SSH Hardening Scripts
While we often use custom Ansible playbooks, several open-source tools are excellent for standalone hardening:
- DevSec SSH Baseline: An Ansible role that implements high-security settings.
- Ssh-audit (Hardening modes): Can generate recommended configurations based on the current OpenSSH version.
- Custom Bash Scripts: For quick deployment on single instances where Ansible is not available.
How to Safely Deploy a Hardening Script on Production Servers
We never run a hardening script on a production server without a backout plan. A common mistake is disabling password authentication before the SSH key is correctly placed in ~/.ssh/authorized_keys, leading to a lockout. We follow this workflow:
- Verify SSH key access works in a separate terminal session.
- Run the hardening script on a staging/UAT environment first.
- Use
sshd -tto check the configuration syntax before restarting the service. - Keep one active SSH session open while restarting the daemon and testing a new connection.
#!/bin/bashA basic SSH hardening script for Indian SOCs
set -e
echo "Starting SSH Hardening..."
1. Backup current config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
2. Apply hardening parameters
cat < /etc/ssh/sshd_config.d/99-hardening.conf Protocol 2 IgnoreRhosts yes HostbasedAuthentication no PermitRootLogin no MaxAuthTries 3 MaxSessions 2 PubkeyAuthentication yes PasswordAuthentication no PermitEmptyPasswords no KexAlgorithms [email protected] Ciphers [email protected],[email protected] MACs [email protected],[email protected] ClientAliveInterval 300 ClientAliveCountMax 0 LoginGraceTime 30 EOF
3. Test configuration
sshd -t
4. Restart service
systemctl restart ssh
echo "SSH Hardening Complete. Please do not close this session until you verify access in a new window."
Conclusion: Maintaining a Hardened SSH Environment
Maintaining security is an iterative process. We've observed that "configuration drift" often occurs when developers temporarily re-enable features for troubleshooting and forget to revert them. This is why we treat SSH hardening as a lifecycle task.
Regular Security Reviews
We conduct quarterly reviews of all sshd_config files. We use tools like Lynis or custom scanning scripts to ensure that no unauthorized changes have been made. In the Indian context, where internal threats are as significant as external ones, we also audit the authorized_keys files for every user to ensure no rogue keys have been added.
# Audit authorized_keys for all users
find /home -name "authorized_keys" -exec ls -l {} \; find /home -name "authorized_keys" -exec cat {} \;
Staying Updated with SSH Security Patches
The XZ and regreSSHion vulnerabilities proved that even the most trusted tools can have flaws. We subscribe to the CERT-In vulnerability notes and the OpenSSH security mailing list. For Indian organizations, we recommend mirroring official repositories locally to ensure that patches can be deployed rapidly even if international bandwidth is throttled or restricted during a national cyber emergency.
We observed that servers running EOL (End-of-Life) distributions like CentOS 7 are the most vulnerable because they do not receive backported patches for these new CVEs. We strongly advocate for migrating these workloads to supported versions like Rocky Linux, AlmaLinux, or Ubuntu LTS immediately.
Check the LoginGraceTime on your servers right now. If it is set to the default (120 seconds), you are more vulnerable to regreSSHion. Reducing it to 30 seconds significantly complicates the attacker's ability to win the race condition required for the exploit.
# Check LoginGraceTimegrep "LoginGraceTime" /etc/ssh/sshd_config
If not set or high, reduce it
echo "LoginGraceTime 30" | sudo tee -a /etc/ssh/sshd_config.d/99-hardening.conf sudo systemctl reload ssh
