Follow secure coding principles. While not specific to embedded applications, a good
place to start is the general software security coding practices maintained by the
Open Web Application Security Project.
Develop and maintain a secure architecture. Build security in. Make it a default, not an
option. Identify, reduce and harden attack surfaces. Favor simplicity in design to
reduce the attack surface area. Take care when handling open files to reduce attack
surfaces.
Network services
Best practices for securing network services include the following specific
measures:
- Keep ports closed unless keeping them open is essential to the service that the
system is providing. Enable only the services and ports that are necessary.
- Move services to non-standard ports to avoid detection from broad, sweeping service scans.
- Consider using port-knocking to open debug or management ports.
- Disable qconn (port 8000) and pdebug. Some BSPs are shipped with one or both
enabled.
- Avoid using FTP and Telnet services as they send user credentials in the clear.
Consider using SSH and SFTP or a VPN as secure alternatives to communicate with
the system.
- Keep libssl.so and other third-party libraries up to
date.
- Disable ping response to avoid detection by ping scans. Otherwise, use filters
that restrict or limit responses and provide configurable options to disable
this behavior temporarily while troubleshooting network issues.
- Disable broadcast ping response to avoid an attacker using the device as part of
a denial-of-service (DoS) attack.
- Review /etc/inetd.conf to determine which services are configured to run. Remove unnecessary services.
- Configure SSH with certificate authentication and disable password
authentication. For additional security, configure a non-network backchannel
(separately authenticated) to enable debug services and then use secure
authentication to access it. If you are using SSH and SFTP, use keys instead of
passwords where possible.
- Where possible, execute multiple io-pkt instances for isolating network interfaces.
- If you are using an embedded HTTP server (for example, to provide a thin client
interface to an application), disable port 80 and instead use HTTPS with port
443 (its default port). If you must keep port 80 open, set it up to
automatically redirect to the HTTPS site.
- Secure network endpoints.
System services
Secure system services. Enable the /dev/random service, and use it to provide a
source of secure random data.
Private and public channels
If a channel doesn't need to be accessed by other processes, make it private to
reduce the attack surface. Securing a channel keeps it private and reduces its
vulnerability to attack.
File systems and mount points
Best practices for securing file systems and mount points include the following:
- Pay attention to how you set ownership, file permissions, and access modes. For
example, if a file is not meant to be written to, make it non-writable.
- Mark all executables as non-writable.
- Be wary of executables that have the permission bits for setuid (set
user ID) or setgid (set group ID) set. While it may be a convenient way
of having non-privileged processes perform privileged actions, it may also allow
unintended actions.
- Many UNIX systems have mountpoints that require you to create a node or
directory in an underlying filesystem before mounting a new filesystem at that
point. However, QNX Neutrino does not. Don't rely on parent directory
permissions to protect the mountpoint of an attached pathname space.
- Also, there is no exclusivity of mountpoints when file systems are unioned.
Use channel-based mandatory access controls to protect mountpoints from
registration. To protect them from client access, consider sandboxing them.
Using POSIX permissions and access control lists (ACLs) further up the tree does
not protect them adequately. Check the POSIX permissions and ACLs before you
grant access to a client.
Users and authentication
If SSH is enabled, consider using public key
authentication instead of username- and password-based authentication. Where possible,
use rootless systems. (See
Rootless Execution.)
Interprocess messaging
Best practices for interprocess messaging include the following:
- Mitigate risk by authenticating messages between connected modules. Consider adding
a hash or signature to the payload and use it to verify integrity.
- Statically link the processes that are system critical in
libc to avoid situations where an agent can circumvent
security features by replacing the shared library (or modified environment
variables) with a different libc.
Trusted execution
Best practices for trusted execution include the following:
- Embed cryptographic keys in hardware to secure the root of trust.
- Boot securely on a trusted platform.
- Use an integrity-protected filesystem (for example, the Merkle filesystem).
- Follow the principle of least privilege to ensure that every process, user, and
program has access only to the information and resources that it needs. Use
procmgr abilities, mandatory access controls, and
security policy to enforce it.
Root privilege
Minimize the need to use and keep root privilege. Minimize all root processes. Ensure
that only the most essential tasks are running as root. Where possible, have
all processes running as non-root. (See Rootless Execution.)