Stripe recommends using their libraries for validating the signature, but since they do not have one that will work with Classic, I have to do it manually, and I am really struggling with it.
(https://stripe.com/docs/webhooks/signatures#verify-manually)
Step 1: Extract the timestamp and signatures from the header Split the header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a prefix and value pair. The value for the prefix t corresponds to the timestamp, and v1 corresponds to the signature(s). You can discard all other elements.
Step 2: Prepare the signed_payload string You achieve this by concatenating: The timestamp (as a string) The character . The actual JSON payload (i.e., the request’s body)
Step 3: Determine the expected signature Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the signed_payload string as the message.
Step 4: Compare signatures Compare the signature(s) in the header to the expected signature. If a signature matches, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures.
I think I am on the right track with the following code:
'Keys
If strStripeMode = "live" Then
strSigningSecret = ""
Else
strSigningSecret = "whsec_CIXV2............................UR8Ta0"
End if
'1. Payload
lngBytesCount = Request.TotalBytes
strPayload = BytesToStr(Request.BinaryRead(lngBytesCount))
'2. Stripe-Signature
strStripeSignature = Request.ServerVariables("HTTP_STRIPE_SIGNATURE")
arrStripeSignature = Split(strStripeSignature,",")
intTimeStamp = REPLACE(arrStripeSignature(0),"t=","")
strV1Signature = REPLACE(arrStripeSignature(1),"v1=","")
strSignedPayload = intTimeStamp & "." & strV1Signature & strPayload
'3. Hash using 'signing secret' as the key.
set crypt = Server.CreateObject("Chilkat_9_5_0.Crypt2")
crypt.HashAlgorithm = "sha256"
crypt.EncodingMode = "hex"
crypt.SetHmacKeyEncoded strSigningSecret,"ascii"
strHashedSignature = crypt.HmacStringENC(strSignedPayload)
set crypt = nothing
'4. Compare the values
If strHashedSignature = strV1Signature Then
'Valid
Response.Status = "200"
Response.Write(Response.Status)
'------------ DO SOMETHING------------
Response.End
Else
'Invalid
Response.Status = "300"
Response.Write(Response.Status)
'------------ DO SOMETHING------------
Response.End
End if
' Bytes to String Function
Function BytesToStr(bytes)
Dim Stream
Set Stream = Server.CreateObject("Adodb.Stream")
Stream.Type = 1 'adTypeBinary
Stream.Open
Stream.Write bytes
Stream.Position = 0
Stream.Type = 2 'adTypeText
Stream.Charset = "iso-8859-1"
BytesToStr = Stream.ReadText
Stream.Close
Set Stream = Nothing
End Function
The first issue that I have is that I am not sure if I am generating the signed_payload string in step #2 correctly. The second issue is that the strHashedSignature in step #3 is coming out blank, likely due to not generating the signed_payload string (strSignedPayload) correctly.
v1string, blindly attempting to replace it assuming it is always element two in the inital split but the documentation says it can also include av0or more schemes. You start off correct by splitting based on,but you should really either split each array element again using=or do something like anInStr()check forv1=. Need to make sure you are combining the right values first before even considering the hash is computed wrongly. - user692942Replace()function is if it doesn't findt=orv1=it will just return the original string unchanged. Which then means you are combining the wrong values and the hash will reflect that. - user692942Stream.Charsetinstead(otherwise you may not read the same string that Stripe sent, which would result in the signatures not matching) - karllekko