====== HMAC Product Verification: Locking Down your Add-To-Cart Links and Forms ======
===== The What & Why =====
==== What Is FoxyCart's HMAC Product Verification? ====
FoxyCart's HMAC Product Verification is a cryptographic method to prevent tampering with your product links and forms, available as of [[:v:0.7.0/|FoxyCart v0.7.0]], based on [[wp>HMAC|HMAC]] [[wp>SHA_hash_functions|SHA-256]]. We know that sounds terrifying, but please read on and it will make more sense. (And //great news!// You can likely implement this without actually having to know anything about the details.)
We recommend taking a moment to familiarize yourself with [[wp>Cryptographic_hash_function|crypographic hash functions]], as they're quite interesting, but the short version is this: Using a hash (specifically with HMAC) provides a very secure way to ensure data isn't modified. So your add-to-cart links and forms can be locked down, preventing a nefarious user from modifying your products.
Note that hashing (or "signing") isn't the same thing as encryption, though they're related. Encryption allows for decryption; it's reversible. Hashing, on the other hand, is one way. You can get a unique hash from your source, but you can't* get the source from the hash. For this reason, it's useful to "sign" things to ensure they haven't been tampered with, but it doesn't "hide" data. For what we care about here, that's perfect.
We'll use the terms "signing" and "hashing" interchangeably on this page.
==== The Potential Risks & Solutions ====
If you do not implement FoxyCart's HMAC cart verification functionality then your products are relatively easily modifiable by web-savvy users. This means that your Product A sold at $20 could be added to the cart at $10 or $1, or added to the cart with the wrong category (possibly affecting taxes or shipping) or etc.
While this certainly can be a major problem, there are multiple ways to minimize the risk. The most common approach taken by stores with smaller order volume is "eyeballing" orders and doing sanity checks prior to delivery. If only one or two people handle the fulfillment and are familiar with the products and pricing, that is often enough to prevent fulfillment of a spoofed order.
Another option is to verify orders automatically using the [[transaction_xml_datafeed|XML datafeed]], comparing the transactions and products against the prices stored in your site's database and alerting on error. Many higher-volume stores take this approach, and it is built into some of the FoxyCart integrations supplied by 3rd parties.
While both of those options can work well depending on your needs, the most robust method of product validation is to use FoxyCart's HMAC Verification. This method prevents spoofing attempts by requiring all links and form values to be "signed" using your "secret" (which in this case is [[v:2.0:store_secret|your store's "secret key"]] available from the "advanced" setting page). Only values signed with your secret key will be accepted by FoxyCart, allowing you to prevent product spoofing before orders are placed.
==== Do You Need FoxyCart's HMAC Product Verification? =====
It depends. If you have low order volume, allow customer-set pricing (like for donations), or otherwise would catch any funny business, you might not need it. We do generally recommend turning it on, especially if you're using a more powerful or flexible CMS where you could implement it automatically.
That said, there are other options that might make more sense:
* The [[./pre_payment_webhook|pre-payment webhook]] can be used to validate carts before you accept payment. This can also be used to verify availability, perform an anti-fraud check, or other custom validation.
* The [[./webhooks|transaction webhook]] can be used to validate things after the transaction's done.
* [[https://github.com/Foxy/foxy-cloudflare-addtocart-signing|Cloudflare workers]] can automatically sign your page. This is a bit more complicated to set up, but will handle things automatically for you.
Foxy gives you a lot of flexibility, so just [[https://foxy.io/contact|reach out to us]] if you're confused. We're here to help.
===== Language and Site Independent Solutions =====
Regardless of how you've built your site, you can use our Cloudflare Worker script, along with Cloudflare, to automatically protect your site. More info is here:
* [[https://github.com/Foxy/foxy-cloudflare-addtocart-signing|Foxy Cloudflare Worker: Add-to-cart Signer]]
Please let us know if you run into trouble getting that set up.
===== Language Specific Examples & Helper Files =====
While you certainly can build your validation functionality from scratch, it may be a boon to use an existing library. Currently we only have official code for PHP but if you're interested in porting the PHP version to another language please [[https://forum.foxycart.com/|let us know]] and we'd be happy to provide assistance.
==== Webflow: Pre-Payment Webhook ====
If you're using Webflow, you can set up a serverless function on Netlify.com to verify your product prices against your Webflow product collection. This is slightly technical, but hopefully will be more straightforward than it looks, and will generally be free unless you're doing really heavy volume. Setting it up starts with a single click.
* [[https://github.com/Foxy/foxy-node-netlify-functions|Netlify Serverless Functions for the Foxy.io API]]
* [[https://github.com/Foxy/foxy-node-netlify-functions/tree/main/src/functions/datastore-integration-webflow|Webflow pre-payment webhook details]]
Let us know if you need help getting it set up.
==== PHP: Automatic Validation with Minimal Effort ====
[[https://github.com/FoxyCart/FoxyCart-Cart-Validation--PHP|FoxyCart Cart Validation, PHP]] (on GitHub) to **automatically sign an entire HTML page** (recommended) or to sign individual links, inputs. This code is capable of automatically signing //all links and forms// on an entire HTML page, which makes it trivially easy to add to an existing site or CMS. Read the wiki on GitHub for instructions on how to implement this PHP script.
==== PHP: Helper Function ====
If you don't want to sign an entire HTML page at once but to make your own links, you can use this handy function:
function get_verification($var_name, $var_value, $var_code, $var_parent_code = "", $for_value = false) {
$api_key = "your_api_key_here";
$encodingval = htmlspecialchars($var_code . $var_parent_code . $var_name . $var_value, ENT_COMPAT);
$label = ($for_value) ? $var_value : $var_name;
return $label . '||' . hash_hmac('sha256', $encodingval, $api_key) . ($var_value === "--OPEN--" ? "||open" : "");
}
With this function, for each product you want to hash, you need to provide the ''name'', ''value'', and ''code'' to it. The ''name'' and ''value'' arguments are the name and value pair of the particular product attribute you're hashing, and the ''code'' argument is the product's code value. **Note the value you pass off to the //get_verification// function should still be the actual value, not a url encoded version.**
This function will return a hash based on the arguments provided - and return something that would look like this:
mycode||of02h1340wef9neq230sfnwr4340adn7z
**Example**
Take for example the following product: "Flute Warm-Up Book" priced at $1.99 with an SKU of "warmups".
To get the hash for the code, you would call the function like this: ''get_verification('code', 'warmups', 'warmups')''. Likewise, for the product name it would be ''get_verification('name', 'Flute Warm-Up Book', 'warmups')'' and the price would be ''get_verification('price', '1.99', 'warmups)''
In a form, in it's vanilla, unsigned state, that would look like this:
Updated to include the helper function would look like this:
**Where should the signature be placed in a form element?** Note that the signature can be appended to either the ''name'' or the ''value'' of an input. Either will work just fine, although for radio inputs and select dropdowns, the signature //must// be appended to the ''value'' as the ''name'' is the same for all options of the field. See [[:#a_basic_overview|the implementation details]] for more information on that.
In a link in it's vanilla state would look like this:
https://yourdomain.foxycart.com/cart?name=Flute%20Warm-Up%20Book&code=warmups&price=1.99
Using PHP you could build out the same link using the helper function like this:
$atc = "https://yourdomain.foxycart.com/cart";
$atc .= "?" . get_verification("name", "Flute Warm-Up Book", "warmups") . "=Flute%20Warm-Up%20Book";
$atc .= "&" . get_verification("code", "warmups", "warmups") . "=warmups";
$atc .= "&" . get_verification("price", "1.99", "warmups") . "=1.99";
**Bundled Products**
If you're signing a link or form for a bundled product (say, a $1.99 warm-up exercises book comes with the purchase of a C Flute), you need to also specify the optional ''parent_code'' value to the ''get_verification()'' function.
===Example: Building a form element in PHP===
If the example product above also belonged to a parent product with a code of ''cflute'', the form verification would look like this, including the parent product, "C Flute":
===Example: Building a link string in PHP===
$atc = 'https://yourdomain.foxycart.com/cart';
// Parent
$atc .= '?' . get_verification('code', 'cflute', 'cflute') . "=cflute";
$atc .= "&" . get_verification('name', 'C Flute', 'cflute') . "=C%20Flute";
$atc .= "&" . get_verification('price', '13999', 'cflute') . "=13999";
// Child
$atc .= '&2:' . get_verification('code', 'warmups', 'warmups', 'cflute') . "=warmups";
$atc .= "&2:" . get_verification('name', 'Flute Warm-Up Book', 'warmups', 'cflute') . "=Flute%20Warm-Up%20Book";
$atc .= "&2:" . get_verification('price', '1.99', 'warmups', 'cflute') . "=1.99";
$atc .= "&2:" . get_verification('parent_code', 'cflute', 'warmups', 'cflute') . "=cflute";
echo $atc . "\n";
**Appending hash to value (select and radio inputs)**
If you're signing a ''select'' or ''radio'' input element in a form, you need to append the verification to the ''value'' attribute rather than the ''name''. To do this, simply specify ''true'' for the optional ''for_value'' argument like this:
Note there is a empty string for the fourth argument for the ''get_verification()'' function calls above. This is for the ''parent_code'' argument, which is only needed if your product is a bundled product.
**User editable fields**
If an input does not have a static value (such as a user-editable field), you can enter ''%%--OPEN--%%'' for the value. For example, that could look like this within your form before signing:
After signing, it will include ''||open'' at the end of the signature, signifying that any value can be entered into that field, although the fields name will still be validated. To be clear, you won't add the ''||open'' suffix yourself, that will be handled by the signing process. The above input would look something like this after signing:
If you are signing through the administration, you will see the ''%%--OPEN--%%'' value in the input after successfully signing the form. You can remove the value at that point, and replace it with whatever default value you want.\\
**Special Notes:** If you values have spaces or special characters, see the [[#important_notes|Important Notes]] section below. Some name/value pairs also don't need to be signed - view the [[#excluded_namevalue_pairs|Excluded Name/Value Pairs]] section for information on that.
==== Manual Signing via FoxyCart Administration ====
If you're building a static HTML site, or just want to sign a couple forms, you can manually sign a form from your stores FoxyCart administration.
- Firstly, build out the link or form as you normally would, building it out entirely.
- On the Sample Code page of your stores FoxyCart administration, paste your link or form (or multiples thereof) into the text box under 'Step 2', replacing the code that was already in there. Ensure that your links are complete link elements and any forms are complete forms wrapped in a form element with the action attribute pointed to your store URL like a normal add to cart form would.
- Click the 'Encode HTML' button below the text box, and your encoded version of the code you pasted will be displayed in a text box underneath that button.
- Paste the encoded HTML into your page and you're done!
One thing to note here, if you need to make a change to an existing signed link or form, you'll need to rebuild it from scratch without signing.
===== The How: Implementation Details =====
==== A Basic Overview ====
Whether you use our PHP script or write your own, the basic idea is to sign each and every product option, excluding things that aren't actually "product options" like ''cart=checkout'' or ''output=json'' and such. There are two steps involved.
First, we concatenate the following values:
* the product code,
* the form element's ''name'', (or the ''name'' part of the link's name/value pair)
* and the form element's ''value'' (if it's a pre-set value) or the string ''--OPEN--'' (if it's an open-input field, like ''quantity'').
Second, we HMAC SHA-256 that string, and append the resulting 64-character hash to the ''name'' attribute of the input with double pipes (''||'') in between. For example, if we had a product with a code of ''ABC123'' and an input with ''name="name" value="My Example Product"'', we'd hash the string ''ABC123nameMy Example Product'' and append that hash to the name. The resulting html output would look like this:
Add To Cart
(We're going to be using a form as an example, but all of it holds true for links.)
You might realize, however, that this approach wouldn't work for ''