commit 95bfd98b793aebf3f2fd4af719e6108af40b25cf
parent fdffc35f4eb4449a5e428a886e3fd718c2e741ce
Author: markseu <mark2011@mayberg.se>
Date: Fri, 11 May 2018 16:25:39 +0200
Updated editor, cross-side request forgery protection
Diffstat:
4 files changed, 158 insertions(+), 83 deletions(-)
diff --git a/system/config/config.ini b/system/config/config.ini
@@ -60,7 +60,7 @@ EditKeyboardShortcuts: ctrl+b bold, ctrl+i italic, ctrl+e code, ctrl+k link, ctr
EditToolbarButtons: auto
EditEndOfLine: auto
EditUserFile: user.ini
-EditUserPasswordMinLength: 4
+EditUserPasswordMinLength: 8
EditUserHashAlgorithm: bcrypt
EditUserHashCost: 10
EditUserStatus: active
@@ -68,7 +68,7 @@ EditUserHome: /
EditLoginEmail:
EditLoginPassword:
EditLoginRestrictions: 0
-EditLoginSessionTimeout: 31536000
+EditLoginSessionTimeout: 2592000
EditBruteForceProtection: 25
ImageThumbnailLocation: /media/thumbnails/
ImageThumbnailDir: media/thumbnails/
diff --git a/system/plugins/core.php b/system/plugins/core.php
@@ -3351,7 +3351,7 @@ class YellowToolbox
break;
case "sha256": if(substrb($hash, 0, 4)=="$5y$")
{
- $prefix = substrb($hash, 0, 4);
+ $prefix = "$5y$";
$salt = substrb($hash, 4, 32);
$hashCalculated = "$prefix$salt".hash("sha256", $salt.$text);
}
diff --git a/system/plugins/edit.js b/system/plugins/edit.js
@@ -244,6 +244,7 @@ yellow.edit =
"<div id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+
"<div id=\"yellow-pane-settings-fields\">"+
"<input type=\"hidden\" name=\"action\" value=\"settings\" />"+
+ "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+
"<p><label for=\"yellow-pane-settings-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-settings-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+
"<p><label for=\"yellow-pane-settings-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-settings-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+
"<p><label for=\"yellow-pane-settings-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-settings-password\" maxlength=\"64\" value=\"\" /></p>"+rawDataLanguages+
@@ -517,7 +518,7 @@ yellow.edit =
sendPane: function(paneId, paneAction, paneStatus, paneArgs)
{
if(yellow.config.debug) console.log("yellow.edit.sendPane id:"+paneId);
- var args = { "action":paneAction };
+ var args = { "action":paneAction, "csrftoken":this.getCookie("csrftoken") };
if(paneId=="yellow-pane-edit")
{
args.action = this.getAction(paneId, paneAction);
@@ -738,6 +739,7 @@ yellow.edit =
var thisObject = this;
var formData = new FormData();
formData.append("action", "preview");
+ formData.append("csrftoken", this.getCookie("csrftoken"));
formData.append("rawdataedit", elementText.value);
formData.append("rawdataendofline", yellow.page.rawDataEndOfLine);
var request = new XMLHttpRequest();
@@ -790,6 +792,7 @@ yellow.edit =
var thisObject = this;
var formData = new FormData();
formData.append("action", "upload");
+ formData.append("csrftoken", this.getCookie("csrftoken"));
formData.append("file", file);
var request = new XMLHttpRequest();
request.open("POST", window.location.pathname, true);
@@ -872,7 +875,13 @@ yellow.edit =
key = prefix + yellow.toolbox.toUpperFirst(key) + yellow.toolbox.toUpperFirst(postfix);
return (key in yellow.text) ? yellow.text[key] : "["+key+"]";
},
-
+
+ // Return cookie string
+ getCookie: function(name)
+ {
+ return yellow.toolbox.getCookie(name);
+ },
+
// Check if plugin exists
isPlugin: function(name)
{
@@ -1393,6 +1402,13 @@ yellow.toolbox =
return lines;
},
+ // Return cookie string
+ getCookie: function(name)
+ {
+ var matches = document.cookie.match("(^|; )"+name+"=([^;]+)");
+ return matches ? unescape(matches[2]) : "";
+ },
+
// Encode HTML special characters
encodeHtml: function(string)
{
diff --git a/system/plugins/edit.php b/system/plugins/edit.php
@@ -5,7 +5,7 @@
class YellowEdit
{
- const VERSION = "0.7.13";
+ const VERSION = "0.7.15";
var $yellow; //access to API
var $response; //web response
var $users; //user accounts
@@ -25,13 +25,13 @@ class YellowEdit
$this->yellow->config->setDefault("editToolbarButtons", "auto");
$this->yellow->config->setDefault("editEndOfLine", "auto");
$this->yellow->config->setDefault("editUserFile", "user.ini");
- $this->yellow->config->setDefault("editUserPasswordMinLength", "4");
+ $this->yellow->config->setDefault("editUserPasswordMinLength", "8");
$this->yellow->config->setDefault("editUserHashAlgorithm", "bcrypt");
$this->yellow->config->setDefault("editUserHashCost", "10");
$this->yellow->config->setDefault("editUserStatus", "active");
$this->yellow->config->setDefault("editUserHome", "/");
$this->yellow->config->setDefault("editLoginRestrictions", "0");
- $this->yellow->config->setDefault("editLoginSessionTimeout", "31536000");
+ $this->yellow->config->setDefault("editLoginSessionTimeout", "2592000");
$this->yellow->config->setDefault("editBruteForceProtection", "25");
$this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"));
}
@@ -194,14 +194,7 @@ class YellowEdit
case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break;
case "login": $statusCode = $this->processRequestLogin($scheme, $address, $base, $location, $fileName); break;
case "logout": $statusCode = $this->processRequestLogout($scheme, $address, $base, $location, $fileName); break;
- case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break;
- case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break;
- case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break;
- case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break;
- case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break;
case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break;
- case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break;
- case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break;
case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break;
case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break;
case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break;
@@ -275,7 +268,7 @@ class YellowEdit
function processRequestLogout($scheme, $address, $base, $location, $fileName)
{
$this->response->userEmail = "";
- $this->response->destroyCookie($scheme, $address, $base);
+ $this->response->destroyCookies($scheme, $address, $base);
$location = $this->yellow->lookup->normaliseUrl(
$this->yellow->config->get("serverScheme"),
$this->yellow->config->get("serverAddress"),
@@ -403,7 +396,7 @@ class YellowEdit
if($this->response->status=="ok")
{
$this->response->userEmail = "";
- $this->response->destroyCookie($scheme, $address, $base);
+ $this->response->destroyCookies($scheme, $address, $base);
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error";
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
}
@@ -433,14 +426,14 @@ class YellowEdit
$pending = $emailSource;
$home = $this->users->getHome($emailSource);
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
- $this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unconfirmed", "", "", $pending, $home) ? "ok" : "error";
+ $this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unconfirmed", "", "", "", $pending, $home) ? "ok" : "error";
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
}
if($this->response->status=="ok")
{
$pending = $email.':'.(empty($password) ? $this->users->getHash($emailSource) : $this->users->createHash($password));
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
- $this->response->status = $this->users->update($fileNameUser, $emailSource, "", $name, $language, "", "", "", $pending) ? "ok" : "error";
+ $this->response->status = $this->users->update($fileNameUser, $emailSource, "", $name, $language, "", "", "", "", $pending) ? "ok" : "error";
if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
}
if($this->response->status=="ok")
@@ -524,7 +517,7 @@ class YellowEdit
if($this->response->status=="ok")
{
$this->response->userEmail = "";
- $this->response->destroyCookie($scheme, $address, $base);
+ $this->response->destroyCookies($scheme, $address, $base);
$this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error";
if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!");
}
@@ -751,32 +744,32 @@ class YellowEdit
// Check user
function checkUser($scheme, $address, $base, $location, $fileName)
{
- if($_POST["action"]=="login")
+ if($this->isRequestSameSite("POST", $scheme, $address) || $_REQUEST["action"]=="")
{
- $email = $_POST["email"];
- $password = $_POST["password"];
- if($this->users->checkUser($email, $password))
- {
- $session = $this->response->createCookie($scheme, $address, $base, $email);
- $this->response->userEmail = $email;
- $this->response->userSession = $session;
- $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
- $this->response->language = $this->getUserLanguage($email);
- } else {
- $this->response->email = $email;
- $this->response->action = "fail";
- }
- } else if(isset($_COOKIE["login"])) {
- list($email, $session) = explode(',', $_COOKIE["login"], 2);
- if($this->users->checkCookie($email, $session))
+ if($_REQUEST["action"]=="login")
{
- $this->response->userEmail = $email;
- $this->response->userSession = $session;
- $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
- $this->response->language = $this->getUserLanguage($email);
- } else {
- $this->response->email = $email;
- $this->response->action = "fail";
+ $email = $_REQUEST["email"];
+ $password = $_REQUEST["password"];
+ if($this->users->checkAuthLogin($email, $password))
+ {
+ $this->response->createCookies($scheme, $address, $base, $email);
+ $this->response->userEmail = $email;
+ $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
+ $this->response->language = $this->getUserLanguage($email);
+ } else {
+ $this->response->userFailed = true;
+ $this->response->userFailedEmail = $email;
+ }
+ } else if(isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) {
+ if($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]==""))
+ {
+ $this->response->userEmail = $email = $this->users->getAuthEmail($_COOKIE["authtoken"]);
+ $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName);
+ $this->response->language = $this->getUserLanguage($email);
+ } else {
+ $this->response->userFailed = true;
+ $this->response->userFailedEmail = $this->users->getAuthEmail($_COOKIE["authtoken"]);
+ }
}
}
return $this->response->isUser();
@@ -785,21 +778,21 @@ class YellowEdit
// Check user failed
function checkUserFailed($scheme, $address, $base, $location, $fileName)
{
- if($this->response->action=="fail")
+ if($this->response->userFailed)
{
- $email = $this->response->email;
+ $email = $this->response->userFailedEmail;
if($this->users->isExisting($email))
{
$modified = $this->users->getModified($email);
$errors = $this->users->getErrors($email)+1;
$fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile");
- $status = $this->users->update($fileNameUser, $email, "", "", "", "", $modified, $errors) ? "ok" : "error";
+ $status = $this->users->update($fileNameUser, $email, "", "", "", "", "", $modified, $errors) ? "ok" : "error";
if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
if($errors==$this->yellow->config->get("editBruteForceProtection"))
{
if($status=="ok")
{
- $status = $this->users->update($fileNameUser, $email, "", "", "", "inactive", $modified, $errors) ? "ok" : "error";
+ $status = $this->users->update($fileNameUser, $email, "", "", "", "inactive", "", $modified, $errors) ? "ok" : "error";
if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!");
}
if($status=="ok")
@@ -809,6 +802,8 @@ class YellowEdit
}
}
}
+ $this->response->destroyCookies($scheme, $address, $base);
+ $this->response->status = "error";
$this->yellow->page->error(430);
}
}
@@ -861,22 +856,30 @@ class YellowEdit
if(!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language");
return $language;
}
+
+ // Check if request came from same site
+ function isRequestSameSite($method, $scheme, $address)
+ {
+ if(preg_match("#^(\w+)://([^/]+)(.*)$#", $_SERVER["HTTP_REFERER"], $matches)) $origin = "$matches[1]://$matches[2]";
+ if(isset($_SERVER["HTTP_ORIGIN"])) $origin = $_SERVER["HTTP_ORIGIN"];
+ return $_SERVER["REQUEST_METHOD"]==$method && $origin=="$scheme://$address";
+ }
}
class YellowResponse
{
var $yellow; //access to API
var $plugin; //access to plugin
+ var $active; //location is active? (boolean)
var $userEmail; //user email
- var $userSession; //user session
var $userRestrictions; //user can change page? (boolean)
- var $active; //location is active? (boolean)
+ var $userFailed; //user failed authentication? (boolean)
+ var $userFailedEmail; //email of failed authentication
var $rawDataSource; //raw data of page for comparison
var $rawDataEdit; //raw data of page for editing
var $rawDataOutput; //raw data of dynamic output
var $rawDataEndOfLine; //end of line format for raw data
- var $email; //response user email
- var $language; //response user language
+ var $language; //response language
var $action; //response action
var $status; //response status
@@ -1014,7 +1017,7 @@ class YellowResponse
return $file;
}
- // Return page data including login information
+ // Return page data including status information
function getPageData()
{
$data = array();
@@ -1087,7 +1090,7 @@ class YellowResponse
$data = array();
foreach($_REQUEST as $key=>$value)
{
- if($key=="login" || $key=="password" || substru($key, 0, 7)=="rawdata") continue;
+ if($key=="password" || $key=="authtoken" || $key=="csrftoken" || substru($key, 0, 7)=="rawdata") continue;
$data["request".ucfirst($key)] = trim($value);
}
return $data;
@@ -1261,19 +1264,21 @@ class YellowResponse
return $text;
}
- // Create browser cookie
- function createCookie($scheme, $address, $base, $email)
+ // Create browser cookies
+ function createCookies($scheme, $address, $base, $email)
{
- $session = $this->plugin->users->createSession($email);
+ $authToken = $this->plugin->users->createAuthToken($email);
+ $csrfToken = $this->plugin->users->createCsrfToken();
$timeout = $this->yellow->config->get("editLoginSessionTimeout");
- setcookie("login", "$email,$session", $timeout ? time()+$timeout : 0, "$base/", "", $scheme=="https");
- return $session;
+ setcookie("authtoken", $authToken, $timeout ? time()+$timeout : 0, "$base/", "", $scheme=="https", true);
+ setcookie("csrftoken", $csrfToken, $timeout ? time()+$timeout : 0, "$base/", "", $scheme=="https", false);
}
- // Destroy browser cookie
- function destroyCookie($scheme, $address, $base)
+ // Destroy browser cookies
+ function destroyCookies($scheme, $address, $base)
{
- setcookie("login", "", time()-60*60, "$base/", "", $scheme=="https");
+ setcookie("authtoken", "", 1, "$base/", "", $scheme=="https", true);
+ setcookie("csrftoken", "", 1, "$base/", "", $scheme=="https", false);
}
// Send mail to user
@@ -1393,8 +1398,9 @@ class YellowUsers
preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches);
if(!empty($matches[1]) && !empty($matches[2]))
{
- list($hash, $name, $language, $status, $modified, $errors, $pending, $home) = explode(',', $matches[2]);
- $this->set($matches[1], $hash, $name, $language, $status, $modified, $errors, $pending, $home);
+ list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(',', $matches[2]);
+ if($errors=="none") { $home=$pending; $pending=$errors; $errors=$modified; $modified=$stamp; $stamp=$matches[1]; } //TODO: remove later
+ $this->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home);
if(defined("DEBUG") && DEBUG>=3) echo "YellowUsers::load email:$matches[1]<br/>\n";
}
}
@@ -1409,11 +1415,13 @@ class YellowUsers
preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches);
if(!empty($matches[1]) && !empty($matches[2]))
{
- list($hash, $name, $language, $status, $modified, $errors, $pending, $home) = explode(',', $matches[2]);
+ list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(',', $matches[2]);
+ if($errors=="none") { $home=$pending; $pending=$errors; $errors=$modified; $modified=$stamp; $stamp=$this->createStamp(); } //TODO: remove later
+ if(strlenu($stamp)!=20) $stamp=$this->createStamp(); //TODO: remove later, converts old file format
if($status=="active" || $status=="inactive")
{
$pending = "none";
- $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$modified,$errors,$pending,$home\n";
+ $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n";
}
} else {
$fileDataNew .= $line;
@@ -1423,7 +1431,7 @@ class YellowUsers
}
// Update users in file
- function update($fileName, $email, $password = "", $name = "", $language = "", $status = "", $modified = "", $errors = "", $pending = "", $home = "")
+ function update($fileName, $email, $password = "", $name = "", $language = "", $status = "", $stamp = "", $modified = "", $errors = "", $pending = "", $home = "")
{
if(!empty($password)) $hash = $this->createHash($password);
if($this->isExisting($email))
@@ -1433,6 +1441,7 @@ class YellowUsers
$name = strreplaceu(',', '-', empty($name) ? $this->users[$email]["name"] : $name);
$language = strreplaceu(',', '-', empty($language) ? $this->users[$email]["language"] : $language);
$status = strreplaceu(',', '-', empty($status) ? $this->users[$email]["status"] : $status);
+ $stamp = strreplaceu(',', '-', empty($stamp) ? $this->users[$email]["stamp"] : $stamp);
$modified = strreplaceu(',', '-', empty($modified) ? time() : $modified);
$errors = strreplaceu(',', '-', empty($errors) ? "0" : $errors);
$pending = strreplaceu(',', '-', empty($pending) ? $this->users[$email]["pending"] : $pending);
@@ -1443,30 +1452,31 @@ class YellowUsers
$name = strreplaceu(',', '-', empty($name) ? $this->yellow->config->get("sitename") : $name);
$language = strreplaceu(',', '-', empty($language) ? $this->yellow->config->get("language") : $language);
$status = strreplaceu(',', '-', empty($status) ? $this->yellow->config->get("editUserStatus") : $status);
+ $stamp = strreplaceu(',', '-', empty($stamp) ? $this->createStamp() : $stamp);
$modified = strreplaceu(',', '-', empty($modified) ? time() : $modified);
$errors = strreplaceu(',', '-', empty($errors) ? "0" : $errors);
$pending = strreplaceu(',', '-', empty($pending) ? "none" : $pending);
$home = strreplaceu(',', '-', empty($home) ? $this->yellow->config->get("editUserHome") : $home);
}
- $this->set($email, $hash, $name, $language, $status, $modified, $errors, $pending, $home);
+ $this->set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home);
$fileData = $this->yellow->toolbox->readFile($fileName);
foreach($this->yellow->toolbox->getTextLines($fileData) as $line)
{
preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches);
if(!empty($matches[1]) && $matches[1]==$email)
{
- $fileDataNew .= "$email: $hash,$name,$language,$status,$modified,$errors,$pending,$home\n";
+ $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n";
$found = true;
} else {
$fileDataNew .= $line;
}
}
- if(!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$modified,$errors,$pending,$home\n";
+ if(!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n";
return $this->yellow->toolbox->createFile($fileName, $fileDataNew);
}
// Set user data
- function set($email, $hash, $name, $language, $status, $modified, $errors, $pending, $home)
+ function set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home)
{
$this->users[$email] = array();
$this->users[$email]["email"] = $email;
@@ -1474,38 +1484,60 @@ class YellowUsers
$this->users[$email]["name"] = $name;
$this->users[$email]["language"] = $language;
$this->users[$email]["status"] = $status;
+ $this->users[$email]["stamp"] = $stamp;
$this->users[$email]["modified"] = $modified;
$this->users[$email]["errors"] = $errors;
$this->users[$email]["pending"] = $pending;
$this->users[$email]["home"] = $home;
}
- // Check user login from email and password
- function checkUser($email, $password)
+ // Check user authentication from email and password
+ function checkAuthLogin($email, $password)
{
$algorithm = $this->yellow->config->get("editUserHashAlgorithm");
return $this->isExisting($email) && $this->users[$email]["status"]=="active" &&
$this->yellow->toolbox->verifyHash($password, $algorithm, $this->users[$email]["hash"]);
}
- // Check user login from email and session
- function checkCookie($email, $session)
+ // Check user authentication from tokens
+ function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $ignoreCsrfToken)
{
+ $session = "$5y$".substru($authToken, 0, 96);
+ $email = $this->getAuthEmail($authToken);
return $this->isExisting($email) && $this->users[$email]["status"]=="active" &&
- $this->yellow->toolbox->verifyHash($this->users[$email]["hash"], "sha256", $session);
+ $this->yellow->toolbox->verifyHash($this->users[$email]["hash"], "sha256", $session) &&
+ ($this->verifyToken($csrfTokenExpected, $csrfTokenReceived) || $ignoreCsrfToken);
}
- // Create session
+ // Create authentication token
+ function createAuthToken($email)
+ {
+ $session = $this->createSession($email);
+ return substru($session, 4).$this->getStamp($email);
+ }
+
+ // Create CSRF token
+ function createCsrfToken()
+ {
+ return $this->yellow->toolbox->createSalt(64);
+ }
+
+ // Create user session
function createSession($email)
{
- if($this->isExisting($email))
- {
- $session = $this->yellow->toolbox->createHash($this->users[$email]["hash"], "sha256");
- if(empty($session)) $session = "error-hash-algorithm-sha256";
- }
+ $session = $this->yellow->toolbox->createHash($this->users[$email]["hash"], "sha256");
+ if(empty($session)) $session = "error-hash-algorithm-sha256";
return $session;
}
+ // Create user stamp
+ function createStamp()
+ {
+ $stamp = $this->yellow->toolbox->createSalt(20);
+ while($this->getAuthEmail("none", $stamp)) $stamp = $this->yellow->toolbox->createSalt(20);
+ return $stamp;
+ }
+
// Create password hash
function createHash($password)
{
@@ -1519,7 +1551,9 @@ class YellowUsers
// Create request ID for action
function createRequestId($email, $action, $expire)
{
- return $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256");
+ $id = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256");
+ if(empty($id)) $hash = "error-hash-algorithm-sha256";
+ return $id;
}
// Return response status for action
@@ -1542,6 +1576,17 @@ class YellowUsers
if($expire<=time()) $status = "expired";
return $status;
}
+
+ // Return user email from authentication, timing attack safe email lookup
+ function getAuthEmail($authToken, $stamp = "")
+ {
+ if(empty($stamp)) $stamp = substru($authToken, 96);
+ foreach($this->users as $key=>$value)
+ {
+ if($this->verifyToken($value["stamp"], $stamp)) $email = $key;
+ }
+ return $email;
+ }
// Return user hash
function getHash($email)
@@ -1567,6 +1612,12 @@ class YellowUsers
return $this->isExisting($email) ? $this->users[$email]["status"] : "";
}
+ // Return user stamp
+ function getStamp($email)
+ {
+ return $this->isExisting($email) ? $this->users[$email]["stamp"] : "";
+ }
+
// Return user modified
function getModified($email)
{
@@ -1613,6 +1664,14 @@ class YellowUsers
return $data;
}
+ // Verify that text is identical, timing attack safe text string comparison
+ function verifyToken($text1, $text2) //TODO: remove later, use directly from core after next release
+ {
+ $ok = !empty($text1) && strlenb($text1)==strlenb($text2);
+ if($ok) for($i=0; $i<strlenb($text1); ++$i) $ok &= $text1[$i]==$text2[$i];
+ return $ok;
+ }
+
// Check if user is taken
function isTaken($email)
{