Powering E mail Unsubscribe Hyperlinks The usage of Signed URLs In ColdFusion

Powering E mail Unsubscribe Hyperlinks The usage of Signed URLs In ColdFusion

[ad_1]

Previous this week, I mentioned the usage of signatures to stop URL tampering in ColdFusion. Then, the day prior to this, I used to be having a dialog about unsolicited mail emails and unsubscribe hyperlinks. It passed off to me that the usage of signed URLs is a method during which unsubscribe hyperlinks can also be applied in ColdFusion. As such, I sought after to run thru a small demo.

An unsubscribe hyperlink within the footer of a advertising and marketing e-mail has a couple of other traits:

  • It must be user-specific. That means, each and every e-mail will get its personal unsubscribe hyperlink in order that one consumer does not by chance unsubscribe some other consumer.

  • It wishes to supply non-credentialed entry. That means, we do not want the consumer to must log into the app in an effort to procedure the unsubscribe operation. We are aiming to create a maximally handy workflow for a annoyed consumer.

  • It (most certainly) should not be time-boxed. That means, a consumer can pass right into a advertising and marketing e-mail that used to be despatched 6 months in the past and use the unsubscribe hyperlink. Once more, we are seeking to maximize the benefit right here for the consumer.

  • The similar unsubscribe hyperlink can be utilized more than one instances. That is very true if the touchdown web page supplies each unsubscribe and re-subscribe capability.

In the end, an unsubscribe hyperlink has an excessively restricted set of safety hooks. However, we nonetheless want to make it protected. And, that is the place the HMAC (Hashed Message Authentication Code) signature comes into play. For this demo, the one data-point that we are together with within the URL is the objective userID. And, to ensure that the userID hasn’t been tampered with, we are together with an HMAC signature.

To set this demo up, I will outline an Software.cfc ColdFusion utility framework part that caches our URL signing carrier and a few mock consumer records. The UrlSigner.cfc that I instantiate in my onApplicationStart() tournament handler is similar one who I take advantage of in my earlier publish. As such, I may not reproduce the code right here rather then to mention that the default hashing set of rules is HmacSha256 and the default encoding is base64url.

part
	output = false
	trace = "I give you the utility settings and tournament handlers."
	{

	this.title = "UnsubscribeDemo";
	this.applicationTimeout = createTimeSpan( 1, 0, 0, 0 );
	this.sessionManagement = false;
	this.setClientCookies = false;

	// Mailhog take a look at server.
	this.mailServers = [{
		host: "127.0.0.1",
		port: 1025
	}];

	// ---
	// LIFE-CYCLE METHODS.
	// ---

	/**
	* I initialize the ColdFusion utility.
	*/
	public void serve as onApplicationStart() {

		// This ColdFusion part is helping generate message authentication codes (HMAC).
		utility.urlSigner = new UrlSigner(
			secretKey = binaryDecode( fileRead( "./secret.key" ), "base64" )
		);

		// Makes use of an ORDERED STRUCT to carry pretend consumer records in order that we will be able to glance customers up via
		// ID in addition to iterate over them so as.
		utility.customers = [
			"1": { id: 1, name: "Molly",  email: "molly@example.com",  subscribed: true },
			"2": { id: 2, name: "Arnold", email: "arnold@example.com", subscribed: true },
			"3": { id: 3, name: "Kim",    email: "kim@example.com",    subscribed: true }
		];

	}

}

Realize that each and every of our mock customers has a subscribed assets. This assets determines whether or not or no longer the consumer receives advertising and marketing emails. And, our unsubscribe hyperlink goes to take the consumer to a web page that permits each and every consumer to regulate this assets with out logging in.

Let us take a look at our send-email.cfm template, which loops over the mock customers, generates a protected hyperlink for each and every consumer, after which sends out an e-mail. To check the emails, I am the usage of the very at hand Mailhog SMTP building server.

Realize within the following template that we are skipping over any consumer whose consumer.subscribed assets is false:

<cfscript>

	// DEMO ONLY: For making it more uncomplicated to learn the inbox subject-lines.
	sendID = createUniqueId( "counter" );

	// Ship an e-mail to each and every of the customers.
	loop
		key = "userID"
		price = "consumer"
		struct = utility.customers
		{

		// If the consumer isn't subscribed to emails, proceed onto subsequent consumer.
		if ( ! consumer.subscribed ) {

			proceed;

		}

		// We are about to embed a user-specific hyperlink inside an e-mail that permits the
		// consumer to switch their state with no need to authenticate with credentials.
		// As such, we want to ensure that the URL can't be tampered with; another way,
		// we might disclose some way during which a malicious actor may just trade the state of any
		// consumer.
		signature = utility.urlSigner.generateSignature({
			userID: consumer.identification,
			characteristic: "unsubscribe-#consumer.identification#"
		});

		// Get ready records construction to be ate up in e-mail template.
		partial = {
			consumer: consumer,
			unsubscribeUrl: (
				"http://#cgi.http_host#" &
				"/signed-url/unsubscribe.cfm" &
				"?userID=#encodeForUrl( consumer.identification )#" &
				"&signature=#encodeForUrl( signature )#" // Come with signature!
			)
		};

		mail
			to = partial.consumer.e-mail
			from = "no-reply@instance.com"
			discipline = "Nice new provides! (#sendID#)"
			kind = "html"
			async = false
			{

			come with "./email-content.cfm";
		}

	}

</cfscript>

<a href="https://www.bennadel.com/weblog/./send-email.cfm">Ship once more!</a>

In my .generateSignature() name, I am passing within the consumer ID, which we are together with within the unsubscribe hyperlink. However, I am additionally together with some other key, characteristic, that we aren’t passing thru within the hyperlink. This secondary characteristic assets is not strictly vital (from what I have learn); however, I am together with it as one of those “pepper” that differentiates one kind of signed URL for some other.

Warning: That is most certainly a excellent position to provide you with a warning that I am not a safety professional.

As soon as now we have the signed URL ready, I am sending the next e-mail – I at all times love to stay my e-mail templates become independent from the CFMail tag:

<cfoutput>
	<h1>
		Hi #encodeForHtml( partial.consumer.title )#
	</h1>
	<p>
		Take a look at all our new provides!
	</p>
	<p>
		<a href="#partial.unsubscribeUrl#">Unsubscribe</a>
	</p>
</cfoutput>

When the consumer clicks the “Unsubscribe” hyperlink, we want to take them to a web page that validates the URL signature (making sure that the userID wasn’t tampered with); and, permits them to each unsubscribe and re-subscribe to advertising and marketing emails.

Since this web page has two purposes, I am not appearing any motion on preliminary load. As an alternative, I will render an HTML shape that tells the consumer whether or not or no longer they are lately subscribed to advertising and marketing emails; and, permits them to toggle the present state. To stay this straightforward all over shape processing, I am simply updating the in-memory records constructions.

<cfscript>

	// Ensure that request parameters.
	param title="url.userID" kind="numeric";
	param title="url.signature" kind="string";
	param title="shape.motion" kind="string" default="";

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// Preferably, we wish to take a look at the URL signature earlier than we do anything in order that in
	// addition to protective the consumer we will be able to additionally give protection to different upstream products and services (such
	// because the database). If the signature does not fit, an error is thrown.
	utility.urlSigner.testSignature(
		{
			userID: url.userID,
			characteristic: "unsubscribe-#url.userID#"
		},
		url.signature
	);

	// ASSERTION: At this level, we all know that the URL used to be no longer tampered with (in regards to the
	// consumer ID). As such, we will be able to accept as true with that the present consumer (a minimum of) has entry to the
	// goal consumer's e-mail account. To that finish, we will be able to render this web page as though the consumer
	// has been correctly authenticated (noting that this accept as true with does NOT prolong to any
	// different web page throughout the present utility).

	// Get the consumer document (for demo simplicity, I am not validating this).
	consumer = utility.customers[ url.userID ];

	// Procedure shape submission.
	transfer ( shape.motion ) {
		case "subscribe":
			consumer.subscribed = true;
		damage;
		case "unsubscribe":
			consumer.subscribed = false;
		damage;
	}

</cfscript>
<cfoutput>

	<!-- To get title to turn within the browser tab. -->
	<name>
		#encodeForHtml( consumer.title )#
	</name>

	<h1>
		Hi #encodeForHtml( consumer.title )#
	</h1>

	<shape
		manner="publish"
		motion="#cgi.script_name#?userID=#encodeForUrl( consumer.identification )#&signature=#encodeForUrl( url.signature )#">

		<cfif consumer.subscribed>

			<p>
				You're lately <robust>subscribed</robust> to all emails.
			</p>
			<p>
				<button kind="put up" title="motion" price="unsubscribe">
					Unsubscribe Now!
				</button>
			</p>

		<cfelse>

			<p>
				You're lately <em>unsubscribed</em> to all emails.
			</p>
			<p>
				<button kind="put up" title="motion" price="subscribe">
					Subscribe Once more
				</button>
			</p>

		</cfif>

	</shape>

</cfoutput>

As you’ll be able to see, the first thing we do on the most sensible of the web page is take a look at the incoming HMAC signature to ensure that the url.userID price hasn’t been tampered with. And, after we know that the request is “approved”, we will be able to then pass about fetching the consumer records and both rendering or processing the shape.

On every occasion the shape is submitted, we need to come with the unique URL parameters (userID and signature) within the shape motion in order that the next web page load continues to go HMAC signature verification. This additionally permits the shape to be submitted more than one instances. Which, in flip, permits the consumer to proceed managing their subscription state over the years:

Numerous what I perceive concerning the safety of this workflow comes from previous conversations and from studying Wikipedia and StackOverflow. I actually would like to take a seat down with any person and hammer-out a deeper working out of the finer-points of hardening an access-point inside a ColdFusion utility. However, for now, I’m hoping this may a minimum of level you in the best path.

Wish to use code from this publish?
Take a look at the license.



[ad_2]

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back To Top
0
Would love your thoughts, please comment.x
()
x