Secure coding is an essential topic for secure applications, and it’s one of the most important steps to make an app better for everyone. Without proper security measures in the code, significant damage can occur to systems, and systems can also become vulnerable to attacks.
This 101 guide is designed for beginners in programming and security, making it easy to get started with this series. I will update these articles from time to time, and when I complete this series, I will gather everything in one category to make it easy to access.
So Why We Need Secure Coding
That’s quite easy to answer. The most important reason is the reliability of applications, which is crucial for companies and individuals who use the app.
Day by day, our business, life, entertainment, and other activities are transforming into the virtual realm, making them increasingly vulnerable. As users, we naturally want better security for our data, just as we change the locks on our homes to prevent unauthorized access.
The most important practice for cybersecurity is secure coding from the start. Secure code reduces vulnerabilities and makes applications less likely to be exploited.
It sounds cool isn’t it? However, it’s important to mention that 100% secure code is not possible, even if you are planning to write a simple “Hello, World!” program (which can also be vulnerable). Our job is more about securing as much as possible. Like many other things, perfect security or perfect code has not yet been achieved, and it is unlikely to be achieved in the foreseeable future.
What is Input Exactly?
As I mentioned, I’m preparing all this documentation for beginners, so those with a background in programming may find this section boring. If that’s the case, feel free to skip this part without any worries.
Traditional programs take an input and produce an output using algorithms and these inputs. It’s a simple case, much like a mathematical function that receives an input and generates an output.
However, inputs should be checked carefully; otherwise, the code can generate unwanted behaviors such as crashing, security vulnerabilities, and data corruption. There are many cases that can arise in specific situations.
So, here’s what we can do to prevent this: we can calculate the possible ways to exploit inputs to make applications crash or bypass security solutions, and then address those vulnerabilities. We can test the code against different security scenarios and fix these problems to prepare for real-life attacks.
Testing Code? How?
I feel like some people have this question: how can I test my code for security? While there are several ways to do this, I want to focus on four different methods: static code analysis, dynamic code analysis, penetration testing, and unit testing.
Let’s start with static code analysis. As you can understand, it’s an analysis method that does not require running the code. With static analysis tools, you can identify potential vulnerabilities in the code before deploying your application, making it an easy and effective way to enhance security.
On the other hand, dynamic code analysis is the sister of static code analysis, with the key difference being that you are running the code. This method helps you catch vulnerabilities that occur during execution, such as memory leaks or buffer overflows. You can use tools to simulate various attacks, or you can focus on testing the application yourself to identify any new cases specific to your code.
Pen testing simulates real-world attacks on your application to identify vulnerabilities that could be exploited by attackers. Offensive security engineers typically conduct these tests to assess the security of the system or application, depending on the specific case.
Finally, we have unit testing. This method is all about making sure everything works smoothly without any issues. In simple terms, it helps us confirm that each part of the code does what it’s supposed to do, giving us confidence in the overall quality of the application.
Let’s Validate Inputs in C
I preferred using C for this article because it has no garbage collector, allowing us to manage memory without any restrictions. This makes it easier to demonstrate how to validate inputs effectively.
#include <stdio.h>
int main() {
int number;
printf("integer: ");
scanf("%d", &number);
printf("integer: %d\n", number);
return 0;
}
Here is our code! Let’s check what’s going on here. First, we are getting a number from the user and printing it on the screen. It seems quite straightforward.
Boom! This code is trash, to be honest. Let’s fix it. Note: I assume the reader has some knowledge about buffer overflow and C.
#include <stdio.h>
#include <stdlib.h>
int main() {
int num;
char buffer[20];
printf("integer: ");
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
char *endptr;
num = strtol(buffer, &endptr, 10);
if (endptr != buffer && *endptr == '\n') {
printf("integer: %d\n", num);
} else {
printf("Invalid input.\n");
}
} else {
printf("Please try again.\n");
}
return 0;
}
Now let’s check what we changed here. In the first program, we were expecting only integers from the user; however, the user could enter some strings, and at that moment, our program would crash due to a type mismatch so we fixed it!
fgets
reads a specified number of characters from the input stream and stores them in a buffer. This helps prevent buffer overflow. How does it work? It’s simple: if you set a buffer size before executing the code, users can’t exceed this limit, so there is no overflow at all!
There are many good features of fgets
, such as null termination (the string read by it ends with \0
, making it a valid C string in anyway!). It also handles new lines and adds them to the buffer to prevent buffer overflow in any case.
Summary of Input Validation Steps
There are many possibilities that you should check in your code to make it secure, but let’s create a list of what we learned from this example to establish a foundation for your secure coding skills.
Define your buffer size to ensure your input doesn’t cause a buffer overflow!
Handle errors if something goes wrong; don’t let the program crash—at least inform the user!
Use secure converters and make sure you convert the buffer securely!
Check the conversion result to end the program safely!
Everything is good! So, in our next project, we will ensure that we limit the buffer size for inputs, implement error handling systems, use functions that have security measures, and check the results after every process.
Conclusion
Securing code is really a hard job, huh? I agree with you, and in my opinion, there isn’t a perfect method to make your code secure. I think everyone has different styles and methods to achieve that.
Input validation is a really good start for secure coding because it requires low knowledge and addresses one of the most common vulnerabilities.
Always check for the easy things. Complex systems can be more secure than the simple parts. Yes, it’s kind of ironic, but people are more likely to make simple mistakes than hidden ones, and one simple mistake can cause big damage.
Check your inputs, buddy. Seriously.