SHA-256-ing an email address in Velocity

Level 10 - Community Moderator

Can't believe someone other than me has thought about cryptographic techniques in Marketo! But in this recent Nation thread, a user asked about attaching the SHA-256 hash of a recipient's email address to (email) links.

The aim being to send a secure representation of a lead's email to an external (non-Marketo) site, where it could be looked up as part of a 3rd-party subscription center.

In other words, a link would look like this:

http:​//nonmarketopage.example.com/?emailHash=284C36B4333F55511617FB225C62813316038522DF5C2AEC6B6B13B3D3774F5C

The target site's database would hold pre-computed hashes of all their registered users' emails, the hash being stored in a separate column from the email itself.[1]

A match would then be done from hash-to-hash (the way hashes are always used, since they can never be reversed to the original input) to load the user's record.

Mind you, I have no idea why this would actually be done as opposed to sending the URL-encoded email address:

http:​//nonmarketopage.example.com/?email=sandy%40figureone.com

Or the easily reversed, Base64-encoded (not encrypted) address, which wouldn't require a hash lookup:

http:​//nonmarketopage.example.com/?emailB64=c2FuZHlAZmlndXJlb25lLmNvbQ==

Being a realist, I suspect the justification is merely that they want the links to look more “technical” than they would with Base64 (?) and they don't truly have any need for security (as it makes little sense to care about the security of the recipient's own email address![2]). UPDATE: OP says the client now wants AES encryption instead of hashing, so they really are trying to securely transport stuff in the URL. That gives me a chance to bring out the big guns and show you how encryption is done in VTL... at some point.

Anyway, should you need it, here's a utility Velocimacro to gen a SHA-256 hash:

#**

    HashTool in VTL v2

    @copyright (c) 2018 Sanford Whiteman, FigureOne, Inc.

    @license MIT License: all reproductions of this software must include the data above

*#

#macro( SHA256_v2 $mktoField )

## reflect some dependencies

#set( $Class = $context.getClass() )

#set( $java = {} )

#set( $java.lang = {

     "StringBuilder" : $Class.forName("java.lang.StringBuilder"),

     "Appendable" : $Class.forName("java.lang.Appendable")

} )

#set( $java.security = {

    "MessageDigest" : $Class.forName("java.security.MessageDigest")

} )

#set( $java.util = {

    "Formatter" : $Class.forName("java.util.Formatter").getConstructor($java.lang.Appendable)

} )

## get an MD and make it go, then (important) reset MD singleton for reuse

#set( $MD = $java.security.MessageDigest.getInstance("SHA-256") )

#set( $digestBytes = $MD.digest( $mktoField.getBytes("utf8")) )

#set( $void = $MD.reset() )

## gen hex string representation

#set( $hexString = $java.lang.StringBuilder.newInstance() )

#set( $hexer = $java.util.Formatter.newInstance($hexString) )

#foreach( $B in $digestBytes )

#set( $void = $hexer.format("%02x",$B) )

#end

## return uppercase, but note a hex string should be treated as case-insensitive

$hexString.toString().toUpperCase()##

#end

After including the above in its own token (best to not pollute your user code with utility functions) use the macro like so:

#set( $emailHash = "#SHA256_v2($lead.Email.toLowerCase())" )

<a href="http://www.example.com/?emailHash=${emailHash}">Click here</a>

Note that I toLowerCase()d the Email before passing it, as that's appropriate for that particular field (as I've written about before, though SMTP addresses are actually case-sensitive, they're commonly matched case-insensitively for sanity's sake).


NOTES

[1] Hope I'm not hoping for too much here. They'd better be already pre-computing hashes for all the stuff in their database, or this idea goes from merely frivolous to very bad. If they didn't pre-hash, they'd have to hash every record in the database, on-the-fly, for every lookup. This would absolutely destroy performance and be a sign that the back end was not well thought out.

[2] Long as the destination site runs SSL. But if the site doesn't run SSL then the connection could be intercepted and you have a lot worse problems than showing the attacker an email address.

7 Comments

Hi Sanford, Thanks for this post. Looking over the original question as well, I am need of this but also hashing the email in MD5 upper and lowercase and SHA1 upper and lowercase. Is the process the same?

Level 10 - Community Moderator

Yes, "MD5" and "SHA-1" are also values you can pass to MessageDigest.

Thanks for breaking this down. My team would like to generate a SHA-256 encryption for email addresses, but I am having issues implementing this. I've added both velocimacros above as tokens within an email program.

Screen Shot 2019-02-25 at 10.45.02 AM.png

Screen Shot 2019-02-25 at 10.45.17 AM.png

Within the email HTML, I am calling out the token as {{my.hashemail}} When I save the HTML, I get the error code:

Validation Error approving 2019.0206 - DMP - PIxel test.Email — <div>An error occurred when procesing the email Rendered_Email_Velocity_Error_Area_?! </div> <p>Invocation of method 'digest' in class java.security.MessageDigest$Delegate threw exception java.lang.NullPointerException near</p> <div><pre >?</pre></div>

Any leads are much appreciated. Thanks!

Level 10 - Community Moderator

The NPE indicates $mktoField is null. Ensure you've checked the Email Address field in the tree and that you're testing with a real email (not sample) or with Preview by List.

Hi Sanford,

I too am trying to use this (I formerly worked with Stephanie Castillo) and can't produce a hashed email.

Here is my token code:

#**
HashTool in VTL v2
@copyright (c) 2018 Sanford Whiteman, FigureOne, Inc.
@license MIT License: all reproductions of this software must include the data above
*#
#macro( SHA256_v2 $mktoField )
## reflect some dependencies
#set( $Class = $context.getClass() )
#set( $java = {} )
#set( $java.lang = {
"StringBuilder" : $Class.forName("java.lang.StringBuilder"),
"Appendable" : $Class.forName("java.lang.Appendable")
} )
#set( $java.security = {
"MessageDigest" : $Class.forName("java.security.MessageDigest")
} )
#set( $java.util = {
"Formatter" : $Class.forName("java.util.Formatter").getConstructor($java.lang.Appendable)
} )
## get an MD and make it go, then (important) reset MD singleton for reuse
#set( $MD = $java.security.MessageDigest.getInstance("SHA-256") )
#set( $digestBytes = $MD.digest( $mktoField.getBytes("utf8")) )
#set( $void = $MD.reset() )
## gen hex string representation
#set( $hexString = $java.lang.StringBuilder.newInstance() )
#set( $hexer = $java.util.Formatter.newInstance($hexString) )
#foreach( $B in $digestBytes )
#set( $void = $hexer.format("%02x",$B) )
#end
## return uppercase, but note a hex string should be treated as case-insensitive
$hexString.toString().toUpperCase()##
#end

#set( $emailHash = "#SHA256_v2($lead.Email.toLowerCase())" )

And here is the template code I set up to test:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head>
<!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!--<![endif]-->
<!-- START Marketo Variables -->
<meta class="mktoString" id="emailHash" mktoname="emailHash" default="0" min="0" max="30" units="" step="1" mktomodulescope="true" />
<!-- END Marketo Variables -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div class="mktEditable" id="emailHash" mktoname="emailHash">
<span style="font-family:Arial,sans-serif;font-size:13px;line-height:24px;color:#A7A7A7;">http://www.example.com/?emailHash=${emailHash}</span>
</div>
</body>
</html>

Does anything jump out at you Sanford Whiteman‌? I'm not getting any errors but I also am getting a "0" value for the emailHash value. Thanks!

Level 10 - Community Moderator

Unfortunately, under Marketo's "new regime" for Velocity post-June 2019, this code will no longer work, and there's no alternative.

(Do highlight your code next time using the syntax highlighter, though... I couldn't read it without highlighting but happened to know what the problem was anyway.)

Thanks Sanford Whiteman‌ and will do on the code next time.