APIGEE OPDK - validate client hash :SHA256(client_id+client_secret+timestamp)

Hi,

 

I have the following requirement

1. APIGEE has generated client id and secret for the client application during app registration.

2. client instead of calling APIs through access token. They will call it using SHA256(client_id+client_secret+timestamp). This header will be passed in each request.

3. When this request comes to APIGEE, APIGEE should create a same hash to verify it. there should be a buffer of (+,-)3-4 min kept and allow request with (+,-) 3 min delay or early as per servers time.

Can I have this functionality in APIGEE OPDK with minimum latency.

This functionality is provided by Mashery OOTB

ankitajain_0-1637160814002.png

 

 

@anilsr @anilsagar , @dchiesa1 

Thanks in advance

Ankita

 

0 1 523
1 REPLY 1

They will call it using SHA256(client_id+client_secret+timestamp). ...Can I have this functionality in APIGEE OPDK with minimum latency.

Sure you can do it. I am not sure this is a good thing to do. Let's put aside that discussion for a moment. It is possible to do this.

But you haven't provided sufficient information for how the authentication protocol works.

You can have clients that compute a SHA256 on any payload they like. And Apigee can compute a SHA256, and compare the results. That's no problem. There is a static function in the AssignMessage policy that does this. It looks like this:

 

<AssignMessage name='AM-SHA256'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <AssignVariable>
    <Name>sha256_hex_encoded</Name>
    <Template>{sha256Hex(payload_here)}</Template>
  </AssignVariable>
  <AssignVariable>
    <Name>sha256_b64_encoded</Name>
    <Template>{sha256Base64(payload_here)}</Template>
  </AssignVariable>
</AssignMessage>

 

That will give you a sha256, encoded either as hex or base64.

The question is, what is the payload? You said "client_id+client_secret+timestamp".

But that means the request needs to pass the client_id and the timestamp, explicitly. I suppose the client will pass these things in separate headers. like this:

 

GET /foo/bar

SHA256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
ClientID: ABCDEFG
Timestamp: 20211117-1658

 

Given that kind of request, then on the Apigee side, you'd need to do this kind of check:

  1. verify the API key (client ID)
  2. check the timestamp that the client passes in, to see that it's valid (maybe: not older than 60 seconds and not in the future by more than 60 seconds?)
  3. compute the expected SHA256
  4. Raise a fault if the provided SHA256 does not match the computed SHA256

In a policy flow, that logic would look something like this:

 

  <Step>
    <Name>VerifyApiKey</Step>
  </Step>
  <Step>
    <Name>JS-Validate-Timestamp</Name>
  </Step>
  <Step>
    <Name>AM-Compute-SHA256</Name>
  </Step> 
  <Step> 
    <Name>RaiseFault-Invalid-SHA256</Name>
    <Condition>NOT(request.header.sha256 = computed_sha256_b64_encoded)</Name>
  </Step>

 

The VerifyAPIKey policy would look like this:

 

<VerifyAPIKey name='VerifyApiKey'>
    <APIKey ref='request.header.ClientID'/>
</VerifyAPIKey>

 

That will implicitly set the context variables client_id and client_secret, if I recall correctly.

The AssignMessage-Compute-SHA256 would be something like this:

 

<AssignMessage name='AM-Compute-SHA256'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <AssignVariable>
    <Name>signature_base</Name>
    <Template>{client_id}-{client_secret}-(request.header.timestamp}</Template>
  </AssignVariable>
  <AssignVariable>
    <Name>computed_sha256_hex_encoded</Name>
    <Template>{sha256Hex(signature_base)}</Template>
  </AssignVariable>
  <AssignVariable>
    <Name>computed_sha256_b64_encoded</Name>
    <Template>{sha256Base64(signature_base)}</Template>
  </AssignVariable>
</AssignMessage>

 

The JavaScript step would need to deserialize the passed-in timestamp into a Date object, and subtract the numeric value from Date.now(). Take the absolute value of that. (Math.abs()) and check to see that it's less than 60.

The RaiseFault is pretty simple - it just sends back the error you want to send in the case the SHA does not match.

ok that takes care of HOW to do it.

The next question is, SHOULD you do it? And the answer to that is "probably not". It's not a good idea to compute a SHA directly on a secret (like client_secret. There are better ways than SHA256 to secure API calls, including supporting a timestamp to prevent replay attacks.

  1. I recommend that you use an HMAC, not a SHA256. HMAC using SHA256 as the compute function, and the client_secret as the key.
  2. The payload should include the VERB, the PATH, a TIMESTAMP, and the client ID.

There are a couple of popular ways to do this. HTTPSignature is one option. There's a community-contributed policy that will compute HttpSignature in Apigee. Another option is to use a signed JWT. The JWT itself can be signed with an HMAC algorithm (like HS256), using the client_secret as the key.

Either of those is a better approach to security than sending a SHA of a composite that includes the secret!

I advise you to not do things "the old way" just because that;s how you've always done it.