This article is one of a collection of primer articles, providing a basic introduction to various topics, for reference in other more in-depth articles. These primer articles are written as people come to security pages from various backgrounds, be it a software engineer who wants to understand an issue they need to fix (or prevent one from occurring in the first place) or a penetration tester with no background in software engineering, who wants to understand and know how to exploit a particular issue.
This primer is regarding Object Orientation.
Object Orientation (OO) is a programming paradigm, a way of designing and developing software. It's emphasis is more on modelling the problem space than working closely with the computer. The OO paradigm itself is centred around, unsurprisingly, a concept called an object, and typically described as being built on of 3 main principles:
In reality, there are many more elements and concepts (e.g. reusability, 'the open closed principle'; but these are all related to these three principles and the objects themselves). As such, we shall start with objects and these three principles.
Objects are a set of logically grouped data and behaviour. A common example is that of the car, which could have data such as: Brand, Model, Colour, Engine etc, and then related functionality such as: Start the Engine, Accelerate, Brake, Change Gear, Stop Engine, etc.
Objects themselves are really instances of a Class (Or we could say they are all members of a set). A Class is therefore often said to be the 'blueprint' for objects, and as such it is the Class that contains the code that defines a given object.
Following on from the above example, in an OO program we would have a Class that defines the Car, with its various pieces of data and functionality.
The following skeleton C#.NET code shows what this might look like:
public class Car
{
// Data
public string Brand;
public string Model;
public string Colour;
// Functionality / Behaviour
public void StartEngine()
{
// Code here..
}
public void StopEngine()
{
// Code here..
}
public void Accelerate()
{
// Code here..
}
public void ChangeGear()
{
// Code here..
}
}
Objects, or Classes, can be composed of other objects, as you may have noticed, in the above sample, there is no data that refers to an Engine, we shall move onto this soon!
But first, we shall discuss Universal Modelling Language (UML). UML is one method for capturing the design of an OO-based system. Two key UML types are: Static Structure and Sequence. Whilst Sequence Diagrams describe the behaviour of OO-based systems, Static Structure Diagrams describe the classes and their relationships. The following diagram demonstrates how to describe the above Class using UML:
So, now we have that, how do we add an Engine to the system, or model? There are conceptually two different types of relationships like this, (1) Composition and (2) Aggregation. Composition is where one element is fundamentally made up of others, e.g. a relationship between a human and a brain, one cannot exist without the other. Whereas, aggregation describes less tightly bound relationships, e.g. where one element can exist without the other. In this instance, a Car can exist without an Engine (though, it may not drive so well), and an Engine can exist without a Car.
The following diagram shows this relationship:
The empty diamond indicates an aggregation, whereas filled-in diamond would indicate composition. The number indicates just how many CarEngines are associated with a given Car.
Now that we have the beginnings of the understandings of what an object and a class is, as well as how this can be coded and represented diagrammatically, we shall move onto the three common principles of OO: Encapsulation, Inheritance, and Polymorphism.
For behaviour, whilst other programming paradigms use procedures and/or functions, OO uses methods. Practically speaking (though somewhat dependant upon the language), these are quite similar to procedures and functions. A method either has a return type, or it does not, in which case the return type is indicated as void. A method without a return type is more similar to a procedure in some languages, whilst a method with a return type is more similar to a function in some languages. In the above code and UML diagrams, the methods are: StartEngine(), StopEngine(), Accelarate(), Brake(), and ChangeGear().
Finally, beyond UML it is worth noting that there are other methods for designing OO software, including the Z-Notation language, which is a mathematical way of defining software. This uses Pure Mathematics techniques to describe structures such as Classes and their functions (typically called Methods in OO). The Firesand Certified Secure Software Engineer training courses touch briefly on these methods.
Encapsulation really is the idea of encapsulating a specific concept into a class or object, i.e. the combining of the related data and functionality. In addition, it also includes the notion that much of the inner workings and design of a class is hidden from its external users. This enables the engineers of a class to rework the internal aspects (perhaps for efficiency, or even security reasons) without affecting any other classes.
This is typically managed by having a public interface, i.e. as per the examples above, all the elements were marked as public. These are the elements that are usable by a calling class. There may, however, be a vast array of private variables and methods that enable the entire object to function properly.
It is generally a good idea to keep the most minimal public interface possible, this limits coupling between classes/objects and thus reduces the impact of change.
Inheritance is a key component of OO, and one of the most overt ways that OO attempts to provide high levels of reusability. Inheritance allows us to specialise and generalise. Take, for example, the Car class that was designed above, functionality such as Accelerate(), as well as data such as Brand, Model, Colour, could all be abstracted away into a base class. This is because, these elements all apply to more than just cars, the apply to vehicles in general.
Thus, we could place the common elements and logic into a base class. From which, we could then derive different types of vehicles, beyond car, that all share those attributes. For example, a Bike class. Further still, we could abstract away the Engine element into a another base class called MotorVehicle.
The following UML diagram, expands upon the former, and presents such a model:
In this model, we now have to abstract base classes: Vehicle and MotorVehicle. Vehicle represents, generically, what it is to be a vehicle, data and behaviour (perhaps) common to all vehicles. We can say that this represents, mathematically, the total set of all possible vehicles. Whilst MotorVehicle represents, generally, what it is to be a MotorVehicle (i.e. a Vehicle that has an engine/motor!).
Inheritance, in UML, is represented by the line with the hollow arrow. In code, this is often indicated using a colon or a keyword such as extends. The following C#.NET code presents the above model:
public abstract class Vehicle
{
// Data
public string Brand;
public string Model;
public string Colour;
// Functionality / Behaviour
public void Accelerate()
{
// Code here..
}
public void ChangeGear()
{
// Code here..
}
}
public abstract class MotorVehicle : Vehicle
{
// Functionality / Behaviour
public void StartEngine()
{
// Code here..
}
public void StopEngine()
{
// Code here..
}
public MotorEngine Engine;
}
public class MotorEngine
{
// Data
public int NoOfCylinders;
public string Manufacturer;
}
public class Car : MotorVehicle
{
// Data
public Seat[] Seats;
}
public class Motorboat : MotorVehicle
{
// Data
public Rudder Rudder;
}
public class Bike : Vehicle
{
// Data
public Seat Seat;
public Handlebar Handlebar;
}
public class Handlebar
{
// Code here
}
public class Rudder
{
// Code here
}
public class Seat
{
// Data
public string Material;
public string Colour;
}
Based on the code above, an instance of a Car would automatically gain all the properties and behaviour of the Car class, but also the MotorVehicle, and ultimately the Vehicle class.
This ensures that the logic for Vehicle and MotorVehicle only need to be written once. The system, can now easily be extended to add in further Vehicle types. e.g. Unicycle, or any other type of vehicle, without having to rewrite all the code that had already been written for Vehicle or MotorVehicle.
This is another key component of reusability within the OO world! This enables the development of highly reusable algorithms. It can be built using inheritance, but also other methods such as something termed 'interfaces' in some programming languages.
We shall focus in on inheritance based polymorphism. Polymorphism literally means "many forms", and is a method by which - in OO - we can design a generic or common algorithm that works with a range of different objects. I.e. an algorithm can be written today, that continues to work for new programming types/classes tomorrow, next year, or the further in the future. Algorithms that allow for this are also said to support the Open-Closed Principle, where the algorithm is fixed (i.e. closed), yet is simultaneously open to change (i.e. open)!
To illustrate this, we can expand upon the prior vehicle-based example, let us suppose that there is a Garage class that can repair Cars, we can represent this using UML, as per the following:
Here we have now introduced a Garage class which offers a Repair method. This Method expects an instance of the Car class to be passed in. This is all well and good, but, ideally we do not want to have to write a different repair algorithm for every vehicle type, on the assumption that we can do the same thing (programmatically that is) for different types of vehicles.
As such, what OO allows us to do is to design algorithms that work on base classes, e.g. in this case it could be either MotorVehicle or Vehicle. Thus the Repair method could be written to support any and all MotorVehicles or any and all Vehicles. Simply by changing the type from Car to one of these. Obviously, the algorithm needs to truly work at a generic level!
What this then provides us, is the ability to write this Repair algorithm once, and have it function for all the existing Vehicle based types, and any future types that also inherit (or derive) from Vehicle.
So, we ultimately end-up with the following (nearly identical) design, with huge ramifications for reusability:
A key component of Polymorphism is that the one algorithm can ultimately behave differently based on the types passed in. Thus, to ensure this model does truly represent polymorphism, we will assume that the Repair method tests the vehicle, calling the Accelerate method on the provided vehicle, and we shall then assume that subclasses (Bike, Car, and Motorboat) have modified the behaviour of the Accelerate. Thus, when the Garage's Repair method executes the Accelerate method, the specific behaviour is different, without any change having been made to the Garage's Repair method itself.
OO allows software engineers and architects to design highly reusable code, in a very organised manner that is predominantly focussed on modelling the problem-space.
It is a powerful programming paradigm, and can be used to produce robust software, especially when combined with the concepts of asserts, pre-conditions, and post-conditions (which are covered in Firesand's Certified Secure Software Engineer training course.
Note, this has been a whistle-stop tour of and introduction to Object Orientation, there are many concepts, techniques, etc not covered. But, the hope is that this has provides a better understanding of what Object Orientation is! As, it is useful for security professionals to understand what they are either securing, or trying to break!
Cookie Notice
We use cookies to ensure that we give you the best experience on our website. Please confirm you are happy to continue.