Object-oriented thinking is often the starting point for many new developers. It is also where many stop their learning journeys when it comes to new code construction methods. JavaScript is an example of a language that many developers pick up in an unofficial manner, self-learning through ad-hoc Google searches for solutions that may potentially introduce vulnerabilities to your software.
Functional programming is a way to prevent this and enforces a level of structural integrity into the way you build your code.
But what is functional programming? Why does it matter? How does it protect your code against vulnerabilities? This is exactly what we’ll find out in this article.
What Is Functional Programming?
Functional programming is an idea where the process of building software rejects shared states, mutable data, and side effects. This is often done through composing pure functions and using declarative methodologies rather than imperative ones.
But what does this mean? Let’s break it down one concept at a time.
Shared State In A Nutshell
A shared state is when variables or an object sit in a shared scope. Take a look at the JavaScript example below:
In the code above, cartTotalX and cartTotalY sit in ashared space. This is because both their associated functions have access to it. The issue with this is that sequencing becomes crucial towards giving the correct answer. If the business rule requires you to exclude shipping before any discounts are applied, it means that the sequencing in cartTotalY returns an incorrect result.
Why Is Shared State A Vulnerability Issue?
When data sits in a shared state, it becomes easily accessible by methods and function calls. In the example above, a malicious user can exploit your cart discount processes, or if there are no back-stop or proper procedures in place, walk away with free items.
Alternatively, imagine you had a shared state for your app’s routing permissions. If a malicious user finds a vulnerability in one of the processes that has access to them, the results can compromise the security layer over accessibility.
Immutable Data In A Nutshell
Functional programmers advocate for immutability because it ensures that data isn’t impacted by side effects. But what is immutability?
Immutability in programming refers to an object, variable, function, or method being unchangeable once it is been assigned. In JavaScript, the most obvious way to achieve immutability is through const. You can also use freeze() to enforce immutability on an object that needs to be changed but eventually becomes unchangeable once it reaches a certain point. For example, if you had a cart price item, you may want to freeze the cart total after the final discount codes have been applied to stop it from getting exploited further down the process.
Why Is Mutability A Vulnerability Issue?
Mutability refers to objects, variables, functions, and methods that can be changed. A potential bug in the code can override your original intention, or result in a cascade of vulnerabilities from an expected change.
A feature of functional programming methodology is that everything you create is immutable. While data variables and objects may change, their shape shouldn’t also change. For example, if you’re expecting a cart object to look a certain way, it shouldn’t change if a malicious user sends through a modified object. In this case, the data shape may have been modified with an addition of unescaped scripts that will be called and used later in the checkout process.
Side Effects — In A Nutshell
A side effect in programming is when something changes a variable, object, function, or method from outside its scope. Side effects can occur in any programming language, making it a universal problem for code that doesn’t follow a functional approach.
The purpose of preventing side effects in code is to ensure that the intention of the programmer remains intact. This means that any future changes don’t impact how the variable, object, function, or method is consumed or operates. Immutability is one way to prevent side effects, along with pure functions and keeping your scopes private.
Why Are Side Effects A Security Issue?
A side effect can be viewed as a change on the data or structural integrity of a function, method, or object. In the context of programming, data may need to change. However, if side effects are minimized in other areas, it allows the developer to protect the data against unwanted changes or modifications that may come through code injections.
Side effects can also exist in seemingly innocent forms such as shared states and scopes, and functions and methods that use external variables to produce outputs — also known as impure functions.
Pure Functions — In A Nutshell
The purpose of a function() is to take inputs, also known as arguments, and produce an output, or the return value.
There are three main reasons why we use functions, and they are for:
- mapping: to produce an output based on the arguments passed in.
- procedures: a function that’s used to perform a specific sequence of steps.
- I/O: functions that are used to communicate with other parts of the system such as storage, system logs, or the network.
A pure function is when you give the value an input and it will always return the same output, and it has no side effects. It sounds simple, but it’s easy to introduce values that are susceptible to change such as shared scopes. This will make your function impure, and therefore prone to vulnerabilities.
Take a look at the example JavaScript code below:
While the code above looks innocent, it is prone to vulnerabilities because it uses a shared scope inside the function. This means that if cartTotal is compromised, the final checkout price displayed with taxes applied may be incorrect.
How Will Pure Functions Help Fix Your Vulnerability Issue?
Side effects happen when your function interacts with the outside world in some way. This means that it is reliant on external variables to work. If something goes wrong, then it can cause your function to also fail.
Using pure functions makes it easier to test and prevents unwanted external implications on your output. When debugging impure functions, you’re required to track and trace down all the potential side effects, and the side effects of those side effects, leading to a long vine-like code tracing exercise that can take time and has the potential for human error.
As a result, impure functions can lead to a domino effect if there is a bug in your application, resulting in a network of side effects traversing through the functions, methods, and objects it is connected to.
Declarative vs Imperative
Declarative programming is a process where the steps aren’t required to be in a particular sequence to return the expected result. In contrast, imperative programming relies on a series of steps executed in the correct sequence to return to the expected result. A change in the sequence will impact the outcome. In contrast, a declarative approach doesn’t require you to follow a particular sequence, allowing for more flexibility whilst guarding your outputs against unwanted side effects.
Functional programming uses declarative methodologies to help achieve its consistent returned outputs. In contrast, object-oriented programming is designed to support imperative thinking.
How Will Declarative Thinking Fix Your Vulnerability Issue?
When sequencing is eliminated, it reduces another potential node where failures in the code can occur. The fewer potential points of failure, the less vulnerable your applications will be to malicious users looking for vulnerabilities.
How Does Functional Programming Protect Your Code Against Vulnerabilities?
Functional programming is better at preventing future exposures to bugs and is easier to test than the object-oriented way. While it’s easy to believe that your software is secured when it is first created, changes and time can introduce new vulnerabilities.
The way functional programming works makes it easier to prevent the introduction of new vulnerabilities in the particular section of the software once it’s written, reviewed, and approved for production. The code may need to change structurally, but the movement of code won’t break it in the same capacity or severity as introducing changes to an object or moving it.
Object-oriented works like a web and can grow into a monolithic piece of code if it is unmonitored. Functional programming, however, works in smaller pieces of code by design, with distinct reasons for existing.
It’s good to also note that functional programming and object-oriented programming need to be used exclusively from each other. Functional programming ideas can exist inside objects, increasing the robustness of the object itself. Object size management is out of scope for this piece but is something that you should be aware of when creating extensions. Functional programming can provide entryways into testing code and keep your objects in check, making it easier to read the code and debug when your objects get too big.