Created
June 10, 2022 11:02
-
-
Save bennadel/2a2eb3e1131d19600573e045942d2a7c to your computer and use it in GitHub Desktop.
A Relational Database Table To Prevent Double Form-Submissions In ColdFusion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE TABLE `double_submission_token` ( | |
`token` varchar(50) NOT NULL, | |
`expiresAt` datetime NOT NULL, | |
PRIMARY KEY (`token`), | |
KEY `IX_byExpiration` (`expiresAt`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
// Set up default form values. | |
// -- | |
// NOTE: We're generating a unique token for this form instance. This value will only | |
// be generated once per form since the post-back will also include the submission | |
// token thereby avoiding the subsequent CFParam default call to createUuid(). | |
param name="form.submitted" type="boolean" default=false; | |
param name="form.submissionToken" type="string" default=createUuid(); | |
param name="form.message" type="string" default=""; | |
if ( form.submitted ) { | |
try { | |
// By wrapping the processing in a CFTransaction tag, it creates an atomic | |
// boundary around both "INSERT INTO" queries. This means that if something | |
// goes wrong with the message insert (the second query), the token insert | |
// (the first query) will naturally rollback allowing for the form to be | |
// re-submitted without issue. And, of course, since the submission token has | |
// a unique index on it (primary key), any accidental double-submission will | |
// cause a "duplicate entry" error on the first insert, thereby preventing the | |
// second query from ever executing. | |
transaction { | |
``` | |
<cfquery> | |
INSERT INTO | |
double_submission_token | |
SET | |
token = <cfqueryparam value="#form.submissionToken#" sqltype="varchar" />, | |
expiresAt = ( UTC_TIMESTAMP() + INTERVAL 1 HOUR ) | |
</cfquery> | |
<cfquery> | |
INSERT INTO | |
ben_message | |
SET | |
message = <cfqueryparam value="#form.message#" sqltype="longvarchar" /> | |
</cfquery> | |
``` | |
} // END: Transaction. | |
location( url = "./success.cfm", addToken = false ); | |
} catch ( any error ) { | |
if ( error.message contains "Duplicate entry" ) { | |
echo( "Oops, it looks like your message is already being processed." ); | |
abort; | |
} | |
echo( "Sorry, an unexpected error occurred." ); | |
abort; | |
} | |
} | |
</cfscript> | |
<!--- Reset the output buffer and render the page. ---> | |
<cfcontent type="text/html; charset=utf-8" /> | |
<cfoutput> | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<title>Send a message</title> | |
</head> | |
<body> | |
<h1> | |
Send a Message | |
</h1> | |
<form method="post" action="#cgi.script_name#"> | |
<!--- Posting our double-submission form token back to server. ---> | |
<input type="hidden" name="submitted" value="true" /> | |
<input type="hidden" name="submissionToken" value="#encodeForHtmlAttribute( form.submissionToken )#" /> | |
<textarea name="message">#encodeForHtml( form.message )#</textarea><br /> | |
<button type="submit"> | |
Send message | |
</button> | |
</form> | |
</body> | |
</html> | |
</cfoutput> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment