0

I am trying to validate an API request from Slack as per the instructions here.

In summary the steps are:

  1. Concatenate the request timestamp with a version and the request body.
  2. Hash the above string with HMAC SHA256, then "Hex Digest" this.
  3. Compare the above with the signature sent from Slack.

I have applied the suggestion in this post which is:

string hexaHash = "";
foreach (byte b in my_signature)
{
    hexaHash += String.Format("{0:x2}", b);
}

I'm expecting a value like this:

'a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'

When I don't apply the code above I'm getting a value like this:

'KvFZL2TojhYJj6ahS0Z7etDwSn4='

Which changes to this when applying that code:

'76303d6f4f31494741457277466e4b32344c6f655172713281ef935fe1fa3d'

My full Azure API Management policy code is as below:

<inbound>
  <!-- Setting variables from the headers passed by Slack -->
  <set-variable name="timestamp" value="@(context.Request.Headers.GetValueOrDefault("X-Slack-Request-Timestamp"))" />
  <set-variable name="slack_signature" value="@(context.Request.Headers.GetValueOrDefault("X-Slack-Signature"))" />

  <!-- Set body received from slack as variable -->
  <set-variable name="slack_body" value="@(context.Request.Body.As
    <String>(preserveContent: true))" />
  <set-variable name="slack_signing_secret" value="{{Slack-Signing-Secret}}" />

  <!-- Create concatenation string as per slack documentation -->
  <set-variable name="sig_basestring" value="@{
                    string body = (string)context.Variables.GetValueOrDefault("slack_body");
                    string timestamp = (string)context.Variables.GetValueOrDefault("timestamp");
                    string sig_basestring = "v0:" + timestamp + ":" + body;
                    return sig_basestring;
    }" />
                    
    <!-- Apply HMACSHA256 to concatenated string using slack signing secret as key  -->              
   <set-variable name="my_signature" value="@{
                <!-- Hash-based Message Authentication Code (HMAC) using SHA256 hash -->
                System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes("{{Slack-Signing-Secret}}"));
                return Convert.ToBase64String(hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes((string)context.Variables["sig_basestring"])));
     }" />
    
    
   <!-- I'm using this method to send the data back to slack to validate both signatures -->
   <return-response response-variable-name="existing response variable">
      <set-status code="200" reason="OK" />
      <set-header name="Content-Type" exists-action="override">
        <value>application/json</value>
      </set-header>
      <set-body>@{
            string my_signature =  (string)context.Variables["my_signature"];
            string slack_signature = (string)context.Variables["slack_signature"];
            string hexaHash = "";
            <!-- This code is applying the "hex digest" method I found -->
            foreach (byte b in my_signature)
            {
                hexaHash += String.Format("{0:x2}", b);
            }
            my_signature = "v0=" + hexaHash;
            return my_signature + " " + slack_signature;
       }</set-body>
    </return-response>
</inbound>

Is there anything that I could apply to the current hashed value to get a similar result to the hexdigest() method suggested in the Slack documentation?

starball
  • 20,030
  • 7
  • 43
  • 238

1 Answers1

1

Using the Slack example values I managed to accomplish the correct digest. I'm sure there is somewhat more elegant way to achieve the removal of the '-' chars that BitConverter.ToString() produces.

    <inbound>
        <!-- Setting variables from the headers passed by Slack -->
        <set-variable name="timestamp" value="@(context.Request.Headers.GetValueOrDefault("X-Slack-Request-Timestamp"))" />
        <set-variable name="slack_signature" value="@(context.Request.Headers.GetValueOrDefault("X-Slack-Signature"))" />
        <!-- Set body received from slack as variable -->
        <set-variable name="slack_body" value="@(context.Request.Body.As
        <String>(preserveContent: true))" />
        <!-- Create concatenation string as per slack documentation -->
        <set-variable name="sig_basestring" value="@{
                        string body = (string)context.Variables.GetValueOrDefault("slack_body");
                        string timestamp = (string)context.Variables.GetValueOrDefault("timestamp");
                        string sig_basestring = "v0:" + timestamp + ":" + body;
                        return sig_basestring;
        }" />
        <!-- Apply HMACSHA256 to concatenated string using slack signing secret as key  -->
        <set-variable name="my_signature" value="@{
                    System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes("8f742231b10e8888abcd99yyyzzz85a5"));
                    return System.BitConverter.ToString(hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes((string)context.Variables["sig_basestring"]))).ToLower().Replace("-", "");
        }" />
    </inbound>
Equestre
  • 91
  • 5
  • I had actually added the BitConverter.ToString() method after I submitted the question but I was computing the hash from the incorrect basestring variable so it wasn't matching. But it's matching now after I looked over it again - thank you for the help! Also, you may want to edit your slack signing secret out of your answer. – JackMorrison Dec 13 '22 at 11:33
  • 1
    Glad I could help! The signing key is from the slack-example you linked in your original post, so no worries there. I thought it could be helpful to be able to verify implementation step by step against the example data. – Equestre Dec 13 '22 at 12:51