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 the device being used 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 accessible 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, don't set it to be
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 mount points requiring you to create a node/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 mount point of an attached pathname space.
- Also, there is no exclusivity of mount points when file systems are unioned.
Use channel-based mandatory access controls to protect mount points from
registration. To protect them from client access, consider sandboxing them.
Using POSIX permissions and access control lists (ACLs) further up the tree
won't be enough for protection. Check the POSIX permissions and ACLs before
granting access to a client.
Users and authentication
If ssh is enabled, consider using public key
authentication instead of username and password based authentication. Favor rootless
systems.
Interprocess messaging
Best practices for interprocess messaging include the following:
- Mitigate risk with message authentication 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 replaces the shared library (or modified environment variables) to pick up a different libc and circumvent security features.
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. Strive to have no process
running as root.