Introduction
If you have watched the news in the past 3 years, you will have seen the massive conversation about cybersecurity, data breaches, and insecure coding practices. Code with relatively common vulnerabilities such as buffer overflow, input validation errors, and broken access control is frequently a part of popular software. This is a serious concern with such software being used in critical infrastructure, by defense contractors, and in other high-security use cases.
While the problem is not necessarily hard to fix, it does require unlearning intrinsic coding practices and relearning new methods to develop secure software ((which can be difficult). The practices outlined in this article are a subset of programming principles known as “defensive programming”. In this article, I will outline defensive programming practices with respect to Python.
What is defensive programming?
Defensive programming is a term for the collection of best practices that are necessary to develop and maintain secure code. Defensive programming prevents software vulnerabilities by being proactive with development, rather than reactive.
Some of these practices include:
- Proper use of white space
- Proper use of comments
- Input validation
- Memory management
- Zero Trust
The goal of defensive programming is to develop secure, maintainable, and readable code. Code reading is an essential skill for any developer, and it is made significantly more challenging with poorly commented code with improper use of white space.
Take this line of code for example:
[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
The above line of code is significantly more concise than this code:
# Create List my_list = [] for x in [1,2,3]: # Outer for loop for y in [3,1,4]: # Inner for loop if x != y: # Evaluate if x is equal to y my_list.append((x, y)) # If x is not equal to y append both
But it is not as easy to read. Python does, however, have defensive programming principles intrinsically built in. Python forces the developer to use white space effectively, thus creating readability.
Input validation
In my opinion, the most important principle in defensive programming is input validation. By miles. Without proper input validation, code is subject to injection attacks, which took the number 3 spot on the OWASP 2021 Top Ten list. While buffer overflow has become less common (so much so that it is no longer listed on the OWASP Top Ten), injection attacks are still extremely common. OWASP states, “94% of the applications were tested for some form of injection, and the 33 CWEs mapped into this category have the second most occurrences in applications”.
This is to say that input validation is of utmost importance in any user-facing application. Input validation can be implemented using pattern-matching techniques such as regex or newer utilities such as machine learning. One could even write a polymorphic program that updates regular expressions embedded in an application when an injection attempt is made thereby blocking any subsequent attempts to inject invalid input.
An example of proper input validation in python is:
# Sample pattern matching program # Noah Caldwell # Import RegEx dependencies import re # Get password from user and store in variable password = input("Create password with 12 characters " + "with at least 1 uppercase letter, 1 lowercase letter " + "and one special character: ") # Check if password matches doesMatch = re.search("^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$", password) if doesMatch: print("Yay! You met the requirements!") else: print("Please try again!")
Using input validation in addition to other defensive programming principles will help developers build and maintain secure code by design and will further contribute to reducing data breaches.