Hardening Microcomputer Operating Systems

IoT Security

IoT Security involves safeguarding Internet of Things (IoT) devices and the networks they connect to from unauthorized access, misuse, data breaches, and other cyber threats. IoT devices frequently operate with limited computing power, minimal built-in security, and are deployed in large numbers, making them attractive targets for attackers seeking to exploit vulnerabilities.

A major challenge is that many IoT devices are not adequately secured by default. Common issues include weak or hardcoded passwords, outdated firmware, insufficient encryption, and inadequate patching mechanisms. This exposes devices such as smart cameras, wearable technology, industrial sensors, and home automation systems to risks like malware infections, botnet recruitment, and data theft.

However, awareness of these risks is growing. In recent years, governments, industry groups, and standards organizations have implemented laws, regulations, security standards, and best practices to improve IoT security. Examples include requirements for stronger authentication, regular software updates, encrypted communication, and transparency about how data is collected and stored. These measures are expected to significantly reduce the number of insecure devices in the coming years.

Ultimately, IoT security is not just about protecting individual devices; it also involves safeguarding entire ecosystems from personal smart home networks to critical infrastructure across industries such as healthcare, transportation, and energy. As IoT adoption expands, robust security practices will be essential to maintain user trust and ensure the safe integration of billions of connected devices into daily life.


Initial IoT Devices Security Steps

After purchasing an IoT device with inadequate security measures, the risk of exploitation begins almost immediately. Studies have shown that unsecured IoT devices can be discovered and compromised by automated scanning tools or malicious actors within just a few minutes of being connected to the internet. Once compromised, these devices may be used in a botnet, subjected to surveillance, or manipulated in ways that threaten your data, privacy, and network security.

To mitigate these risks, it’s important to take proactive steps before and after connecting the device to the internet. Here are some key actions you can take:

  • Check Manufacturer Information: Visit the manufacturer’s website for details on security features, firmware updates, and recommended setup practices.
  • Review Security Advisories: Many vendors provide security advisories, patch releases, or guides to help configure devices more securely.

Practical Steps to Secure IoT Devices

  • Change Default Passwords: Replace factory-set credentials with strong, unique passwords. Default passwords are widely known and are often the first thing attackers try.
  • Update Firmware and Applications: Always install the latest updates to close known vulnerabilities. Enable automatic updates if supported to ensure security patches are applied promptly.
  • Enable Logging: Turn on activity logs to track access attempts, configuration changes, or unusual behavior. Logs can help identify suspicious activity early.
  • Disable Unused Features and Services: Shut down unnecessary services, such as remote management, Bluetooth, or Universal Plug and Play (UPnP). Each unnecessary feature is a potential entry point for attackers.
  • Enable Multi-Factor Authentication (MFA): If available, require MFA for logins. This adds an extra layer of security even if your password is compromised.
  • Block Unused Ports: Restrict network access by closing or filtering ports that the device does not use. This reduces exposure to external attacks.
  • Connect to Wi-Fi Carefully: Place IoT devices on a separate network or VLAN rather than your main network. If one device is compromised, it will be harder for it to spread to other systems, such as laptops and phones. For some devices, consider not connecting them to the internet if connectivity isn’t essential.
  • Review Application Permissions: Check what kind of data the device’s companion app collects (e.g., location, microphone, contacts) and restrict permissions to only what is necessary.
  • Turn Devices Off When Not in Use: Shutting down devices when not in use minimizes their exposure to online threats.
  • Monitor Device Activity: Use your router’s interface or a network monitoring tool to watch for unusual traffic patterns. Early detection of suspicious behavior can prevent larger compromises.

By implementing these steps, you create multiple layers of defense that significantly reduce the likelihood of exploitation of your IoT devices. This approach, often called “defense in depth,” ensures that if one safeguard fails, others remain in place to protect your devices and network.


Vulnerabilities vs Misconfiguration

A misconfiguration occurs when a system, application, or device is set up incorrectly, deviating from security best practices (e.g., using weak or hard-coded passwords, having permissive firewall rules, running unnecessary services, or exposing interfaces unnecessarily). While a vulnerability is a flaw or weakness in software, hardware, or protocols that can be exploited to compromise security (e.g., an outdated SSH version with known exploits or design flaws in interfaces).

Top IoT Vulnerabilities/Misconfiguration

  • Weak or Hard-Coded Passwords
    • Type: Misconfiguration
    • Why: Choosing a weak password or leaving a default/hard-coded password is a configuration choice. These passwords can be easily guessed or cracked by attackers.
  • Insecure Service
    • Type: Could be vulnerability or misconfiguration, depending on context
      • If the service itself has a flaw (like an old SSH version with a known exploit):
        • Type: Vulnerability
        • Why: The service contains inherent security flaws that attackers can exploit.
      • If it’s running unnecessarily or open to the network without need:
        • Type: Misconfiguration
        • Why: Running unnecessary services or exposing them to the network without proper justification is a configuration error.
  • Insecure Configuration
    • Type: Misconfiguration
    • Why: By definition, misconfiguration occurs when system settings are insecure (e.g., permissive firewall rules, open ports). These settings leave systems vulnerable to attacks.
  • Insecure Interface
    • Type: Usually a vulnerability, if the interface itself has design flaws
      • If the interface itself has design flaws:
          • Type: Vulnerability
      • Why: Design flaws in interfaces can allow attackers to exploit vulnerabilities.
      • If it’s exposed unnecessarily:
        • Type: Misconfiguration
        • Why: Exposing an interface that should be protected is a configuration error.
  • Lack of Updates
    • Type: Misconfiguration (or operational issue)
    • Why: Not applying patches is a configuration/maintenance failure that leaves systems exposed to known vulnerabilities.
  • Use Outdated Modules
    • Type: Misconfiguration (or operational issue)
    • Why: Similar to lack of updates; continuing to use old libraries or modules is a choice/configuration oversight. These outdated components may contain security flaws.
  • Poor Physical Security
    • Type: Misconfiguration (or security control failure)
    • Why: Leaving hardware unprotected is a misconfiguration of physical security measures. Proper physical security controls are essential to prevent unauthorized access to devices.

By understanding these classifications and their underlying reasons, you can better identify and address potential security issues in your IoT devices, thereby enhancing overall system security.


Changing the default password

You can change the password of the user pi using the passwd command, keep in mind that the ssh will not drop

passwd # Linux command used to change the password of the currently logged-in user

(RPi) passwd
Changing password for pi.
Current password: 
New password: 
Retype new password: 
passwd: password updated successfully

or, one-liner, if you use a different username, you need to change pi to your username, this will bypass the password security controls like password length

(RPi) # Indicates the command is executed on the Raspberry Pi device
echo # Prints the string to standard output
‘pi:P@ssw0rd!’ # String in the format username:password to update the password
| # Pipe operator; passes the output of echo as input to the next command
sudo # Run the command with superuser (administrator) privileges
chpasswd # Linux command that reads username:password pairs and updates user passwords

(RPi) echo 'pi:P@ssw0rd!' | sudo chpasswd

Force user to change password next login

You can change the expiry information of the user pi using passwd --expire <user>, and use chage -l <user> to check the info

sudo # Run the command with superuser (administrator) privileges
passwd # Linux command used to change a user’s password
–expire # Option to immediately expire the user’s password, forcing a reset on next login
pi # The username of the account whose password will be expired (default Raspberry Pi user)

(RPi) sudo passwd --expire pi
passwd: password expiry information changed.

Then

chage # Linux command used to view or modify user password aging information
-l # Option to list the current password aging settings for a user
pi # The username of the account whose password aging information is being displayed

(RPi) chage -l pi
Last password change                    : password must be changed
Password expires                    : password must be changed
Password inactive                    : password must be changed
Account expires                        : never
Minimum number of days between password change        : 0
Maximum number of days between password change        : 99999
Number of days of warning before password expires    : 7

Remove nopasswd feature

The nopasswd feature allows a user to run the command as a sudo user without having to enter a password. This can be disabling or removing the 010_pi-nopasswd

sudo # Run the command with superuser (administrator) privileges
rm # Linux command to remove/delete files
/etc/sudoers.d/ # Directory containing additional sudo configuration files
010_* # Wildcard pattern matching all files starting with “010_” in the directory

(RPi) sudo rm /etc/sudoers.d/010_*

Or,

sudo # Run the command with superuser (administrator) privileges
visudo # Open the sudoers file in a safe editor that checks for syntax errors before saving

sudo visudo

change this 

Defaults        env_reset

to 

Defaults        env_reset,timestamp_timeout=0

Update & upgrade Raspberry Pi OS

Use the apt-get update to update the package sources list (It does not install or upgrade any package). Then, apt-get upgrade to install or upgrade the packages currently installed on the system from /etc/apt/sources.list

sudo # Run the command with superuser (administrator) privileges
apt-get # Linux package management command used to handle software packages
update # Option to refresh the local package index with the latest versions available from repositories

(RPi) sudo apt-get update

Then

sudo # Run the command with superuser (administrator) privileges
apt-get # Linux package management command used to handle software packages
update # Option to install the latest versions of all currently installed packages

(RPi) sudo apt-get upgrade

Or one liner

sudo # Run the command with superuser (administrator) privileges
apt-get # Linux package management command used to handle software packages
update # Option to refresh the local package index with the latest versions available from repositories
&& # Logical AND operator; runs the next command only if the previous one succeeds
sudo # Run the following command with superuser privileges
apt-get # Linux package management command
upgrade # Option to install the latest versions of all currently installed packages

(RPi) sudo apt-get update && sudo apt-get upgrade

Enable Logging

Debain OS 12 has a new way of logging using systemd-journald which it saves the logs in a centralized journal, you can access the logs using journalctl command, or you can replace that with classic logging using rsyslog

Reviewing the last 2 hours logs

journalctl # Linux command to query and display system logs managed by systemd
–since # Option to filter logs starting from a specific time
“2 hour ago” # Time specification; shows logs from the last 2 hours

(RPi) journalctl --since "2 hour ago"

Classic Logs, install rsyslog

sudo # Run the command with superuser (administrator) privileges
apt-get # Linux package management command used to handle software packages
install # Option to install a specified package
rsyslog # System logging daemon package to collect and manage logs

(RPi) sudo apt-get install rsyslog

Start the rsyslog service

sudo # Run the command with superuser privileges
systemctl # Command to control systemd services
start # Option to start the specified service immediately
rsyslog # The logging service to be started

(RPi) sudo systemctl start rsyslog

Enable it when the system starts

sudo # Run the command with superuser privileges
systemctl # Command to control systemd services
enable # Option to configure the service to start automatically at boot
rsyslog # The logging service to be enabled

(RPi) sudo systemctl enable rsyslog

Review the logs

tail # Linux command to view the last lines of a file
/var/log/syslog # Path to the system log file where rsyslog writes messages

(RPi) tail /var/log/syslog

Configure SSH key-based authentication

The best practice is not to use a password, but key-based authentication

First, you need to create ssh-key (Enter the needed info)

ssh-keygen   # Generate a new SSH key pair (private + public) on the host machine

(Host) ssh-keygen

You need to copy the public key to the Raspberry Pi

cat # Command to display the contents of a file
~/.ssh/id_rsa.pub # Path to the host’s public SSH key
| # Pipe operator: send the output of the previous command to the next command
ssh # Command to connect to a remote machine via SSH
pi@jdoe.local # Remote username (pi) and host (jdoe.local)
‘ # Start of the remote command in single quotes
mkdir -p .ssh/ # On the remote machine: create the .ssh directory if it doesn’t exist (-p avoids errors)
&& # Logical AND: run next command only if previous succeeds
cat >> .ssh/authorized_keys # Append the incoming key to the authorized_keys file on the remote machine
‘ # End of the remote command

(Host) cat ~/.ssh/id_rsa.pub | ssh pi@jdoe.local 'mkdir -p .ssh/ && cat >> .ssh/authorized_keys'

or

ssh-copy-id # Utility to copy a public SSH key to a remote host’s authorized_keys
-i # Option to specify the identity (public key file) to copy
~/.ssh/id_rsa.pub # Path to the host’s public SSH key to copy
pi@jdoe.local # Remote username (pi) and host (jdoe.local)

(Host) ssh-copy-id -i ~/.ssh/id_rsa.pub pi@jdoe.local

ssh to the Pi

ssh # The SSH command used to securely connect to a remote machine
pi@jdoe.local # Specifies the remote username (pi) and hostname or IP address (jdoe.local)

(Host) ssh pi@jdoe.local

You need to re-configure sshd

sudo # Run the following command with superuser (root) privileges
nano # Opens the nano text editor in the terminal
/etc/ssh/sshd_config # Path to the SSH server configuration file on the Pi

(RPi) sudo nano /etc/ssh/sshd_config

Change the values of these (Remove # at the beginning) and save the file

PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

Now, you need to reload sshd service

sudo # Run the following command with superuser (root) privileges
systemctl # The systemd command to control system services
reload # Reload the service configuration without fully stopping the service
sshd # The SSH daemon (server) service

(RPi) sudo systemctl reload sshd

If you try to connect without a key, it will output Permission denied (publickey)

ssh # Command to connect to a remote machine via SSH
pi@jdoe.local # Remote username (pi) and hostname (jdoe.local)
-o # Option flag to specify a configuration setting for this SSH session
PubKeyAuthentication=no # Disable public key authentication for this session (forces password login)

(Host) ssh pi@jdoe.local -o PubKeyAuthentication=no

Current Connections

You can check current connections using the netstat command

sudo # Run the following command with superuser (root) privileges
netstat # Network statistics tool that shows network connections, routing tables, interface stats, etc.
-t # Show TCP connections only
-u # Show UDP connections only
-p # Show the PID and name of the program using each socket
-l # Show only listening sockets (services waiting for connections)
-a # Show all sockets (both listening and non-listening)
-n # Show numerical addresses instead of resolving hostnames

(RPi) sudo netstat -tuplan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      569/sshd: /usr/sbin 
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      1221/cupsd          
tcp6       0      0 :::22                   :::*                    LISTEN      569/sshd: /usr/sbin 
tcp6       0      0 ::1:631                 :::*                    LISTEN      1221/cupsd          
tcp6       0    476 2601:1d3:xxxx:xxxx:c:22 2601:1d3:xxxx:xxx:52976 ESTABLISHED 2246/sshd: pi [priv
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           357/avahi-daemon: r 
udp        0      0 0.0.0.0:68              0.0.0.0:*                           477/dhcpcd          
udp        0      0 0.0.0.0:631             0.0.0.0:*                           1223/cups-browsed   
udp        0      0 0.0.0.0:56974           0.0.0.0:*                           357/avahi-daemon: r 
udp6       0      0 :::5353                 :::*                                357/avahi-daemon: r 
udp6       0      0 :::47980                :::*                                357/avahi-daemon: r 
udp6       0      0 :::546                  :::*                                477/dhcpcd          

Disable a service

You can disable a service using the systemctl command. Let’s say that you want to stop, then disable the xyz service. So, you will use systemctl stop xyz, then systemctl disable xyz

systemctl # The systemd command to manage services, units, and dependencies
–reverse # Shows reverse dependencies: which units depend on the specified unit
list-dependencies # List all dependencies (or reverse dependencies with –reverse) for the specified unit
cups.* # Target unit(s): all services matching the pattern “cups.*” (CUPS printing services)

(RPi) systemctl --reverse list-dependencies cups.*
cups.socket
● ├─cups.service
● └─sockets.target
●   └─basic.target
●     └─multi-user.target
●       └─graphical.target

cups.service
● └─cups-browsed.service

cups.path
● ├─cups.service
● └─multi-user.target
●   └─graphical.target

Next

sudo # Run the following command with superuser (root) privileges
systemctl # The systemd command to manage services, units, and their states
stop # Stop the specified service(s) or unit(s) immediately
cups # Target unit/service (CUPS, Common Unix Printing System)
cups.service # Explicitly stop the main CUPS service unit
cups.socket # Stop the socket unit for CUPS (handles incoming connections)
cups.path # Stop any path-based triggers for CUPS

(RPi) sudo systemctl stop cups cups.service cups.socket cups.path

Then

sudo # Run the following command with superuser (root) privileges
systemctl # The systemd command to manage services, units, and their states
disable # Disable the specified unit(s) from starting automatically at boot
cups # Target unit/service (CUPS, Common Unix Printing System)
cups.service # Explicitly stop the main CUPS service unit
cups.socket # Stop the socket unit for CUPS (handles incoming connections)
cups.path # Stop any path-based triggers for CUPS

(Physical or VM) sudo systemctl disable cups cups.service cups.socket cups.path

Block a Port or IP

You can configure the host-based firewall ufw in Raspberry Pi OS, which is a frontend for iptables. ufw may need to be installed. Be extra careful, any mistake may block your connection. To check the list of apps, you can use sudo ufw app list

Example (Enabling only SSH)

Install the UFW and iptables

sudo # Run the following command with superuser (root) privileges
apt-get # Debian/Ubuntu package manager command (handles installing, updating, removing packages)
install # Tells apt-get to install the following package(s)
ufw # Installs UFW (Uncomplicated Firewall) for easier firewall management
iptables # Installs iptables, the low-level Linux firewall tool used by UFW and for custom rules

Deny all incoming connection to the system

sudo # Run the following command with superuser (root) privileges
ufw # Uncomplicated Firewall command-line tool
default # Modify default firewall policy
deny # Action to take on incoming connections (block)
incoming # Applies the action to incoming traffic

(RPi) sudo ufw default deny incoming

sudo # Run the following command with superuser (root) privileges
ufw # Uncomplicated Firewall command-line tool
default # Modify default firewall policy
allow # Permit traffic (action to take)
outgoing # Apply this action to outgoing connections

(RPi) sudo ufw default allow outgoing

sudo # Run the following command with superuser (root) privileges
ufw # Uncomplicated Firewall command-line tool
allow # Permit traffic (action to take)
ssh # Service name to allow (default TCP port 22, used for remote SSH connections)

(RPi) sudo ufw allow ssh

sudo # Run the following command with superuser (root) privileges
ufw # Uncomplicated Firewall command-line tool
enable # Turn on the firewall with all the currently defined rules

(RPi) sudo ufw enable

To check the UFW logs

sudo # Run the following command with superuser (root) privileges
ufw # Uncomplicated Firewall command-line tool
status # Display the current status of the firewall (active or inactive)
verbose # Show detailed information, including default policies and all configured rules

(RPi) sudo ufw status verbose

Then

sudo # Run the following command with superuser (root) privileges
less # Open a file in a scrollable, read-only pager (you can navigate with arrows, page up/down)
 /var/log/ufw* # Path to all UFW log files (the asterisk * includes ufw.log and any rotated logs like ufw.log.1)

(RPi) sudo less /var/log/ufw*

Disable Bluetooth (not applicable)

If the Bluetooth feature is not needed, you can disable it

sudo # Run the following command with superuser (root) privileges
echo # Print the text or string provided to standard output
“dtoverlay=disable-bt” # The text/string to append; in this case, a device tree overlay that disables Bluetooth
>> # Append operator: adds the output to the end of the specified file without overwriting it
/boot/config.txt # The target file where the string will be appended (Raspberry Pi boot configuration file)

(RPi) sudo echo "dtoverlay=disable-bt" >> /boot/config.txt

Disable WiFi (not applicable)

If the WiFi feature is not needed, you can disable it

sudo # Run the following command with superuser (root) privileges
echo # Print the text or string provided to standard output
“dtoverlay=disable-wifi” # The text/string to append; in this case, a device tree overlay that disables Wi-Fi
>> # Append operator: adds the output to the end of the specified file without overwriting it
/boot/config.txt # The target file where the string will be appended (Raspberry Pi boot configuration file)

(RPi) sudo echo "dtoverlay=disable-wifi" >> /boot/config.txt

Backup

If you want to backup a target folder, you can use the rsync command

rsync # Remote file synchronization tool for copying files and directories efficiently
-avzPi # Combined options: (a) Archive mode: preserves symbolic links, permissions, timestamps, etc. (v) Verbose: shows detailed output of what is being copied. (z) Compress: compress data during transfer for efficiency. (P) Partial + progress: keeps partially transferred files and shows progress. (i) Itemize changes: shows a summary of what changes were made to files
-e ssh # Specifies the remote shell to use (SSH) for secure transfer
pi@192.168.10.2:/home/ # Source: user ‘pi’ on host ‘192.168.10.2’, path ‘/home/’
/destination # Destination path on the local host where files will be copied

(Host) rsync -avzPi -e ssh pi@192.168.10.2:/home/ /destination

If you want to do a full backup, it’s recommended that you do it on a running system, not remotely (You can use the dd command with gzip).

To perform a full backup

sudo # Run the following command with superuser (root) privileges
dd # Disk dump tool: copies raw data from one location to another
if=/dev/mmcblk0 # Input file: the entire SD card (mmcblk0 is the main storage device for Raspberry Pi)
bs=4M # Block size: read/write 4 megabytes at a time (improves performance)
status=progress # Show ongoing progress while copying
| # Pipe operator: send the output of dd to the next command
gzip # Compress the raw disk image using gzip
> pi.gz # Redirect the compressed output to a file named pi.gz

(RPi) sudo dd if=/dev/mmcblk0 bs=4M status=progress | gzip > pi.gz 

To restore from a full backup

gunzip # Decompress gzip-compressed files
–stdout # Output decompressed data to standard output (do not create a file)
> pi.gz # Redirect standard output to pi.gz (this actually overwrites pi.gz instead of sending to dd)
| # Pipe operator: send output from left side to right side (does not work correctly with ‘> pi.gz’)
sudo # Run the following command with superuser (root) privileges
dd # Disk copy tool: writes raw data to a device
of=/dev/mmcblk0 # Output file: the SD card device
bs=4M # Block size: write 4 MB at a time
status=progress # Show ongoing progress while writing

(RPi) gunzip --stdout > pi.gz | sudo dd of=/dev/mmcblk0 bs=4M status=progress