Signing Secret

Overview

App registration supports the generation of a signing secret that can be used to validate payloads that originated from Seismic.

All applications that want to receive callbacks (install, uninstall, enable, disable and update) or use the Configuration UI extension will need to generate a signing secret because Seismic uses this secret to sign payloads. We will use this to encrypt the JSON payload we post to you. You can then ensure that the calls are in fact coming from Seismic.

The signature is calculated using a HMAC algorithm using SHA265. The signature is sent with each request in the request header. The header key for the HMAC is x-seismic-signature.

The recipient service should use the secret to validate that the payload HMAC using SHA256 and compare it with the x-seismic-signature sent in the request header. This same approach can also be used by external services (like content uploader) to send requests to Seismic using the extensions platform.

The secret is issued to an app and is not tenant specific.

❗️

Signing secret regeneration

If the signing secret is leaked, it can be regenerated in App Registration. The signing secret is common for all versions of the app - published and unpublished.

[HttpPost("config")]
public async Task<IActionResult> Configuration()
{
    using (var memoryStream = new System.IO.MemoryStream())
    {
        await this.HttpContext.Request.Body.CopyToAsync(memoryStream);
        var key = System.Text.Encoding.UTF8.GetBytes(signingSecret); // the app's signing secret

        using (var hmac = new System.Security.Cryptography.HMACSHA256(key))
        {
            var body = memoryStream.ToArray();
            byte[] bodyHash = hmac.ComputeHash(body);
            var computed = System.BitConverter.ToString(bodyHash).Replace("-", "");
            var incoming = this.HttpContext.Request.Headers["x-seismic-signature"];

            var bodyString = System.Text.Encoding.UTF8.GetString(body);
            var json = Newtonsoft.Json.Linq.JToken.Parse(bodyString);

            var requestTime = json.Value<DateTime>("timestamp");

            // Validate signatures match and request not more than 2 minutes old.
            if ((incoming != computed) || DateTime.UtcNow - requestTime > TimeSpan.FromMinutes(2))
                return Unauthorized();

            var appId = json.Value<string>("appId");
            var appName = json.Value<string>("appName");
            var tenantId = json.Value<string>("tenantId");
            var tenantName = json.Value<string>("tenant");
            var version = json.Value<string>("version");
            var userId = json.Value<string>("userId");
            var userEmail = json.Value<string>("userEmail");
            var language = json.Value<string>("language");

            return View(new { appId, appName, tenantId, tenantName, version });// or serve the config page in some other way.
        }
    }
}

Additional information regarding the sample code:

  • Read the request body as is, without any transformations or parsing, as bytes.
  • Computes its hmac signature using the app's signing secret.
  • Parse the body as a json and read the timestamp.
  • Execute validations.
  • The computed signature matches the incoming signature in the header.
  • The request is no more than 2 minutes old.
  • Return an error if validations fail.

All request payloads also carry a timestamp object. It is recommended that all apps verify requests are no more than 2 minutes old. Not building this check may lead to request replays. All timestamps are in yyyy-MM-ddTHH:mm:ssZ format and are based on UTC.