diff --git a/_test/tests/inc/PassHash.test.php b/_test/tests/inc/PassHash.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6cb070903861fd7bf0859b24fc939de9c9543ab
--- /dev/null
+++ b/_test/tests/inc/PassHash.test.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * Class PassHash_test
+ *
+ * most tests are in auth_password.test.php
+ */
+class PassHash_test extends PHPUnit_Framework_TestCase {
+
+    function test_hmac(){
+        // known hashes taken from https://code.google.com/p/yii/issues/detail?id=1942
+        $this->assertEquals('df08aef118f36b32e29d2f47cda649b6', PassHash::hmac('md5','data','secret'));
+        $this->assertEquals('9818e3306ba5ac267b5f2679fe4abd37e6cd7b54', PassHash::hmac('sha1','data','secret'));
+
+        // known hashes from https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
+        $this->assertEquals('74e6f7298a9c2d168935f58c001bad88', PassHash::hmac('md5','',''));
+        $this->assertEquals('fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', PassHash::hmac('sha1','',''));
+        $this->assertEquals('80070713463e7749b90c2dc24911e275', PassHash::hmac('md5','The quick brown fox jumps over the lazy dog','key'));
+        $this->assertEquals('de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9', PassHash::hmac('sha1','The quick brown fox jumps over the lazy dog','key'));
+
+    }
+}
\ No newline at end of file
diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php
index 080fb47780e0e5fc6c503a14fe7ac75f39cd1f22..61bd74939f8eb4e48f29ce82f25030f3afdab28c 100644
--- a/inc/PassHash.class.php
+++ b/inc/PassHash.class.php
@@ -494,4 +494,51 @@ class PassHash {
         $this->init_salt($salt, 8, false);
         return ':B:'.$salt.':'.md5($salt.'-'.md5($clear));
     }
+
+    /**
+     * Wraps around native hash_hmac() or reimplents it
+     *
+     * This is not directly used as password hashing method, and thus isn't callable via the
+     * verify_hash() method. It should be used to create signatures and might be used in other
+     * password hashing methods.
+     *
+     * @see hash_hmac()
+     * @author KC Cloyd
+     * @link http://www.php.net/manual/en/function.hash-hmac.php#93440
+     *
+     * @param string $algo Name of selected hashing algorithm (i.e. "md5", "sha256", "haval160,4",
+     *                     etc..) See hash_algos() for a list of supported algorithms.
+     * @param string $data Message to be hashed.
+     * @param string $key  Shared secret key used for generating the HMAC variant of the message digest.
+     * @param bool $raw_output When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
+     *
+     * @return string
+     */
+    public static function hmac($algo, $data, $key, $raw_output = false) {
+        // use native function if available and not in unit test
+        if(function_exists('hash_hmac') && !defined('SIMPLE_TEST')){
+            return hash_hmac($algo, $data, $key, $raw_output);
+        }
+
+        $algo = strtolower($algo);
+        $pack = 'H' . strlen($algo('test'));
+        $size = 64;
+        $opad = str_repeat(chr(0x5C), $size);
+        $ipad = str_repeat(chr(0x36), $size);
+
+        if(strlen($key) > $size) {
+            $key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
+        } else {
+            $key = str_pad($key, $size, chr(0x00));
+        }
+
+        for($i = 0; $i < strlen($key) - 1; $i++) {
+            $opad[$i] = $opad[$i] ^ $key[$i];
+            $ipad[$i] = $ipad[$i] ^ $key[$i];
+        }
+
+        $output = $algo($opad . pack($pack, $algo($ipad . $data)));
+
+        return ($raw_output) ? pack($pack, $output) : $output;
+    }
 }
diff --git a/inc/auth.php b/inc/auth.php
index af9f35b38a9ddd804e5a5df9cb7443bd857021f8..1f8489f03fe8e195bb27974cc790e72db7b8aa2d 100644
--- a/inc/auth.php
+++ b/inc/auth.php
@@ -993,7 +993,7 @@ function act_resendpwd() {
         }
 
         // generate auth token
-        $token = md5(auth_cookiesalt().$user); //secret but user based
+        $token = md5(uniqid(mt_rand(), true)); // random secret
         $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
         $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
 
diff --git a/inc/common.php b/inc/common.php
index 4d939ac772af192bf5f179a2fb9f6a2f90b1ad1f..55c5b5ac4ef01bb0099854cba0228ca132711fea 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -56,7 +56,7 @@ function stripctl($string) {
  * @return  string
  */
 function getSecurityToken() {
-    return md5(auth_cookiesalt().session_id().$_SERVER['REMOTE_USER']);
+    return PassHash::hmac('md5', session_id().$_SERVER['REMOTE_USER'], auth_cookiesalt());
 }
 
 /**
@@ -470,7 +470,7 @@ function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false)
     if(preg_match('#^(https?|ftp)://#i', $id)) {
         $xlink .= 'lib/exe/fetch.php';
         // add hash:
-        $xlink .= '?hash='.substr(md5(auth_cookiesalt().$id), 0, 6);
+        $xlink .= '?hash='.substr(PassHash::hmac('md5', $id, auth_cookiesalt()), 0, 6);
         if($more) {
             $xlink .= $sep.$more;
             $xlink .= $sep.'media='.rawurlencode($id);
diff --git a/inc/fetch.functions.php b/inc/fetch.functions.php
index 5801e96fa60e4fdd49e70d21c499e1eb50e72f25..ea524a37a93a38594833dbe954133825c2f190bb 100644
--- a/inc/fetch.functions.php
+++ b/inc/fetch.functions.php
@@ -99,7 +99,7 @@ function checkFileStatus(&$media, &$file, $rev = '', $width=0, $height=0) {
     //media to local file
     if(preg_match('#^(https?)://#i', $media)) {
         //check hash
-        if(substr(md5(auth_cookiesalt().$media), 0, 6) !== $INPUT->str('hash')) {
+        if(substr(PassHash::hmac('md5', $media, auth_cookiesalt()), 0, 6) !== $INPUT->str('hash')) {
             return array(412, 'Precondition Failed');
         }
         //handle external images
diff --git a/inc/media.php b/inc/media.php
index e29a47631030d8da0ff68bd258f853fee93e406c..18148a44607f3a9131d3b10d46c092f0a65bab66 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -1879,20 +1879,21 @@ function media_crop_image($file, $ext, $w, $h=0){
  * cropped images have been internally generated - and prevent external
  * DDOS attacks via fetch
  *
+ * @author Christopher Smith <chris@jalakai.co.uk>
+ *
  * @param string  $id    id of the image
  * @param int     $w     resize/crop width
  * @param int     $h     resize/crop height
- *
- * @author Christopher Smith <chris@jalakai.co.uk>
+ * @return string
  */
 function media_get_token($id,$w,$h){
     // token is only required for modified images
     if ($w || $h) {
-        $token = auth_cookiesalt().$id;
+        $token = $id;
         if ($w) $token .= '.'.$w;
         if ($h) $token .= '.'.$h;
 
-        return substr(md5($token),0,6);
+        return substr(PassHash::hmac('md5', $token, auth_cookiesalt()),0,6);
     }
 
     return '';