1

I am trying to pass a JSON string from one web application to another using URL parameters (for an internal SSO server).

What I need to do is be able to encrypt the JSON string (which is a user payload object) with a pre-shared key, forward the user to the service provider application with the payload attached as a URL parameter and then on the service provider application decrypt the payload back into a JSON string to get the required information.

Now this part isn't as much of an issue thanks to all of PHP's built in encryption functions but the next part is the difficulty. I am needing to embed a checksum within the encrypted string which can be checked when decrypting it so that if it has been modified in transit then I can raise an exception.

The purpose of this is to make sure that the user payload has not been modified in transit either accidentally or deliberately.

asprin
  • 9,579
  • 12
  • 66
  • 119
Chris Rutherfurd
  • 1,617
  • 1
  • 15
  • 32
  • 1
    @miken32 - I took a look at that post first however it doesn't solve what I am specifically trying to achieve. The topic you posted specifically deals with encrypting passwords and how that is not a good thing and that they should be hashed instead. My use case calls for a reversible encryption with a verification signature to verify nothing has changed in transit, and for it to work in PHP. – Chris Rutherfurd Jan 18 '17 at 04:43
  • Can't you just rely on https? – imel96 Jan 18 '17 at 06:44

1 Answers1

2

You want to provide more than a "checksum" (usually defined as "calculable by any party"); you want to provide an authentication tag or message authentication code (MAC). You have a couple options:

  1. Use an "authenticated encryption" (AE) or "authenticated encryption with associated data" (AEAD) cipher to do this. AE(AD) ciphers provide an "authentication tag" over the cipher text, either in a single pass or with a repeated process over the encrypted cipher text. Examples (probably available in whichever PHP cipher library you're using) are GCM, EAX, and CCM. This is recommended, as the decryption operation will fail if the authentication tag is not verified, and only one shared secret (key) is necessary.
  2. You can construct the system yourself using cryptographic primitives. This is less ideal, as you are responsible for more independent pieces, you need to manage more keys (if you have access to an OMAC implementation, you can use the same key), and your individual construction is not vetted by third parties (aka the collective work of the internet). If you follow this path, you need to keep some key details in mind:
    • Use a strong hash-based message authentication code (HMAC) such as HMAC/SHA-256, -384, or -512. Do not use SHA-1 or MD5, as these are easily brute forced.
    • Verify the HMAC before decrypting the cipher text. Any HMAC that fails means the entire cipher text should be discarded. You can remember this (on the generating side) as Encrypt Then MAC, and if you search for it, you'll see that not following this advice is the source of many cryptographic vulnerabilities and implementation exploits.
    • Verify the HMAC with a constant-time algorithm (i.e. do not use a short-circuit string equality comparison, the default in Java). PHP provides hash_equals to do this. Here's a quick explanation of timing attacks and a code review of a PHP example.

For either choice you'll want to encode the resulting cipher text and authentication tag with URL-safe Base64 in order to avoid data loss or corruption. If your message format is not strictly structured with included lengths, you'll have to pre-share the protocol ahead of time (i.e. for message m of length n bytes -> 16 bytes IV | n-48 bytes cipher text | 32 bytes HMAC).

Last note: always use a unique, non-predictable IV for each message that is encrypted with a key. Many people gloss over this, because it's "easy to just use 0x00 * 16", but any stream cipher mode of operation like CTR used as the foundation of GCM and CCM will lose fundamental security if two messages are encrypted with the same IV and key.

Andy
  • 13,916
  • 1
  • 36
  • 78
  • Based on what you have said @Andy this would appear to be exactly what I am looking for, however a check in PHP's documentation shows that AEAD is not currently supported through the OpenSSL library. It appears that it may be supported (GCM & CCM at any rate) within OpenSSL itself but that the functionality can not be embedded within PHP. This would be perfect if it could work within my php7.0 application. – Chris Rutherfurd Jan 18 '17 at 04:27
  • [Scott Arciszewski @CiPHPerCoder](https://stackoverflow.com/questions/24519554/how-can-we-use-gcm-mode-encryption-in-php#comment51816560_24519554), who I trust on all things PHP crypto, recommends [`libsodium-php`](https://github.com/jedisct1/libsodium-php). – Andy Jan 18 '17 at 04:32
  • Here's extensive documentation on how to use it: [Using Libsodium in PHP Projects](https://paragonie.com/book/pecl-libsodium). – Andy Jan 18 '17 at 04:46
  • Thanks Andy that worked well and appears to do what I need it to do, one final question as this uses a nonce and I have not used them before, is it safe to pass the nonce in the same message as the encrypted JSON sequence. IE: something.php?e=ciphertext&n=nonce, if now what would be the best way to get the nonce to the other web app to decrypt given the fact that the nonce should be different every single time and so can't be pre-shared. – Chris Rutherfurd Jan 18 '17 at 06:19
  • Yes it is safe to pass the nonce (unprotected) alongside the message. Everywhere I referred to the IV in my answer, you can substitute that for the nonce. The IV is essentially the 8-13 byte nonce plus a counter value that is incremented (see [the Wikipedia entry for CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29) for a diagram of how this works). Most systems just prepend the IV to the cipher text and Base64 encode the whole thing because the IV is always required and is always 16 bytes. – Andy Jan 18 '17 at 08:54
  • Typo above: the nonce is 7-13 bytes. – Andy Jan 18 '17 at 09:01