aboutsummaryrefslogtreecommitdiff
path: root/pmwiki.php
diff options
context:
space:
mode:
authorpetko <petko@524c5546-5005-0410-9a3e-e25e191bd360>2023-04-28 09:04:59 +0000
committerpetko <petko@524c5546-5005-0410-9a3e-e25e191bd360>2023-04-28 09:04:59 +0000
commitc87c43fd1815ebccacd79d559d8e3cdaa59f1ccb (patch)
tree86a97799cc87cf70df8b5778bb9f73d30440accb /pmwiki.php
parentfba6096db3b8979409ae715836bb01831021de3d (diff)
downloadpmwiki.svn-c87c43fd1815ebccacd79d559d8e3cdaa59f1ccb.tar.bz2
Refactor pmtoken() to use session tokens, enable for core actions.
git-svn-id: svn://pmwiki.org/pmwiki/trunk@4435 524c5546-5005-0410-9a3e-e25e191bd360
Diffstat (limited to 'pmwiki.php')
-rw-r--r--pmwiki.php111
1 files changed, 43 insertions, 68 deletions
diff --git a/pmwiki.php b/pmwiki.php
index f12d4210..ff3dea66 100644
--- a/pmwiki.php
+++ b/pmwiki.php
@@ -71,7 +71,7 @@ $TimeISOZFmt = '%Y-%m-%dT%H:%M:%SZ';
$MessagesFmt = array();
$BlockMessageFmt = "<h3 class='wikimessage'>$[This post has been blocked by the administrator]</h3>";
$EditFields = array('text');
-$EditFunctions = array('AutoCheckToken', 'EditTemplate', 'RestorePage', 'ReplaceOnSave',
+$EditFunctions = array('EditTemplate', 'RestorePage', 'ReplaceOnSave',
'SaveAttributes', 'PostPage', 'PostRecentChanges', 'AutoCreateTargets',
'PreviewPage');
$EnablePost = 1;
@@ -856,36 +856,40 @@ function pmcrypt($str, $salt=null) {
return crypt($str);
}
-# generate or check a random one-time token to prevent CSRF
-function pmtoken($token = null) {
- global $SessionMaxTokens, $PmTokenFn;
+# generate or check a random session token to prevent CSRF
+# 0=get/set token, 1=check $_POST, 2=check $_GET,
+# 3=check $_COOKIE, -3=set $_COOKIE
+function pmtoken($check = 0, $abort = 0) {
+ global $EnablePmToken, $PmTokenFn, $FmtV, $InputValues;
+ static $called = 0, $cookie = 0;
+
if (IsEnabled($PmTokenFn) && function_exists($PmTokenFn))
return $PmTokenFn($token);
+
+ if (! IsEnabled($EnablePmToken, 1)) return true;
pm_session_start();
- if (!isset($_SESSION['pmtokens'])) $_SESSION['pmtokens'] = array();
- if (is_null($token)) { # create a one-time token
- $len = mt_rand(20,30);
- $token = "";
- while(strlen($token)<$len) {
- $token .= chr(mt_rand(32,126));
- }
- if (count($_SESSION['pmtokens']))
- $id = max(array_keys($_SESSION['pmtokens']))+1;
- else $id = 0;
- $_SESSION['pmtokens'][$id] = $token;
- if (IsEnabled($SessionMaxTokens, 0)) {
- $max = $SessionMaxTokens;
- $_SESSION['pmtokens'] = array_slice($_SESSION['pmtokens'], -$max, $max, true);
- }
- return "$id:" . md5($token);
+ $key = $FmtV['$TokenName'];
+
+ if (!isset($_SESSION['pmtoken'])) {
+ $token = $_SESSION['pmtoken'] = PmNonce().PmNonce();
}
- # else: check a token, if correct, delete it
- @list($id, $hash) = explode(':', $token);
- $id = intval($id);
- if (isset($_SESSION['pmtokens'][$id]) && $hash == md5($_SESSION['pmtokens'][$id])) {
- unset($_SESSION['pmtokens'][$id]);
- return true;
+ else $token = $_SESSION['pmtoken'];
+
+ if (! $called++) {
+ $InputValues[$key] = $FmtV['$TokenValue'] = $token;
+ }
+ if ($check == -3 && !$cookie++) pmsetcookie($key, $token);
+
+ if ($check <= 0) { # get the token
+ return $token;
}
+ # else: check the token
+ if ($check === 1 && $token === @$_POST[$key]) return true;
+ elseif ($check === 2 && $token === @$_GET[$key]) return true;
+ elseif ($check === 3 && $token === @$_COOKIE[$key]) return true;
+
+ # fail
+ if ($abort) Abort('? $[Token invalid or missing.]');
return false;
}
@@ -893,12 +897,10 @@ function PmNonce() {
if (function_exists('random_bytes')) {
$nonce = bin2hex(random_bytes(5));
}
- else $nonce = mt_rand();
+ else $nonce = 'x'.mt_rand().mt_rand();
return $nonce;
}
-
-
function StopWatch($x) {
global $StopWatch, $EnableStopWatch;
if (!$EnableStopWatch) return;
@@ -1613,7 +1615,14 @@ class PageStore {
return ($pagefile && file_exists($pagefile));
}
function delete($pagename) {
- global $Now, $PageExistsCache;
+ global $Now, $PageExistsCache, $TrashNameFmt;
+ if(IsEnabled($TrashNameFmt,0)) {
+ $tn = FmtPageName($TrashNameFmt, $pagename);
+ if(strpos($tn, '%')!==false) $tn = PSFT($tn, $Now);
+ $tn = MakePageName($pagename, $tn);
+ $page = $this->read($pagename);
+ $this->write($tn,$page);
+ }
$pagefile = $this->pagefile($pagename);
@rename($pagefile,"$pagefile,del-$Now");
unset($PageExistsCache[$pagename]); # PITS:01401
@@ -2438,39 +2447,6 @@ function UpdatePage(&$pagename, &$page, &$new, $fnlist = NULL) {
return $IsPagePosted;
}
-# AutoCheckToken verifies if the posted content was sent
-# from the website forms, to prevent CSRF
-function AutoCheckToken() {
- # TODO: Work in progress (Jan 2021), releasing for
- return true;
-
- global $EnablePost, $AutoCheckTokenActions, $EnablePmToken,
- $FmtV, $action, $BlockMessageFmt, $MessagesFmt;
-
- # a quick way to disable tokens
- if (! IsEnabled($EnablePmToken, 1)) return true;
-
- SDVA($AutoCheckTokenActions, array( # 1=POST, 2=GET, 0=disabled
- 'edit' => 1,
- 'postattr' => 1,
- 'postupload' => 1,
- 'approvesites' => 2,
- 'approveurls' => 2,
- ));
- $tname = $FmtV['$TokenName'];
- $x = @$AutoCheckTokenActions[$action];
- if (!$x) return true;
- elseif ($x==1) {
- if ( count($_POST) < 1 || pmtoken(''.@$_POST[$tname]) ) return true;
- }
- elseif ($x==2 && pmtoken(''.@$_GET[$tname])) return true;
-
- $EnablePost = 0;
- $MessagesFmt[] = $BlockMessageFmt;
- $MessagesFmt[] = XL('Token invalid or missing.');
- return false;
-}
-
# EditTemplate allows a site administrator to pre-populate new pages
# with the contents of another page.
function EditTemplate($pagename, &$page, &$new) {
@@ -2695,6 +2671,7 @@ function HandleEdit($pagename, $auth = 'edit') {
$new['csum'] = $ChangeSummary;
if ($ChangeSummary) $new["csum:$Now"] = $ChangeSummary;
$EnablePost &= (bool)preg_grep('/^post/', array_keys(@$_POST));
+ if($EnablePost) pmtoken(1, true);
$new['=preview'] = @$new['text'];
PCache($pagename, $new);
UpdatePage($pagename, $page, $new);
@@ -2706,7 +2683,7 @@ function HandleEdit($pagename, $auth = 'edit') {
$FmtV['$EditText'] =
str_replace('$','&#036;',PHSC(@$new['text'],ENT_NOQUOTES));
$FmtV['$EditBaseTime'] = $Now;
- $FmtV['$TokenValue'] = pmtoken();
+ pmtoken();
if (@$PageEditForm) {
$efpage = FmtPageName($PageEditForm, $pagename);
$form = RetrieveAuthPage($efpage, 'read', false, READPAGE_CURRENT);
@@ -2935,7 +2912,7 @@ function PasswdVar($pagename, $level) {
function PrintAttrForm($pagename) {
global $PageAttributes, $PCache, $FmtV;
- $FmtV['$TokenValue'] = pmtoken();
+ pmtoken();
echo FmtPageName("<form action='\$PageUrl' method='post'>
<input type='hidden' name='action' value='postattr' />
<input type='hidden' name='\$TokenName' value='\$TokenValue' />
@@ -2976,9 +2953,7 @@ function HandleAttr($pagename, $auth = 'attr') {
function HandlePostAttr($pagename, $auth = 'attr') {
global $PageAttributes, $EnablePostAttrClearSession;
- if (! AutoCheckToken()) {
- Abort('? $[Token invalid or missing.]');
- }
+ pmtoken(1, true);
Lock(2);
$page = RetrieveAuthPage($pagename, $auth, true);
if (!$page) { Abort("?unable to read $pagename"); }