====== 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|cryptographic 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. ==== 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 your API key). 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 really depends on a few things, including your order volume, your order processing flow, the nature of your products, and your cost to implement versus the potential payoff. That said, if you're worried about price spoofing we strongly recommend implementing it. If you only have a few products then it's only a little extra work up front. If you have many products then it can be added to your CMS and handled automatically by your site, in the background. ===== 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 [[http://forum.foxycart.com/|let us know]] and we'd be happy to provide assistance. ==== PHP: Automatic Validation with Minimal Effort ==== [[http://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. ===== The How: Implementation Details ===== ==== A Basic Overview ==== 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. We do this by first concatenating the following values: * the product code, * the form element's ''name'', * 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''). We then HMAC SHA-256 that string, and append it 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''. We'd then append the hash to the ''name'' attribute like this: Add To Cart (We're going to be using a form as an example, but all of it holds true for links as well.) You might realize, however, that this approach wouldn't work for ''

The important thing to understand with the "form" method is that each and every ''value'' that relates to a product must be verified, since each ''value'' can impact the price. For example, if we were only to verify the name, code, and price without verifying the size, the size ''value'' would be able to modify the price (using a price modifier), thus bypassing our verification. Similarly, each and every ''value'' must be only be valid for the correct product (as determined by the ''code''), or a price modifying value for Product A could be applied to Product Z. In order to generate an HMAC SHA-256 for each input, let's look at what the PHP function would look like (though you can consider this pseudocode if PHP isn't your language of choice): $secret = "Your store's API key."; hash_hmac('sha256', 'abc123nameExample T-Shirt', $secret); // Hash of the "name" input hash_hmac('sha256', 'abc123price25', $secret); // Hash of the "price" input The important pieces are the middle arguments (the ''abc123nameExample T-Shirt'' and ''abc123price25''), which is the "message" in the HMAC. Notice that the message is the product ''code'' (''abc123''), the ''name'' of the product option, and the ''value'' of the product option. They're concatenated (that means "stuck together" in programmer-speak) into one string, and that's what your values are verified against. This HMAC hash is then appended to the ''value'' of the ''input'', with a double pipe, ''||'' (the "pipe" is the vertical line, generally available by pressing ''Shift+\''). It looks like this: ==== User-Editable Inputs ==== While this method will work for fields that are not user editable and need to be explicitly set, it won't do us much good on fields that require user input, such as a quantity selection, a "Custom Message" textarea for a printed notecard, or a "Registrant Name" field for a conference registration. Because the ''value'' is determined by the user in those cases, the hash must be appended to the ''name'' field instead of the ''value'' field. Further, because the ''value'' isn't known at the time of hashing, rather than running the HMAC on the ''value'', we'll use the keyword ''--OPEN--'' instead. So the HMAC will look like this: hash_hmac('sha256', 'abc123quantity--OPEN--', $secret); // Hash of the "quantity" input, allowing for user-generated values And the HTML will look like this (below). Note that we add ''||open'' after the hash in order for FoxyCart to accept user-generated values in that field. ==== Multiple Products in One Form ==== If you're adding multiple products at one time using the numeric prefixes you must generate the hashes //without the numeric prefix//. So, for example:

You would generate the hash for ''2:name'' without the ''2:'': hash_hmac('sha256', 'hat123nameBaseball Hat', $secret); // Correct ===== Important Notes ===== * Before you begin, you must ensure that you have enabled cart validation from your store's admin page ('Store' → 'Advanced' → 'would you like to enable cart validation?'). * A ''code'' value //is required// to use validation. Without a code there will be nothing to tie options to specific products, thus making the validation potentially vulnerable. * Excluded (''x:'' prefixed) and session (''h:'' prefixed) variables are not verified, and should not be hashed. * You should not use ''--OPEN--'' as the value of a product option, as this will allow the field to be modified. * Any curly brackets, ''{ }'', are stripped from any ''OPEN'' fields, preventing a user-generated value from modifying the price, weight, code, or category. * If you need to use **spaces**, **periods**, or **other special characters** in the ''name'' of an input you may experience difficulties. Spaces and periods are converted to underscores when FoxyCart generates the HMAC against which to verify the submitted hash. We recommend avoiding spaces and periods in the ''name''s of your inputs, but if you do need to use them, generate your hashes against underscores instead. Additionally, generate your hashes against the URL decoded values of any encoded entities rather than the URL encoded values. For example, ''name="Color %26 Texture."'' would become ''name="Color_&_Texture_"'' when verified by FoxyCart. ===== Limitations & Considerations ===== ==== Validation is All or Nothing ==== Because it wouldn't be very useful to validate some product options while allowing unvalidated options elsewhere (as any nefarious customer could easily just pass in whatever extra options they wanted to modify the product), validation is //all or nothing//. If it's turned on for your store then //each and every name/value pair// on //each and every add-to-cart// link and form must be validated. ==== Big Forms ==== Forms with many options may push [[wp>Internet_Explorer|Internet Explorer]] past its [[wp>Internet_Explorer|maximum URL length]] of 2,083 characters. If you have relatively short options (one or two words each) you may be able to get 20+ fields in a normal cart submission using the HMAC method. If you have longer fields (for example, paragraphs of text for a personal message) then the number of fields you are able to submit will be reduced accordingly. Unfortunately, this is a hard limit in IE versions up to 8 (at the time of this writing; it may very well be the same in IE9+). Fortunately, it only affects ''GET'' cart submissions. ''POST'' submissions are unaffected. Because most AJAX and JSON(P) cart implementations (including the default FoxyCart cart overlay) rely on serializing form fields to ''GET'' in order to bypass cross-domain javascript issues, if you do run into this you may need to take a different approach. While there are a handful of options, including faking a JSONP call by POSTing to a hidden iframe, the easiest option is to eliminate any modal windows and ''POST'' to the cart URL directly. If you need assistance please post in [[http://forum.foxycart.com|our forum]]. As always, **TEST** your forms in Internet Explorer to ensure that your customers don't run into any issues. ==== Advanced Forms, Option Dependencies ==== If you have very advanced forms with multiple dependencies and relationships between values (ie. Option A cannot be paired with Option D and requires Option X), that validation must be handled on your end (either before submission to the cart, by validating the request on your end before adding to the cart, and/or validating after the purchase by using the [[.:transaction_xml_datafeed|XML datafeed]] or [[.:api|the API]]). Because the requirements for those types of situations can vary radically from business to business there would be no solid way to build a "one-size-fits-all" type of solution that would be easier than you building yourself it in the first place. ==== Tampering by Removing Options: Don't Make Assumptions ==== It's important to understand that this validation scheme will prevent tampering with existing product options as well as preventing the addition of product options, there is nothing to prevent a user from //removing a product option// from an add-to-cart form. For this reason we recommend examining how you use product option modifiers if you're modifying the price. It's generally going to be a better idea to start with a set base price that is the upper limit, then adjust the price //down// using price modifiers rather than starting low and modifying higher. For example, if you have a conference registration that's $500 for adults and $200 for children, set the price to $500, then on the radio button to select a child's registration adjust it down, like this: