public function read() { // grab cookie or URL from environment // since this is sample we'll just pull from global env $str = $GLOBALS['userdata']; echo "\n\nSTR = {$str}\n"; $ok = SecureString1::validate($str, $this->keys); if (!$ok) { // do something return array(); } parse_str($str, $ary); // now we have all the meta data to make policy decisions // perhaps the creation date is too old, and need to be expired. // perhaps strip out the meta data return $ary; }
/** * what happens when the SS meta data fields are in the * payload? * * Not sure I like this behavior, but I'm just documenting it. * parse_str overwrites the original value when a key is duplicated * parse_str has all sorts of other issues. */ public function testPhpParseStrBehavior() { $keys = array(1 => hash('md5', 'this is my secret key', true)); $salt = '1234567890'; $macd = SecureString1::create(SecureString1::PREFIX_VERSION . '100' . SecureString1::PREFIX_CREATED . 'blah' . SecureString1::PREFIX_KEYID . '666', $salt, $keys, 1); $this->assertTrue(SecureString1::validate($macd, $keys)); parse_str($macd, $parts); $this->assertEquals($parts['_ver'], 1); $this->assertEquals($parts['_kid'], 1); $this->assertNotEquals($parts['_now'], 'blah'); }
public function read($now = 0) { // get cookie $str = null; if (isset($_COOKIE[$this->cookie_name])) { $str = $_COOKIE[$this->cookie_name]; } if (empty($str) || $str == 'deleted') { $this->payload = array(); return self::REASON_MISSING; } // decode any wrappers if (SecureString1::b64_chars($str)) { // if 100% b64 chars, then decode it $str = SecureString1::b64_urldecode($str); } else { // Some proxies will muck with '=' and '&' inspite of // the spec saying these are ok if (strpos($str, '%') !== false) { $str = urldecode($str); // sometimes double encoding happens if (strpos($str, '%') !== false) { $str = urldecode($str); } } } // Now check for cryptographic integrity $ok = SecureString1::validate($str, $this->keys); if (!$ok) { return self::REASON_INVALID; } // from query string to array parse_str($str, $this->payload); // cookie is cryptographically valid, but check for policy on age if ($now === 0) { $now = time(); } $created = $this->payload[SecureString1::PREFIX_CREATED]; $elapsed = $now - $created; if ($elapsed > $this->window) { return self::REASON_EXPIRED; } // Hmmm someone's got a clock skew. I'll you figure out what to do if ($created > $now) { return self::REASON_CLOCK_SKEW; } // valid return self::REASON_OK; }
// ok here's some keys..... ideally from a config file // notice how the plain-text secret is md5'd to scramble the bits $keys = array(1 => hash('md5', "this is my secret key 1", true)); $mac = SecureString1::create($qs, $keys, 1); print $mac . "\n"; // if we do it again, the MAC is different (due to the salting) $mac = SecureString1::create($qs, $keys, 1); print $mac . "\n"; // the SecureString perserves query string formating // so you can break it apart just like this: parse_str($mac, $parts); // and now you have all the meta data in addition to your // data. print_r($parts); // Nice thing about PHP is that the associative array // keeps order. So we can reassemble the parts as is // and it still validates // print SecureString1::validate(http_build_query($parts), $keys) ? "same\n" : "different\n"; // like wise we can tamper with one element // and it won't re-validate $parts['cow'] = 'juice'; print_r($parts); print SecureString1::validate(http_build_query($parts), $keys) ? "same\n" : "different\n"; // and of course we can just directly tamper with it $bad = $mac; $bad[20] = '*'; // or anything, or add data or truncate print SecureString1::validate($bad, $keys) ? "same\n" : "different\n"; print_r(substr(SecureString1::b64_urlencode(pack("L", mt_rand())), 0, 6)); print "\n";