Drama isn’t usual in the go community as far as I’m concerned and I wouldn’t call this incident itself a drama but it reminds me of instances that happen more than often on other technology stacks [citation omitted].
Security utilities are no joke, and no one in their sane mind would go about writing a custom RSA implementation in C++ for, say, their bank employer. People dedicate their entire careers writing this stuff and any modern platform or programming language has a security toolset for developing applications, and, otherwise, the open source solutions which are battle-tested by the community offer more guarantee that a one-man solution. Which brings us to today’s entry.
Go contains several official packages related to security but, to my surprise when I looked around, there was no canonical JWT implementation. I needed to do some token verification and introspection, and I decided to go with https://github.com/dgrijalva/jwt-go as it seems to be one of the most popular packages with an API that looked pretty ergonomic for my use case.
However, as of today it still has a security flaw: https://github.com/dgrijalva/jwt-go/issues/428
The repo is not maintained anymore (altough is stable aside from that issue) and the owner is nowhere to be found, or neither wants to be found. In the context of dependency management, this problem brings up some questions:
- Is it viable to use a fork at the cost of fragmentation?
- What happens to versions which were already audited by some process? a fork and new owner implies a full re-audit of the dependency?
- Why is google in the list of users of this package and didn’t bother to do a fork, or better yet, an official package like in the case of oauth?
Really interesting questions but for which I didn’t have the answers, or the time to find them as I wanted to finish my implementation. Moving to another, more interesting question, how much of the package would I need to copy or re-implement in order to fix this without doing a complete fork or having to move on to another package?
Not so tight, not so loose coupling
The basic workflow goes like this:
|
|
The issue is in the jwtgo.MapClaims
type, which contains the bug, but wait: the ParseWithClaims
method expects a Claims
parameter, which is an interface:
|
|
Extending
The So, knowing this, we can implement our custom type, namely a copy of the fixed implementation of the jwtgo.MapClaims type (for reference, see https://github.com/dgrijalva/jwt-go/pull/385). This type complies with Claims interface and can be extended to advanced validations, like required and optional claims:
|
|
I’d like to think there is a nicer way of doing the last method, so I will leave that up to the reader.
Wrapping up
With that, our initial code piece would end up like this:
|
|
Drama is avoided. Pending refactor is all that remains.
PS: Be nice and don´t forget to include the licenses belonging to the code you fork when applicable.