Security for Engineers¶
Many of us have the need to develop code, code that may have access to very sensitive data, or code that has the ability to wreck havoc on an environment. Whatever the development requirement is, there is a need to be vigilant with the code you develop, and ensuring that you do not inadvertently introduce security issues that could otherwise have been avoided.
Be Brilliant at the basics¶
Many security breaches occur because basic security processes are not followed. Before you even think about how to develop securely, is your own house in order? Run through this check list to make sure you've got the right controls in place.
Keep your system up to date¶
A lot of breaches occur simply because the engineer did not patch their system. There's no excuse - every modern system today has the ability to have auto-update of software enabled. If you haven't done it yet, turn it on, and remember to reboot your computer once in a while for those updates to take effect.
And don't forget your your 3rd party tools. That Google Chrome extension is probably not going to update itself, so if you are using any 3rd party tools, make sure you keep those also up to date.
Run an anti-virus / anti-malware tool¶
Running an anti-malware tool on your workstation offers numerous benefits that are essential for maintaining a secure and efficient computing environment. Firstly, it provides robust protection against various forms of malware, including viruses, trojans, worms, and spyware. By regularly scanning and detecting these malicious programs, an anti-malware tool prevents them from infiltrating your system and compromising sensitive data or causing system malfunctions. Additionally, it helps safeguard your privacy by detecting and eliminating keyloggers, adware, and other intrusive software that may track your online activities. This protection extends to your email as well, as the tool can identify and remove malicious attachments or links that may lead to phishing attacks. Overall, employing an anti-malware tool ensures the integrity and security of your workstation, allowing you to work with peace of mind and uninterrupted productivity.
Enable your local firewall¶
Most often you're working from home where your work laptop is connected to the same network where your kids are downloading games on a regular basis. Any malware on your network could attempt to compromise your work computer as well. By enabling your local firewall, you are reducing the chances of malware on the neighbouring computers from infecting your work machine.
- Block connections to your Mac with a firewall
- Turn Microsoft Defender Firewall on or off - Microsoft Support
Encrypt your hard drive¶
If you lost this computer today, would the individual who took your computer be able to consume your content (keeping in mind the password locking the computer is not a deterrent, since anyone with access to the hard drive can bypass it)
Encrypt your hard drive. It's a simple mechanism to secure the content in the event of a lost device.
Use strong password policies¶
Maintaining a good personal password policy brings significant benefits when it comes to protecting your online accounts and sensitive information.
- Avoiding password reuse is crucial. By using unique passwords for each account, you minimize the risk of a single data breach compromising multiple accounts. This practice ensures that even if one account is compromised, your other accounts remain secure.
- Utilizing password managers is highly recommended. These tools generate strong, complex passwords and securely store them, eliminating the need to remember multiple passwords. They also provide convenient autofill features, saving time and reducing the likelihood of using weak or easily guessable passwords.
- Lastly, it is essential never to share your passwords with anyone else. Sharing passwords weakens the security of your accounts, as you cannot control how others handle and protect that information.
By adhering to a good personal password policy, you significantly enhance the security of your online presence, safeguarding your personal and financial data from unauthorized access.
Consider signing up to the free service at Have I Been Pwned so you're alerted when your password may have been exposed in a data breach.
Hhere are some suggestions for Password Managers:
Multi-factor all the way¶
Most services offer the ability add a 2nd factor to your authentication. Username and password alone is not good enough to secure a system. Whether you use Google Authenticator, SMS, or a Yubikey, if your service allows you to use multi-factor authentication, use it.
Use cloud storage¶
If you lost this computer today, would you still be able to continue working? Will all the code you've developed be gone? If so, you will need to start adopting some devops practices, and store all your code in Github / GitLab, or some other code management repository.
Developing locally¶
You probably have your system at home, complete with VSCode (or any other development tool of choice) installed, and good to go.
Storing and consuming credentials¶
In many use cases, there is a need to connect to some remote system via an API call. I've heard developers use the argument that their laptop is secure, encrypted, patched, and the credentials cannot be accessed.
While there is a point to be made about encrypting credentials locally, it is most often a practice that is difficult to sustain in the long run.
Consuming credentials in code¶
For a quick test, you decide to do a test of an API. You write a quick little python script to test the API.
import requests
token = "1234567890"
result = requests.get("https://my.secret.api.com/makeacall.php",headers = { "Authorization" : f"Bearer {token}"}).text
You are quite happy with the result - the script works, and you start expanding on it. In this case however, the cardinal rule was broken…
Do not EVER hard code credentials in code, not even for testing purposes.
This is a bad idea because:
- Sensitive credentials are stored in clear-text - it can easily be read and consumed a 3rd party. If your computer is compromised with some malware, it could potentially read the files to harvest the credentials.
- Test scripts often evolve into production scripts, with little regard to where the credentials are stored, exposing the credentials to a wider audience than was initially intended.
- Tried that git commit yet? You probably committed the API key to git's history, and it will forever stay there.
Solution 1 - Use temporary credentials¶
Some systems would allow you to log on, and then issue you temporary credentials. These credentials will be short lift (a few minutes to a few hours). It may be a bit inconvenient, but it is a good way to ensure those credentials do not make their way into your source code.
Many of us use AWS on a daily basis, and while using AWS, there may be credentials stored on our local computer. This is not a good idea.
$ cat ~/.aws/credentials
[default]
aws_access_key_id = ASIA1234567890ABCDEF
aws_secret_access_key = abcdefghijklmnopqrstuvwxyz0123456789ABC
Configure the AWS CLI with IAM Identity Center authentication - AWS Command Line Interface
Solution 2 - Use AWS Parameter Store¶
By using the SSM Parameter store, the credentials are in an external repository, protected by AWS IAM credentials. For you to consume it, you will have to authenticate to the AWS environment.
import boto3
import json
def readSSM(region_name,parameter_name):
return json.loads(boto3.client('ssm',region_name=region_name).get_parameter(Name=parameter_name,WithDecryption=True)['Parameter']['Value'])
Solution 3 - Use environment variables¶
It can be argued that the use of environment variables is not ideal, however in an isolated system, it is highly unlikely for an attacker to consume credentials from environment variables, so it is an acceptable practice. Solutions like AWS Secrets Manager also allows the consumption of secrets via environment variables in an easy way, without exposing the credentials to the developer.
Do NOT use the command line to pass credentials. Command lines can be snooped with a simple
ps -ef
command, exposing the commands to anyone (or anything) with access to the system.
Least Privilege¶
You've been given an API key to perform some activity. How much power does this key have? Do you really need to have the permissions that's been granted to this API key?
When thinking of least privilege, ask your self the question:
If this API key were to be leaked, how much damage could an adversary do with this key?
As a best practice, ensure that your API key has the minimum amount of permissions. We hope the credentials never get leaked, but if they did, the amount of damage that can be inflicted by an attacker should be reduced significantly.
Using production data locally¶
Using production data on a development system is widely regarded as a bad idea due to several compelling reasons.
- Production data often contains sensitive and confidential information, including personally identifiable information (PII), financial records, or proprietary business data. Exposing this data to a development environment poses significant risks, such as data breaches, unauthorized access, or accidental leaks.
- Development systems typically have looser security measures and may not provide the same level of protection as production environments. This discrepancy increases the chances of data exposure and compromises system integrity.
- Development systems are frequently used for testing and experimentation, which could involve modifications, deletions, or corruption of data. Accidentally altering or destroying production data can lead to severe consequences, including financial losses, legal ramifications, and damage to a company's reputation.
In summary, it is crucial to maintain a clear separation between production and development environments to uphold data security, compliance, and the overall stability of systems.
Commit your code¶
Using a source code repository like GitHub or GitLab and committing code regularly is of paramount importance in software development.
A source code repository acts as a centralized hub for storing and version-controlling code. It enables developers to track changes, collaborate effectively, and revert to previous versions if needed. By committing code to a repository, developers create a permanent record of their work, preserving a historical timeline of changes and facilitating collaboration among team members.
Repositories offer robust backup and disaster recovery mechanisms. Should a local machine or storage device fail, the code and its entire revision history remain safely stored in the repository. This ensures that valuable work is not lost and can be easily restored.
Source code repositories enable seamless collaboration. Multiple developers can work on the same project simultaneously, making independent changes to their local copies and then merging them into a unified codebase. The repository tracks these changes and provides tools for resolving conflicts, ensuring a smooth and organized development process.
Using a source code repository also promotes code quality and best practices. Developers can review each other's code, provide feedback, and suggest improvements through the repository's built-in features like pull requests and code reviews. This collaborative approach leads to better code quality, identification of bugs or vulnerabilities, and the adoption of coding standards across the team.
Overall, utilizing a source code repository and committing code regularly brings numerous benefits, including version control, collaboration, backup, disaster recovery, and code quality improvement. It is a fundamental practice in modern software development, enabling efficient and organized workflows while ensuring the integrity and reliability of the codebase.
Running remotely¶
Protect that API¶
Use HTTPs - ALWAYS¶
HTTPS (Hypertext Transfer Protocol Secure) ensures secure communication between the API server and clients by encrypting data in transit. This encryption prevents unauthorized parties from intercepting and accessing sensitive information transmitted over the network, such as authentication credentials or user data. By implementing HTTPS, developers can establish a secure channel that safeguards the privacy and integrity of the data being transmitted.
Secondly, HTTPS provides authentication and verification mechanisms through the use of digital certificates. These certificates validate the identity of the API server, ensuring that clients are connecting to the intended and trusted endpoint. This helps prevent man-in-the-middle attacks and protects against impersonation or tampering of the API communication.
Furthermore, using HTTPS is crucial for compliance with industry standards and regulations. Many privacy and data protection laws, such as the General Data Protection Regulation (GDPR), require the use of secure communication protocols when handling personal data. Adhering to these standards not only helps to meet legal obligations but also enhances user trust and confidence in the API and the service it provides.
Authenticate¶
When developing an authentication mechanism for an API, using temporary credentials is often considered superior to bearer tokens for several reasons. Firstly, temporary credentials have a limited lifespan, typically expiring after a set period. This built-in expiration promotes better security by reducing the risk of long-term token exposure and potential misuse. In contrast, bearer tokens, once obtained, can be used indefinitely unless explicitly revoked or expired.
Secondly, temporary credentials can be more easily revoked or invalidated. If suspicious activity is detected or if a user's privileges change, revoking a temporary credential is straightforward. This ability to quickly revoke access enhances security and allows for better control over user access rights. In contrast, bearer tokens can be challenging to revoke once issued, as they are typically valid until their expiration time or manually revoked by the user.
Furthermore, temporary credentials can be tied to specific scopes or permissions, limiting the access granted to the API. This granularity ensures that users are only granted the necessary privileges for their intended actions. Bearer tokens, on the other hand, often provide unrestricted access to all resources associated with the token, increasing the potential impact if compromised.
Lastly, temporary credentials are typically issued for a specific purpose or session, which can be useful for auditing and logging purposes. These credentials can be associated with specific actions, timestamps, or user sessions, allowing for better traceability and accountability.
Avoid this pattern ❌¶
import requests
token = <retrieve token from credential store>
# Make API call to retrieve data
myData = requests.post(
'https://myurl/api.php',
headers = { "Authorization" : f"Bearer {token}"}).text
Recommended Pattern ✅¶
import requests
token = <retrieve token or credentials from credential store>
# Log on to obtain the temporary credentials
temporaryToken = requests.post(
'https://myurl/login.php',
headers = { "Authorization" : f"Bearer {token}"}).text
# Make API call to retrieve data
myData = requests.post(
'https://myurl/api.php',
headers = { "Authorization" : f"Bearer {temporaryToken}"}).text
Never leave an API with access to sensitive data or actions exposed without authentication, even just for testing. The Optus breach in 2022 demonstrated this exact point.
Rate Limit¶
Building a rate limit feature into an API is a smart decision with numerous benefits. Firstly, rate limiting helps protect the API server from abuse and malicious activities. By setting limits on the number of requests that can be made within a specific time frame, it prevents excessive or aggressive usage that could overload the server or compromise its performance. This safeguard ensures fair and equitable access for all users while maintaining the stability and availability of the API.
Secondly, rate limiting helps mitigate the risk of Distributed Denial of Service (DDoS) attacks. By restricting the number of requests from a single source or IP address, it becomes more challenging for attackers to overwhelm the API server with a flood of requests, thus enhancing its resilience against such attacks.
Furthermore, rate limiting promotes better resource allocation and usage optimization. By controlling the frequency and volume of requests, it allows the API server to allocate its resources more efficiently and serve a larger number of users simultaneously. This ensures a consistent and reliable experience for all API consumers while avoiding unnecessary strain on the server infrastructure.
Additionally, implementing rate limits can encourage the development of more efficient and optimized client applications. Developers are encouraged to design their applications to make the most of each request, reducing unnecessary calls and optimizing data retrieval and processing. This leads to improved performance, reduced bandwidth consumption, and overall better user experiences.
Lastly, rate limiting helps protect against accidental or unintentional misuse of the API. It prevents scenarios where an application or user unknowingly generates a high volume of requests due to software bugs or misconfigurations. By enforcing limits, the API provider can identify and address such issues promptly, minimizing their impact on the server and other users.
Restrict IP Address Ranges¶
Restricting IP addresses that can access an API can be a beneficial practice in certain scenarios, although it may not be suitable for every use case. Implementing IP address restrictions adds an extra layer of security by allowing only authorized clients or networks to access the API. This approach can be particularly valuable when the API serves sensitive or confidential information, requires strict access control, or operates within a limited trusted network environment. By limiting access to specific IP addresses, the API provider can reduce the surface area for potential attacks and unauthorized access attempts.
However, it's important to acknowledge that IP address restrictions may not be suitable for all situations. In cases where the API is intended for public consumption, enforcing IP restrictions can hinder accessibility and limit the reach of the API. If the API serves a diverse set of users or needs to be accessed from various locations, such as in a mobile application or a cloud-based service, IP restrictions may not align with the desired use case.
Moreover, IP addresses can be easily spoofed or manipulated, rendering IP restrictions less effective against determined attackers. It's essential to complement IP restrictions with other robust security measures, such as authentication and encryption, to ensure a comprehensive defense against unauthorized access and data breaches.