diff --git a/include/class.config.php b/include/class.config.php
index 6ce3716b931d710751b6ecc122f31798a7b459fb..32f5ca702feb2855e52d437ea8d4bbb625354ded 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -176,6 +176,20 @@ extends VerySimpleModel {
             $this->updated = SqlFunction::NOW();
         return parent::save($this->dirty || $refetch);
     }
+
+    // Clean password reset tokens that have expired
+    static function cleanPwResets() {
+        global $cfg;
+
+        if (!$cfg || !($period = $cfg->getPwResetWindow())) // In seconds
+            return false;
+
+        return ConfigItem::objects()
+             ->filter(array(
+                'namespace' => 'pwreset',
+                'updated__lt' => SqlFunction::NOW()->minus(SqlInterval::SECOND($period)),
+            ))->delete();
+    }
 }
 
 class OsticketConfig extends Config {
diff --git a/include/class.cron.php b/include/class.cron.php
index 232d6bf2bee90bad3824e238ee77812ee411a187..5db0a5b7b1aa6e898096e1cf964fc90d786d76fa 100644
--- a/include/class.cron.php
+++ b/include/class.cron.php
@@ -56,6 +56,11 @@ class Cron {
         DbSessionBackend::cleanup();
     }
 
+    function CleanPwResets() {
+        require_once(INCLUDE_DIR.'class.config.php');
+        ConfigItem::cleanPwResets();
+    }
+
     function MaybeOptimizeTables() {
         // Once a week on a 5-minute cron
         $chance = rand(1,2000);
@@ -106,6 +111,7 @@ class Cron {
         self::TicketMonitor();
         self::PurgeLogs();
         self::CleanExpiredSessions();
+        self::CleanPwResets();
         // Run file purging about every 10 cron runs
         if (mt_rand(1, 9) == 4)
             self::CleanOrphanedFiles();