In discussing the Aeson
Object design, I pointed
out that using a
[(Text, Value)] representation could
even help with mitigation of the denial-of-service vulnerability that is
currently being addressed:
In some cases, it would even be possible to use the list (better than
Vectorfor conversion) length to mitigate attacks. For example, consider an API that uses 10 properties. If the length of the list is above a certain threshold (10 for a strict API), an error could be returned. Otherwise, the
HashMapcould be constructed and used as usual. This would allow users to mitigate attacks while keeping the performance of
HashMap, with only the additional cost of determining the length of the list (one time
O(n) at the end, but note that the
n here is the threshold, not the length of the
list. We only need to count up to the threshold to determine the result
of the predicate; there is no need to compute the actual length of the
This function is usually called
lengthExceeds, and Hoogle
makes it easy to see which packages define it.
lengthExceeds function could be implemented as
not . lengthAtMost.
The hedgehog testing
library includes the following function, also implemented using
length xs >= n
I prefer Henning’s implementation, which uses
n <= 0
n == 0.
The following functions are included in ghc:
length xs >= n
length xs <= n
length xs > n
length xs /= n
length xs == n
length xs < n
These functions are implemented the following function, not
atLength :: ([a] -> b) -- Called when length ls >= n, passed (drop n ls) -- NB: arg passed to this function may be  -> b -- Called when length ls < n -> [a] -> Int -> b
Implementing this function using a tail-recursive helper function would be a good exercise for somebody learning Haskell!
If Aeson were to use the list representation of
lengthExceeds function could be added to Aeson so that
additional dependencies are not required.
-- | Check if a list is longer than the specified length -- -- (lengthExceeds n xs) = (length xs > n) lengthExceeds :: Int -> [a] -> Bool lengthExceeds n| n < 0 = const False | otherwise = not . null . drop n
2 entries) $ fail "Coord object has too many properties"when (lengthExceeds
In the common case where the entry list is only used for this check
before constructing as
HashMap, a helper function can be
withMaxLength :: Int -> (ObjectEntries -> Parser a) -> ObjectEntries -> Parser a = do withMaxLength n f entries $ fail "object has too many properties" when (lengthExceeds n entries) f entries
The example instance definition could be written to use this function to mitigate attacks as follows:
instance FromJSON Coord where = withObjectEntries "Coord" . withMaxLength 2 . asHashMap $ parseJSON -> Coord \v <$> v .: "x" <*> v .: "y"
With this implementation, the error message is not very helpful. Sometimes is is preferred to not give helpful error messages when dealing with security issues, because helpful error messages help attackers. In cases where a more helpful error message is desired, however, a function like the following may be preferred to handle the common case:
withObjectOfMaxLength :: String -> Int -> (HashMap Text Value -> Parser a) -> Value -> Parser a
This function checks that the value is an object, makes sure that the
object does not have too many properties, and constructs the
HashMap. It can be used as follows:
= withObjectOfMaxLength "Coord" 2 $ \v -> Coord example <$> v .: "x" <*> v .: "y"
This demonstrates how the design can be used to mitigate the
denial-of-service attack and still provide the performance of