I am having an issue with encrypting/decrypting a ...
# cfml-general
d
I am having an issue with encrypting/decrypting a string which I am hoping someone can shed some light on. In an old ModelGlue app I have created an encrypted string which is a combination of a 6 digit numeric id number, a pipe symbol, and a numeric mailing code. I am using the following code to do so:
Copy code
<cfset unsubscribeValues = MailingListID & "|" & mailingNum />
<cfset unsubscribeVars = encrypt(unsubscribeValues, encryptKey, "AES/CBC/PKCS5Padding", "HEX") />
And adding as a link in my emails.
Copy code
<a href="#domain#/unsubscribe?v=#encodeForURL(unsubscribeVars)#">Unsubscribe</a>
When someone clicks the unsubscribe link, I am using the following code to decrypt the string.
Copy code
<cfset var unsubscribeVars = decodefromUrl(arguments.event.getValue("v")) />
<cfset var unsubscribeValues = len(unsubscribeVars) ? decrypt(unsubscribeVars, encryptKey, "AES/CBC/PKCS5Padding", "HEX") : "" />
Half the times that someone tries to unsubscribe, it works fine. The other half though, it throws an error when trying to decrypt the string with the following error: An error occurred while trying to encrypt or decrypt your input string: Given final block not properly padded. Such issues can arise if a bad key is used during decryption. Both encrypt/decrypt functions are using the same key. Am I doing something wrong? Is there a better way to do this? Any help would be appreciated.
b
Are you able to see the values for "v" that cause padding errors, and 1) try to manually decrypt them and 2) see what might be up? -- e.g., is it a user manually modifying the "v" parameter or some weird encoding/formatting happening along the way between email formatting/HTML, links, browser behavior, etc? Message padding lets you encrypt messages of any length when using a block cipher (such as AES). The plaintext to be encrypted will be automatically "padded" with extra bytes at the end as needed, and the decryption process verifies and then removes the padding. If the padding check fails, you'll get the padding error you see here. If you were passing Base64-encoded data (and not Hex data), I'd suspect that something along the way during URL encoding/decoding could be the culprit, but with Hex data, nothing should actually be getting modified when you URL encode/decode. (Shameless plug) I cover messaging padding a bit my recent CFSummit presentation (https://hoyahaxa.blogspot.com/2023/04/slides-from-coldfusion-summit-east-2023.html, slides 27-37). You also potentially have to be aware of padding oracle attacks when using a block cipher in CBC mode without validating the integrity of the ciphertext before trying to decrypt it - but I think the practical risk here is low. But it's possible these padding errors could be users trying to probe/mess with the padding here. I cover padding oracle attacks in that presentation too, and I'll hopefully blog about it soon (specifically as it relates to ColdFusion) - but here's a good overview of padding oracle attacks - https://robertheaton.com/2013/07/29/padding-oracle-attack/
d
Thanks for the reply and information, Brian. I’m definitely going to take a look at your presentation. I do see the “v” values that are failing and have tried decrypting them manually, and they do fail. These current failures are from a 600-700 recipient mailing, so my next step will be to encode all the values into a table and compare the failed value to see if there are any discrepancies. If it was only one or two I would think that it was someone screwing with the values, but this is a brand new feature that I build and it was 8-10 people hitting the unsubscribe in first couple of hours of the mailing. It is just odd because I can see successful unsubscribes from the same mailing.
b
I'm curious - do you see any values that don't contain valid Hex chars (e.g., 0-9, A-F)?
d
I don’t. Here is an example of one that failed. 1CBA92675C7013B57D3F73AB71E293A20D1A3878A03CF2A40F042BE724D46D50
b
Unless the value of "encryptKey" changed at some point, or the parameters passed to encrypt()/decrypt() changed at some point, I'm out of ideas 😂
d
Unfortunately, not. The key is a global value that I use for both functions. Thanks again for your input, I really appreciate it.
b
Good luck! Also when you say that you might "encode all the values into a table and compare" -- note that you'll get different outputs when you encrypt the same value in this setup. Since you're not passing in an IV (initialization vector) parameter when you encrypt(), the first block (16-bytes) of the outputted ciphertext will contain the IV used in encryption. So when you decrypt() without passing in an IV, it uses the first block of the ciphertext as the IV.
d
Awesome, thanks for the heads-up, Brian!
m
I'm not saying it is necessarily your issue, because i think i would have expected a little different thrown message if this were the case. But we have had mailings in the past were the clients mail system messed with the url variables, including appending to them, so when we recieved the clickback, the last variable was messed up. You might try appending an extra throwaway &w=1 or something at the end of the href, so that if it does get appended to, it might append to that w and not mess up your v.