Skip to content

Commit

Permalink
move file cookies to class
Browse files Browse the repository at this point in the history
  • Loading branch information
splitbrain committed Dec 6, 2023
1 parent 09b1e97 commit c6d794b
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 79 deletions.
70 changes: 70 additions & 0 deletions FileCookie.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace dokuwiki\plugin\captcha;

/**
* Remember the issuing (and use) of CAPTCHAs by placing a file on the server
*
* This is used to prevent replay attacks. It is generated when the captcha form
* is shown and checked with the captcha check. Since we can not be sure about the
* session state (might be closed or open) we're not using it.
*
* We're not using the stored values for displaying the captcha image (or audio)
* but continue to use our encryption scheme. This way it's still possible to have
* multiple captcha checks going on in parallel (eg. with multiple browser tabs)
*/
class FileCookie
{
protected $path;

/**
* Initialize the cookie
*
* @param $fixed string the fixed part, any string
* @param $rand float some random number between 0 and 1
*/
public function __construct($ident, $rand)
{
global $conf;
$path = $conf['tmpdir'] . '/captcha/' . date('Y-m-d') . '/' . md5($ident . $rand) . '.cookie';
io_makeFileDir($path);
}

/**
* Creates a one time captcha cookie
*/
public function set()
{
touch($this->path);
}

/**
* Checks if the captcha cookie exists and deletes it
*
* @return bool true if the cookie existed
*/
public function check()
{
if (file_exists($this->path)) {
unlink($this->path);
return true;
}
return false;
}

/**
* remove all outdated captcha cookies
*/
public static function clean()
{
global $conf;
$path = $conf['tmpdir'] . '/captcha/';
$dirs = glob("$path/*", GLOB_ONLYDIR);
$today = date('Y-m-d');
foreach ($dirs as $dir) {
if (basename($dir) === $today) continue;
if (!preg_match('/\/captcha\//', $dir)) continue; // safety net
io_rmdir($dir, true);
}
}
}
5 changes: 2 additions & 3 deletions action.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use dokuwiki\Extension\EventHandler;
use dokuwiki\Extension\Event;
use dokuwiki\Form\Form;
use dokuwiki\plugin\captcha\FileCookie;
use dokuwiki\plugin\captcha\IpCounter;

/**
Expand Down Expand Up @@ -227,9 +228,7 @@ public function handle_indexer(Event $event, $param)
$last = @filemtime($lastrun);
if (time() - $last < 24 * 60 * 60) return;

/** @var helper_plugin_captcha $helper */
$helper = plugin_load('helper', 'captcha');
$helper->cleanCaptchaCookies();
FileCookie::clean();
touch($lastrun);

$event->preventDefault();
Expand Down
80 changes: 4 additions & 76 deletions helper.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use dokuwiki\Extension\Plugin;
use dokuwiki\plugin\captcha\FileCookie;
use dokuwiki\Utf8\PhpString;

/**
Expand Down Expand Up @@ -51,7 +52,8 @@ public function getHTML()
global $ID;

$rand = (float)(random_int(0, 10000)) / 10000;
$this->storeCaptchaCookie($this->fixedIdent(), $rand);
$cookie = new FileCookie($this->fixedIdent(), $rand);
$cookie->set();

if ($this->getConf('mode') == 'math') {
$code = $this->generateMath($this->fixedIdent(), $rand);
Expand Down Expand Up @@ -160,7 +162,7 @@ public function check($msg = true)
$rand === false ||
PhpString::strtolower($field_in) != PhpString::strtolower($code) ||
trim($field_hp) !== '' ||
!$this->retrieveCaptchaCookie($this->fixedIdent(), $rand)
!(new FileCookie($this->fixedIdent(), $rand))->check()
) {
if ($msg) msg($this->getLang('testfailed'), -1);
return false;
Expand All @@ -170,80 +172,6 @@ public function check($msg = true)

// endregion

// region Captcha Cookie methods

/**
* Get the path where a captcha cookie would be stored
*
* We use a daily temp directory which is easy to clean up
*
* @param $fixed string the fixed part, any string
* @param $rand float some random number between 0 and 1
* @return string the path to the cookie file
*/
protected function getCaptchaCookiePath($fixed, $rand)
{
global $conf;
$path = $conf['tmpdir'] . '/captcha/' . date('Y-m-d') . '/' . md5($fixed . $rand) . '.cookie';
io_makeFileDir($path);
return $path;
}

/**
* remove all outdated captcha cookies
*/
public function cleanCaptchaCookies()
{
global $conf;
$path = $conf['tmpdir'] . '/captcha/';
$dirs = glob("$path/*", GLOB_ONLYDIR);
$today = date('Y-m-d');
foreach ($dirs as $dir) {
if (basename($dir) === $today) continue;
if (!preg_match('/\/captcha\//', $dir)) continue; // safety net
io_rmdir($dir, true);
}
}

/**
* Creates a one time captcha cookie
*
* This is used to prevent replay attacks. It is generated when the captcha form
* is shown and checked with the captcha check. Since we can not be sure about the
* session state (might be closed or open) we're not using it.
*
* We're not using the stored values for displaying the captcha image (or audio)
* but continue to use our encryption scheme. This way it's still possible to have
* multiple captcha checks going on in parallel (eg. with multiple browser tabs)
*
* @param $fixed string the fixed part, any string
* @param $rand float some random number between 0 and 1
*/
protected function storeCaptchaCookie($fixed, $rand)
{
$cache = $this->getCaptchaCookiePath($fixed, $rand);
touch($cache);
}

/**
* Checks if the captcha cookie exists and deletes it
*
* @param $fixed string the fixed part, any string
* @param $rand float some random number between 0 and 1
* @return bool true if the cookie existed
*/
protected function retrieveCaptchaCookie($fixed, $rand)
{
$cache = $this->getCaptchaCookiePath($fixed, $rand);
if (file_exists($cache)) {
unlink($cache);
return true;
}
return false;
}

// endregion

// region Captcha Generation methods

/**
Expand Down

0 comments on commit c6d794b

Please sign in to comment.