How do we encrypt an incoming payload link and decrypt it when a user clicks on it?

Scenario: The Front end will be sending a link to a zip file. My proxy has to encrypt this link with the timestamp of when it was first sent to Apigee and send it to the backend along with the original link.

When the user clicks on the encrypted link the Proxy should decode this link and check if it is within 12 days of its creation and then start streaming the contents of the zip File through Apigee.


Do we need to create two proxies to handle this requirement or can 1 proxy handle it?
Any help in the right direction would be greatly appreciated!

0 1 453
1 REPLY 1

Interesting design challenge.

First. I would not design the system so that the data from the GCP bucket would stream through Apigee. Rather, I would want the front end to directly connect to the GCP bucket using a signed URL. The signed URL can include an expiry, usually within a day, sometimes within an hour. But at most 7 days. The interaction would then be:

  • Backend generates an email with a link that is somehow "time-limited" to 12 days
  • user clicks through the email. This gets resolved by Apigee.
  • Apigee proxy checks the data in the request, verifies that it is not expired.
  • If not expired, then Apigee generates a "signed URL" that points directly to the GCP bucket. The expiry on this URL can be 5 minutes (for example)
  • The user then clicks or 302-redirects to the GCP bucket and receives the data directly.

You have a goal to restrict access to within 12 days. That exceeds the maximum expiry on a v4 signed URL for GCP, which means it will not be possible to generate a single signed URL with a 12-day expiry and deliver that to the user in the email.

But we can manage this. JWT have an expiry. The backend can product an assertion that includes these claims

{ 
  "resource" : "/GCS/bucket/and/resource/path", 
  "iat" : 1573679499,
  "exp" : 1574716299
}

(The "exp" is exactly 12 days after the "iat".)

And then, with that information, produce a signed (or encrypted, if you like) JWT. This can then be included in a link. You mentioned AES encryption - that also works, and would produce a shorter ciphertext (because you can omit the JWT header, and agree on a bunch of other fixed parameters).

Regardless which you choose, the user clicks the link, this resolves to an endpoint hosted in Apigee. Apigee can verify the signature (or decrypt if it is encrypted) and then evaluate the expiry. If the current time is after the expiration, Apigee can return a 404. If the current time is before expiration, then Apigee will generate the signed URL with something like this callout, and then returns the resulting URL as a 302 redirect to the user.

The user-agent follows that 302 and gets direct access to the data stored by GCS. (No Apigee involved in this URL)

There is no additional storage required for this, although Apigee and the backend system need to agree on keys. There is a catch: including the entire JWT in the clickable link means you may have a link that is 500+ characters. (this tool shows ~500 characters for the above payload and a PS256 signature). This is valid, but it might not be the user experience you want. So you might also want to rely on a URL shortener for that. This would require storage.

With AES crypto, base64-encoded, the ciphertext + IV will be something like 200 characters. That may be acceptable for a link for a user.

eg,

https://apigee.endpoint/deref/F3bjqaP8XAnFeSC8R%2F6SzMjBvcuShw8rJiGBK21dCfnZQlMW9LmCCc4uRWSHkCoUGkmf...

...in which case, no additional storage.

You'll have to separately solve the key management problem. Eg, what happens when you rotate keys? What if you want to "turn off" a key because it's been compromised? How do you prevent Apigee from treating that key as valid. Not intractable problems, but they are worth thinking about.