In this tutorial, I will explain the security issues when working with remote collaborators on projects that require any kind of credentials such as WiFi or any type of confidential information. I will also explain some recommended security layers as well as the solution of best practice recommendations. Additionally, I will show you how to securely handle credentials in development environments and IoT devices.
🔧 Initial Problem
When working on projects with remote collaborators, it is common to share code without exposing sensitive credentials, such as WiFi network credentials. A common mistake is including these credentials in the source code (this sometimes happens because development or testing is faster), but it is a terrible security practice:
const char* DEFAULT_WIFI_SSID = "RedPrivada";
const char* DEFAULT_WIFI_PASSWORD = "ClaveSecreta123";
🚨 Problems with this practice
- Credential leakage: If we upload the code to GitHub, anyone with access to the repository can see the network and password.
- Difficulty changing credentials: Any change requires modifying the code, recompiling, and uploading the firmware again.
- Not scalable: If multiple IoT devices require access, each one needs its own unique configuration.
🚀 Implemented Solution
To solve these problems, a dual approach is chosen:
- During development (IoT device connected to the computer): Use environment variables with
getenv()
to load credentials. - When the IoT device operates independently: Use a
secrets.h
file, which should be ignored with .gitignore so it is not uploaded to GitHub.
🔨 Secure Code Implementation
1. Setting Environment Variables on the Computer
To keep credentials out of the source code, they should be stored in environment variables.
On macOS/Linux, add the variables in ~/.zshrc
(or ~/.bashrc
on other systems):
echo 'export WIFI_SSID="RedPrivada"' >> ~/.zshrc
echo 'export WIFI_PASSWORD="ClaveSecreta123"' >> ~/.zshrc
source ~/.zshrc
On Windows (PowerShell):
$env:WIFI_SSID = "RedPrivada"
$env:WIFI_PASSWORD = "ClaveSecreta123"
This way, the code can obtain the values without exposing them on GitHub.
2. Creating the secrets.h
File for Use on IoT Devices
For the IoT device to connect to WiFi when not connected to the computer, a secrets.h
file must be created:
#ifndef SECRETS_H
#define SECRETS_H
#define WIFI_SSID "RedPrivada"
#define WIFI_PASSWORD "ClaveSecreta123"
#endif
This file is not uploaded to GitHub because it is added to .gitignore
:
echo "secrets.h" >> .gitignore
Thus, secrets.h
will be present only on the local computer and the IoT device, but never in the repository.
3. Modifying the Code to Use Both Approaches
Now, the code checks if environment variables exist and if not found, uses the values from secrets.h
:
#include "wifi_module.h"
#include "secrets.h" // Includes credentials only for standalone IoT device
#include <cstdlib> // Needed for getenv()
const char* DEFAULT_WIFI_SSID = getenv("WIFI_SSID") ? getenv("WIFI_SSID") : WIFI_SSID;
const char* DEFAULT_WIFI_PASSWORD = getenv("WIFI_PASSWORD") ? getenv("WIFI_PASSWORD") : WIFI_PASSWORD;
void initWiFi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(DEFAULT_WIFI_SSID, DEFAULT_WIFI_PASSWORD);
int wifi_connect_tries = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
wifi_connect_tries++;
if (wifi_connect_tries >= 10) {
Serial.println("\nFailed to connect to WiFi");
break;
}
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nConnected to WiFi.");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}
}
🔒 Security Assessment
Now, the solution protects credentials in different scenarios:
Scenario | Risk | Implemented Security |
---|---|---|
GitHub Repository | Anyone could see the credentials | Credentials are no longer in the code, they use getenv() and secrets.h is in .gitignore . |
Freelancer accesses the repository | Could see credentials | Cannot see secrets.h , needs to set up their own environment. |
Someone steals the IoT device | Could extract firmware to see credentials | If Flash encryption is configured on the IoT device, memory will be inaccessible. |
Compromised computer | Environment variables could be exposed | Could be improved with a more secure secrets manager. |
🚀 Conclusion
Now, the project is secure and scalable:
- During development, use environment variables to avoid exposing credentials.
- When the IoT device is standalone, use
secrets.h
(which is not on GitHub). - The risk of exposing credentials on GitHub has been eliminated.
- The remote collaborator can work without seeing the real credentials.
This method allows sharing code with collaborators without compromising security. 🔒🌟
🚀 You can now use this same method to protect your IoT projects or any other environment where sensitive credentials are handled! 🚀