Why is OOP and type safety good for security. But only if done right
Languages such as Java that are object-oriented and type safe can greatly enhance the security of apps and services. But only if the class design is done well.
I love OOP because it makes it possible to establish clear demarcations between security concepts. For example, between secured and unsecured messages, or between messages secured with different crypto algorithms. I also love strong typing, for it keeps the security demarcations firmly in place, at compile time.
A classic case study for how important these principles are is the growing array of implementations of the JOSE / JWT spec suite, the emerging standard for securing messages and tokens on the web. It defines three types of objects / tokens:
- unsecured (plain),
- HMAC-ed or signed, and
Security reviews, such as this one by Tim McLean, show the horrible things that can happen when the code fails to impose a clear distinction between crucial security entities or concepts:
Not making a typed distinction between plain and JWS entities may open a door to attackers to substitute plain tokens for signed ones.
Having untyped JWS verification may fool the app into accepting an HMAC JWS computed with a public RSA key.
That unsecured objects / tokens do not have a top-level spec on their own, but are treated as a special case of JSON Web Signatures does not help either, and is also a potential trap for the unwary implementer. The pace of Internet technology is such that imperfect specs are often the norm: standardisation efforts are being overtaken by wide scale adoption of early stage draft implementations, there is an ever expanding mesh of interdependent specs (not all perfect or complete) to consider, and the pressure of the busy schedules of those who actually drive these standardisation efforts inevitably takes its toll.
The wise developer accepts that specs may not be perfect, and designs for this too.
Our approach with the Nimbus JOSE + JWT library
has been just that -- each JOSE / JWT security type is given its own class, and
the spec's ambiguous delineation of
alg none and JWS proper is neutralised.
The application can define a method that expects only signed JWTs, and it is
guaranteed that a plain or encrypted one will not get into it.
Similarly, there are distinct verifiers or decryptors for each algorithm class. For example, there is a verifier class for RSA signatures only (RS256, RS384, RS512, PS256, PS384 and PS512), and its instances can be further limited to accept only a subset of the supported algorithms (e.g. only RS512).
JOSE objects in the Nimbus library also have three distinct states to let code that is handling them know whether they can be trusted or not as they are passed around the application:
- JWS states: unsigned, signed, verified
- JWE states: unencrypted, encrypted, decrypted
We consider strengthening the safety in this regard by making the state a type, rather than an instance variable. One could then define a method that accepts only verified signature JWTs and nothing else.
Image: Mr T CC BY-SA 2.0