11

Currently we don't use contact hashes but we'd really like to.

My understanding is that contact hashes are exposed as a token that can be used in CiviMail where they are appended to links. They're unique to each user (stored in the contact table), and they look like they're an md5 of something.

We're able to use them fine, but my question is around they work.

Are they ever regenerated?

Are there best practices to keeping them secure?

From what I can gather they carry the same privacy considerations as a forgot password link. Though where a forgot password link give the link holder full control of the account, the contact hash only allows forms to be prefilled (still exposes PII).

I'm lead to believe contact hashes should be regenerated within a hour of being clicked (some people might stuff something up, and need to click the link again).

Can anyone shed any light on this?

(sorry for the brain dump)

2 Answers 2

5
  1. Yes contact hashes are regenerated every time they are created. the microtime is embedded in the contact hash.

  2. Contact hashes have a specific life time (by default, this is 7 days). So they are invalid after 7 days

4
  • "regenerated every time they are created", created or used? Or are they just regenerated on a time schedule?
    – Owen Kelly
    Apr 10, 2015 at 0:19
  • created == every time the code has to generate a contact hash. So for example, if u embed a contact hash token in a mailing, every time a mailing is sent to a person, the contact hash is different. When a contact hash is used in a url (i.e. read) no change is made Apr 10, 2015 at 3:32
  • Ah yep. So they're regenerate weekly, and that's it. Thanks for the help lobo
    – Owen Kelly
    Apr 10, 2015 at 3:33
  • 1
    Perhaps worth noting that the "7 days" is configurable via the UI at Admin > Settings > Misc ie civicrm/admin/setting/misc?reset=1
    – petednz - fuzion
    Jan 13, 2017 at 23:27
12

My understanding is that Lobo's answer above is slightly misleading (at least if you read it 100% literally--the gist what he is saying is exactly correct, as usual, but the exact terminology may lead some of us astray).

The contact hash is a value stored in the civicrm_contact table of the database (i.e., civicrm_contact.hash).

The contact hash is used to generate the contact checksum, which is more precisely what Lobo is discussing above. It's easy to conflate to two, because they are closely related. To add to the confusion, the contact checksum is itself a type of hash--but a different one from that hash found in the database under civicrm_contact.hash.

The contact checksum isn't stored in the database but is generated afresh by a call to a CiviCRM function each time it is used. The checksum encodes the time it was generated and also the time it should expire, along with the pertinent civicrm_contact.hash to make a new hash. So the checksum is designed to be shared (e.g. with the contact associated with it) to allow temporary access to various parts of CiviCRM, and it expires after a set period. By default, checksums that civicrm generates will expire after 7 days. But tou can set that expiration time period to whatever you want, including infinite, when you request a checksum to be generated.

The civicrm_contact.hash, by contrast is a secret and shouldn't be shared publicly--probably not even with the contact. It is something like the user's password, and someone with the contact hash could use it to permanently gain access to do anything with that contact that they could do with a valid checksum: edit that contact's information, view payment and general CiviCRM history for that contact, register for events or making payments on behalf of that contact, etc.

Also, as far as I know, the contact hash is never automatically regenerated. (If it were ever changed or regenerated, it would immediately invalidate any checksums that happened to have been recently issued for that contact--generally not something that you would want to do. If you want to give temporary access that expires, you would send them a checksum that expires, not change the hash.)

So leaking the hash is not quite as serious as, say, leaking a person's Drupal password, but it is still a privacy breach and something you'll want to avoid in almost all cases I can imagine.

Just for example, someone with the hash could generate a valid checksum to allow access to that CiviCRM contact at any time.

In short, when working to allow contacts temporary/non-password access to view or update their account info, you want to use the checksum--which is *based on the *contact hash** but not the hash itself. The hash is a secret and you should keep it secret.

An example of how to get the contact's checksum & turn it into a URL that could be used to edit the contact's profile:

// initialize civicrm stuff
// Source: https://forum.civicrm.org/index.php?topic=21438.0
civicrm_initialize(true);
require_once(drupal_get_path('module', 'civicrm').'/../CRM/Contact/BAO/Contact/Utils.php');
require_once(drupal_get_path('module', 'civicrm').'/../CRM/Utils/Mail.php');

// try looking up the contact
$result = civicrm_api('Contact', 'get', array('email' => $email, 'version' => 3));
foreach($result['values'] as $contact) {
    // generate a url for this contact
    $checksum = CRM_Contact_BAO_Contact_Utils::generateChecksum($contact['contact_id'], time(), 24); // 24 hour checksum
    $query = "reset=1&gid=9&id=".$contact['contact_id']."&".$checksum;
    $url = CRM_Utils_System::url('civicrm/profile/edit', $query, true, null, false);

Code from CRM/Contact/BAO/Contact/Utils.php showing the header for the checksum generation function

  /**
   * Generate a checksum for a contactID
   *
   * @param int    $contactID
   * @param int    $ts         timestamp that checksum was generated
   * @param int    $live       life of this checksum in hours/ 'inf' for infinite
   * @param string $hash       contact hash, if sent, prevents a query in inner loop
   *
   * @return array ( $cs, $ts, $live )
   * @static
   * @access public
   */
  static
  function generateChecksum($contactID, $ts = NULL, $live = NULL, $hash = NULL) {

Header from Utils.php showing how to use the checksum validate function:

  /**
   * Make sure the checksum is valid for the passed in contactID
   *
   * @param int    $contactID
   * @param string $inputCheck the checksum to validate
   *
   * @return boolean           true if valid, else false
   * @static
   * @access public
   */
  static
  function validChecksum($contactID, $inputCheck)  ...

Sources: https://wiki.civicrm.org/confluence/display/CRMDOC/Tokens

Get or generate a contact checksum via API?

https://drupal.stackexchange.com/questions/140841/how-to-ensure-civicrm-checksum-cid-are-valid-in-custom-drupal-module

http://backofficethinking.com/blog/civicrm-tip-did-you-know-you-can-use-checksum-tokens-webforms

2
  • 1
    Whoa. I don't know who you are, but your first answer definitively corrected Lobo, who started the CiviCRM project. Jan 13, 2017 at 19:08
  • This answer helped a lot. I notice that the contact's hash is available as a field in Views so it's tempting to use but looks like I should avoid this if at all possible.
    – herb
    Nov 18, 2020 at 16:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.