-->

Creating a Google reCAPTCHA 'secure token'

2020-07-24 03:34发布

问题:

Google allows you to create a 'secure token' for reCAPTCHA which means you can use the same key/secret across multiple domains. No need to create key/secrets for every domain you look after.

Here's their docs, as you can see it has no insight on how the token is encrypted other than an example in Java. My question is how would this be written in ColdFusion. I've had a crack at it for 4 hours, but just can't get it to work. Other examples I've reviewed:

  • An example in PHP:
  • An example in .NET

Any ColdFusion encryption guru's out there know how to do this?

UPDATE

Thanks Leigh, think we're further along but still seeing 'invalid stoken'. Here's what I have:

json_token = '{"session_id":"#createUUID()#","ts_ms":#dateDiff("s", dateConvert("utc2Local", "January 1 1970 00:00"), now())#}';
secret_key_hash = hash(secret_key,"SHA", "UTF-8");
secret_key_binary = binaryDecode(secret_key_hash, "hex");
secret_key_aes = arraySlice(secret_key_binary,1,16);
secret_key_base64 = binaryEncode( javacast("byte[]", secret_key_aes), "base64");
secure_token = Encrypt(json_token,secret_key_base64,"AES/ECB/PKCS5Padding",'base64');

We're using ColdFusion 9 on Java 1.7, the arraySlice method isn't available or the underlying java .subList(). So we're using the arraySlice UDF from cflib.org.

I've also seen comments on the PHP implementation about URL encoded, so I've also tried this at the end, no effect:

    secure_token = Replace(secure_token,"=","","ALL");
    secure_token = Replace(secure_token,"+","-","ALL");
    secure_token = Replace(secure_token,"/","_","ALL");

回答1:

NB: Posting this since I had already written it before the question was closed. Though in future, please include the code you have tried within the question. It would have helped clarify the issue (and probably avoided it being closed as "too broad")

no insight on how the token is encrypted

If you are only stuck on the encryption part, it looks like standard AES encryption (ECB mode and PKCS5Padding) from the java example. The only tricky part is the handling of the encryption key.

byte[] key = siteSecret.getBytes("UTF-8");
key = Arrays.copyOf(MessageDigest.getInstance("SHA").digest(key), 16);

In the java code, the getKey() method decodes the key string and hashes it using SHA1, which produces 20 bytes (or 160 bits). Since that is not a valid AES key size, the code grabs the first sixteen (16) bytes to use as a 128 bit AES encryption key. The rest of the java code is just basic AES encryption, which you can easily reproduce in CF using the encrypt() function.

To replicate the encryption in CF:

  1. Hash the secretKey string

    hashAsHex = hash(secretKey, "SHA", "UTF-8");

  2. Then decode the hash into binary, so you can extract the first sixteen (16) bytes. That gives you the 128 bit AES encryption key (in binary form):

    hashAsBinary = binaryDecode(hashAsHex, "hex"); keyBytes = arraySlice(hashAsBinary, 1, 16);

  3. Now simply convert the key bytes into a base64 string, and pass it into the encrypt() function:

    keyAsBase64 = binaryEncode( javacast("byte[]", keyBytes), "base64"); token = encrypt(jsonToken, keyAsBase64 , "AES/ECB/PKCS5Padding", "base64");

That is it. I will leave you to figure out the rest on your own.