Test for Potential Vulnerabilities
Another way to avoid vulnerabilities is to test for them. When an area of code may be problematic, such as accepting user input, it is possible to write tests that can check for obvious vulnerabilities and ensure that your code will not regress to a point where those vulnerabilities can be exploited in that code.
For instance, while we didn't care about sanitizing input that will never make its way to a user interface, we did care about how that data is stored. If we suspect that a particular part of our String handling may be vulnerable to a buffer overflow, we can write a test that will allow us to check how that code works with specific String length or content.
However, we obviously cannot reasonably expect to be able foresee all potential vulnerabilities which is why it is important to...
Allow for Updates
No matter how well thought-out a security practice may seem, there can be flaws. No system is perfect. It is important to be able to mitigate attacks before they can spread. Over-the-air firnware updates are a great solution for addressing bugs or vulnerabilities that are discovered after a product has shipped.
Arduino supports these OTA updates natively and they can be configured to allow the user to approve updates or to be applied automatically. NervesHub can also act as an Elixir OTA firmware update server for IoT devices.
In conclusion, the IoT development process can sometimes feel like the wild wild west. But these devices can be developed for securely when developers and product owners are willing to put in time and effort to release the best product possible.