We use hash validation to prevent cookie tampering. I've been tracking false positives -- cookies in the request that fail validation but show no signs of tampering -- and typically we see a handful of failures per hour. However, we have also seen our sites experience periods where every request fails the hash check. We're in an extended period like that right now, during which time I've been hitting the sites under my identity, and I see all of my security tokens failing the hash check. I'm pretty sure my evil twin in the universe with the bearded Spock has not been altering my token. Mostly sure.
My network gurus have assured me that the machine keys match on all servers in the cluster, and there is nothing in any log to indicate an environmental change. I've now seen 4 periods of massive failures that go away as mysteriously as the show up. They last several days. We see no indication of any kind of attack.
Our code lives in a back-end WCF service since we're validating the hash only when a site member request needs our services. In the hashing function below, _buffer is a thread-static variable.
I'm hoping some kind soul can see something in our code or can tell me where to look for a solution. We're out of ideas.
Private Shared Function GetHash(ByVal token As Protocol.Token) As Byte()
If _buffer Is Nothing Then
' 8 bytes of time
' 16 bytes of user id
' 30 bytes of screen name
' 16 bytes of site id
' 16 bytes of secret
' 1 byte of isauth
' = 87 bytes
_buffer = Array.CreateInstance(GetType(Byte), 87)
' copy the secret to the end of the array, this never changes
Array.Copy(_secret.ToByteArray(), 0, _buffer, 70, 16)
End If
' copy time to the first 8 bytes
Dim time As Long = token.Created.Ticks
_buffer(0) = time And &HFF
_buffer(1) = (time >> &O10) And &HFF
_buffer(2) = (time >> &O20) And &HFF
_buffer(3) = (time >> &O30) And &HFF
_buffer(4) = (time >> &O40) And &HFF
_buffer(5) = (time >> &O50) And &HFF
_buffer(6) = (time >> &O60) And &HFF
_buffer(7) = (time >> &O70) And &HFF
' copy the user id to the next 16 bytes
Dim userid As Guid = Guid.Empty
If token.Identity IsNot Nothing Then userid = token.Identity.UserId
userid.ToByteArray().CopyTo(_buffer, 8)
' copy the screen name to the next 30 bytes
Dim screenName As String = Space(30)
If token.ScreenName IsNot Nothing AndAlso Not String.IsNullOrEmpty(token.ScreenName) Then screenName = token.ScreenName.PadRight(30)
System.Text.Encoding.ASCII.GetBytes(screenName).CopyTo(_buffer, 24)
' copy the site id to the next 16 bytes
Dim siteid As Guid = Guid.Empty
If token.Identity IsNot Nothing AndAlso token.Identity.Site IsNot Nothing Then siteid = token.Identity.Site.SiteId
siteid.ToByteArray().CopyTo(_buffer, 54)
' hash the buffer.. if the isauth flag is missing in the token, skip that
' last byte. this has the affect that if we attempt to generatea a hash from an xml
' token made by the last gen service, we'll be able to validate it
_buffer(86) = &HFF
Dim count As Integer = _buffer.Length
If Not token.IsAuthenticated.HasValue OrElse _
Not token.IsAuthenticated.Value Then
count = count - 1
End If
Return New SHA1CryptoServiceProvider().ComputeHash(_buffer, 0, count)
End Function