I am having a bit of difficulty with form tokens. I have a global file that i require at the top of all of the controllers.
/*
*----------------------------------------------
* VERIFY FORM TOKENS
*----------------------------------------------
*/
if ($_POST) {
// Define and Sanitize
$formToken = $sanitize->input($utilities->getVar('formToken', 'session'));
$authenticityToken = $sanitize->input($utilities->getVar('authenticityToken'));
// Validate
if ($authenticityToken !== $formToken) {
$errors[] = 'There was a token mismatch error submitting your form. Please try again.';
}
}
// Generate Form Token
$formToken = $forms->token();
$_SESSION['formToken'] = $formToken;
When echo'ing the vars out right after being declared they match. But when i check the db ( I save sessions to db ) every db refresh displays a new formtoken that was saved. I only call the $forms->token(); class once this is what it looks like
class Forms {
public __construct(){}
function token() {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$token = '';
for ($i = 0; $i < 60; $i++) { $token .= $characters[ rand( 0, strlen( $characters ) - 1 ) ]; }
$hash = substr(str_shuffle($token), 0, 32);
return $hash;
}
}
I have been working on this issue for a while now, i am confused as to why this occurs. I am also using mod_rewrite in my .htaccess file. I read that rewrites affect sessions but all other session data is ok ( session login data etc. ) it is just these tokens that are giving me a hard time.
I think you need to wrap an else around where you generate the token. As you have it, it looks like you get the token, then create a new one each time.
if ($_POST)
{
// Define and Sanitize
$formToken = $sanitize->input($utilities->getVar('formToken', 'session'));
$authenticityToken = $sanitize->input($utilities->getVar('authenticityToken'));
// Validate
if ($authenticityToken !== $formToken)
{
$errors[] = 'There was a token mismatch error submitting your form. Please try again.';
//UPDATE: MAYBE PUT IT HERE TOO:
$formToken = $forms->token();
$_SESSION['formToken'] = $formToken;
}
}
else
{
//----putting in an else so this is not done again on POST--------
// Generate Form Token
$formToken = $forms->token();
$_SESSION['formToken'] = $formToken;
}
Related
I am building a small app for Nextcloud (16). I have to execute some code in an external PHP file, not in the Nextcloud app.
This page has to be secured against unauthorized access. I want to achieve this with the existing Nextcloud session cookie.
Currently, I read the nc_session_id cookie from the user's browser and check if this session exists in PHP's session path. This should be secure because an attacker can usually not guess the id.
This is what the Nextcloud session in the browser looks like:
Nextcloud cookies
I have tried to check the cookie with
session_status('nc_session_id') != PHP_SESSION_NONE
but this always returns int(1) --> the session does not exist, because I would have to run session_start() before that. BUT in this special case, the external page shall never start a new session itself - it should only check if a valid Nextcloud session already exists.
My current code seems to do the job:
session_name('nc_session_id');
$sessid_cook = filter_input(INPUT_COOKIE, "nc_session_id", FILTER_SANITIZE_STRING);
$sess_path = session_save_path().'/sess_'.$sessid_cook;
if(isset($_COOKIE['nc_session_id']) &&
isset($_COOKIE['nc_username']) &&
file_exists($sess_path)) {
echo "Session okay";
session_start();
} else {
echo "Access denied";
exit;
}
// my protected logic here
If I manipulate the session cookie in my browser, the PHP code on the server can not find the session file for that manipulated cookie. So the access is denied.
This works in my current setup, but what happens if the sessions are handled by Redis or Memcache? The cookies could not be checked locally.
Is there some better way to "validate" session cookies before starting a PHP session?
Is my solution secure or does it have some flaws?
You cannot assume that your users are correctly connected simply by the existence of the cookie "nc_session_id" because the content of this cookie is always identical to the content of the cookie named with the value "instanceid" in your nextcloud configuration.
To do this, you must decrypt the content of the nextcloud session and test different values
<?php
$session_path = session_save_path().'/sess_'.$_COOKIE["nc_session_id"];
$passphrase = $_COOKIE["oc_sessionPassphrase"];
$nextcloud_path = '/var/www/nextcloud';
include $nextcloud_path.'/3rdparty/phpseclib/phpseclib/phpseclib/Crypt/Base.php';
include $nextcloud_path.'/3rdparty/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php';
include $nextcloud_path.'/3rdparty/phpseclib/phpseclib/phpseclib/Crypt/AES.php';
include $nextcloud_path.'/3rdparty/phpseclib/phpseclib/phpseclib/Crypt/Hash.php';
use phpseclib\Crypt\AES;
use phpseclib\Crypt\Hash;
class nextcloudSession {
private $cipher;
private $session_path;
private $passphrase;
public function __construct($session_path,$passphrase) {
$this->cipher = new AES();
$this->session_path = $session_path;
$this->passphrase = $passphrase;
}
public function getSession() {
$session_crypted = file_get_contents($this->session_path);
$session_crypted_content = substr($session_crypted,strpos($session_crypted,'"')+1,-2);
$session = json_decode($this->decrypt($session_crypted_content,$this->passphrase), true);
return $session;
}
public function setSession($session) {
$session_crypted_content = $crypt->encrypt(json_encode($session),$this->passphrase);
$session_crypted = 'encrypted_session_data|s:'.strlen($session_crypted_content).':"'.$session_crypted_content.'";';
return file_put_contents($session_path,$session_crypted);
}
public function isLogged() {
$session = $this->getSession();
if (isset($session["login_credentials"]) and (!isset($session["two_factor_auth_uid"]) and isset($session["two_factor_auth_passed"])) and !isset($session["app_password"])) {
return true;
} else {
return false;
}
}
private function calculateHMAC(string $message, string $password = ''): string {
$password = hash('sha512', $password . 'a');
$hash = new Hash('sha512');
$hash->setKey($password);
return $hash->hash($message);
}
private function encrypt(string $plaintext, string $password = ''): string {
$this->cipher->setPassword($password);
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$iv = '';
for ($i = 0; $i < 16; $i++) {
$iv .= $characters[rand(0, $charactersLength - 1)];
}
$this->cipher->setIV($iv);
$ciphertext = bin2hex($this->cipher->encrypt($plaintext));
$hmac = bin2hex($this->calculateHMAC($ciphertext.$iv, $password));
return $ciphertext.'|'.$iv.'|'.$hmac;
}
private function decrypt(string $authenticatedCiphertext, string $password = ''): string {
$this->cipher->setPassword($password);
$parts = explode('|', $authenticatedCiphertext);
if (\count($parts) !== 3) {
return false;
throw new \Exception('Authenticated ciphertext could not be decoded.');
}
$ciphertext = hex2bin($parts[0]);
$iv = $parts[1];
$hmac = hex2bin($parts[2]);
$this->cipher->setIV($iv);
if (!hash_equals($this->calculateHMAC($parts[0] . $parts[1], $password), $hmac)) {
return false;
throw new \Exception('HMAC does not match.');
}
$result = $this->cipher->decrypt($ciphertext);
if ($result === false) {
return false;
throw new \Exception('Decryption failed');
}
return $result;
}
}
$nc_session = new nextcloudSession($session_path,$passphrase);
$_SESSION = $nc_session->getSession();
$isLogged = $nc_session->isLogged();
$nc_session->setSession($_SESSION);
Tip: you can retrieve the login and password of the nextcloud session, I use it for a homemade SSO solution with the nginx reverseproxy and a configuration with 'auth_request'.
I have a nu_soap Web service which I want to create and send Captcha code and image with it.
I'm using getcaptcha web service to put generated Captcha code into a session variable and also Captcha Id, And by using another Web service named : validateCaptcha I'm checking if it's valid or not,
Problem is when I check my web services with Firefox Soa client addons web services are working ok and all session variables are working fine.
but when I'm trying to use android phone to check it session variables are not defined or are empty.
get captcha code :
<?php
session_start();
include_once('./Utility/DBM.php');
include_once('captcha.class.php');
class getCaptcha extends DBM
{
public function getcaptchacode($dump)
{
//creating and generating captcha image and code
$img = createcaptcha();
//Creating Captcha Id
$CaptchaId = (mt_rand(1,1000) . "1234");
imagepng($img[0],'Captcha/'.$CaptchaId.'.png');
imagedestroy($img[0]);
//encoding to base64 and getting file content
$base64 = base64_encode(file_get_contents('./Captcha/'.$CaptchaId.'.png'));
//setting up session for captcha
$_SESSION['Captcha_Code']=$img[0];
$_SESSION['Captcha_ID']=$CaptchaId;
$img[]='';
$captcha=$base64;
$captchaId = $CaptchaId;
$this->strResult = "";
$this->strResult.="<captcha>";
$this->strResult.="<captcha>$captcha</captcha>";
$this->strResult.="<captcha_Id>$captchaId</captcha_Id>";
$this->strResult.="</captcha>";
}
function __toString()
{
if($this->strResult!=""){
return $this->strResult;
}
return "";
}
}
?>
validate captcha code :
<?php
session_start();
include_once('./Utility/DBM.php');
class validateCaptcha extends DBM
{
public function validatecaptcha($CaptchaCode,$Captcha_Id)
{
if(($CaptchaCode!=""&&$CaptchaCode==$_SESSION['Captcha_Code'])&&($Captcha_Id!=""&&$Captcha_Id==$_SESSION['Captcha_ID']))
{
$msg="Captcha is correct";
//generating a Token using md5 & salt
$hash = md5(mt_rand(1,1000000) . "123456");
$token=$_SESSION['CapToken'] = $hash;
//starting token session time
$_SESSION['Token_time'] = time();
//destroying captcha image
unlink('./Captcha/'.$Captcha_Id.'.png');
}
else
{
//destroying captcha image
unlink('./Captcha/'.$Captcha_Id.'.png');
//destroying all session
//session_destroy();
$msg="Wrong Captcha Entered";
$token="";
}
$this->strResult = "";
$this->strResult.="<captcha>";
$this->strResult.="<captcha>$msg</captcha>";
$this->strResult.="<token>$token</token>";
$this->strResult.="</captcha>";
}
function __toString()
{
if($this->strResult!=""){
return $this->strResult;
}
return "";
}
}
?>
I Found a solution for this problem, I thought to post it here for others to use.
Instead of using session variables I used mysql database to store captcha (id,code,token,expire_time) then I used php date() function to check if captcha time is not expired and it's valid and In validation web service I used mysql to select from db to check if captcha is valid or not.
validation code :
class validateCaptcha extends DBM
{
public function validatecaptcha($CaptchaCode,$Captcha_Id)
{
$select_captcha = $this->select("captchaid,code,token,expire", "captcha", "code='$CaptchaCode' AND captchaid='$Captcha_Id'", 0, 0);
$captcha_check=mysql_num_rows($select_captcha);
$row = mysql_fetch_assoc($select_captcha);
if($captcha_check)
{
$msg="Captcha is correct";
$token=$row['token'];
//destroying captcha image
unlink('./Captcha/'.$Captcha_Id.'.png');
}
else
{
//destroying captcha image
unlink('./Captcha/'.$Captcha_Id.'.png');
$msg="Wrong Captcha Entered";
$token="";
}
$this->strResult = "";
$this->strResult.="<captcha>";
$this->strResult.="<captcha>$msg</captcha>";
$this->strResult.="<token>$token</token>";
$this->strResult.="</captcha>";
}
get captcha code :
class getCaptcha extends DBM
{
public function getcaptchacode($dump)
{
//creating and generating captcha image and code
$img = createcaptcha();
//Creating Captcha Id
$CaptchaId = (mt_rand(1,1000) . "1234");
imagepng($img[0],'Captcha/'.$CaptchaId.'.png');
imagedestroy($img[0]);
//encoding to base64 and getting file content
$base64 = base64_encode(file_get_contents('./Captcha/'.$CaptchaId.'.png'));
//generating a Token using md5 & salt
$hash = md5(mt_rand(1,1000000) . "123456");
$token=$_SESSION['CapToken'] = $hash;
//Getting time
$getime=date('Y-m-d H:i:s');
//inserting into captcha table
$this->RunQuery("INSERT INTO captcha (captchaid,code,token,expire) VALUES ('$CaptchaId','$img[1]','$token','$getime')", true, false);//TODO query
if (mysql_insert_id())
$Response = 'True';
else
$Response = 'False';
//deleting inactive captcha for 15 minutes
$this->RunQuery("DELETE FROM captcha WHERE expire < DATE_SUB(NOW(), INTERVAL 15 MINUTE)",true,false);
$img[]='';
$captcha=$base64;
$captchaId = $CaptchaId;
$this->strResult = "";
$this->strResult.="<captcha>";
$this->strResult.="<captcha>$captcha</captcha>";
$this->strResult.="<captcha_Id>$captchaId</captcha_Id>";
$this->strResult.="<Response>$Response</Response>";
$this->strResult.="</captcha>";
}
I'm working on a Captcha class and i'm almost done, there is one thing that doesn't work
In the file where I put the form, I start with this line:
include 'captcha.php';
$captcha = Captcha::tryCaptcha(2,4,'#000', '#ffffff');
and this is the captch construct:
static $do_generate = TRUE;
function __construct($aantal_letters = 2, $aantal_cijfers = 4, $voorgrond = '#000000', $achtergond = '#ffffff') {
session_start();
if (self::$do_generate == TRUE) {
$letters = substr(str_shuffle('ABCDEGHJKLMNPQRSTUVWXYZ'),0 ,$aantal_letters);
$cijfers = substr(str_shuffle('23456789'),0 ,$aantal_cijfers);
$imgbreed = 18 * ($aantal_letters + $aantal_cijfers);
$_SESSION['imgbreed'] = $imgbreed;
$_SESSION['captcha'] = $letters . $cijfers;
$_SESSION['voorgrond'] = $this->hex2rgb($voorgrond);
$_SESSION['achtergond'] = $this->hex2rgb($achtergond);
}
}
so in other words I put my stuff in a session if the static var $do_generate == TRUE
So when I post the form, the captcha is getting checked by a procesor.php
like this:
if (Captcha::captcha_uitkomst() == TRUE) {
echo "Great";
} else {
echo "Wrong";
}
And this is the captcha function that checks the etered captcha code:
static function captcha_uitkomst() {
if (strcmp($_SESSION['captcha'], strtoupper(str_replace(' ', '', $_POST['captcha-invoer']))) == 0) {
return TRUE;
} else {
echo "test";
self::$do_generate = FALSE;
return FALSE;
}
}
If I enter a correct captcha code, it's all good, that works I get the echo great.
If wrong I get the echo Wrong,
Perfect, but.... when I go back to form (hit backspace one history back) to enter a correct captcha, it regenerates a new captcha.
In the class: captcha_uitkomst you see that I made the self::do_generate FALSE
And the echo 'TEST' works when it's false, (just for checking)
What am I doing wrong
When you hit "back", the page is reloaded. You get a new CAPTCHA.
The premise of your question is fundamentally flawed, as you have just randomly assumed that this shouldn't happen, whereas in reality this is entirely by design.
It wouldn't be a very effective CAPTCHA if you could repeatedly get it wrong then go back and try again; any bot could just start brute forcing it and learning from the experience.
I have a website running on a less well known CMS called Ushahidi. There is built in OpenID functionality where folk can login with Facebook or Google.
I don't have enough dev skills to understand whats happening here but, it appears that I've almost got it working, except, I'm receiving the following error when trying to test it out on my own Google login:
An error was detected which prevented the loading of this page. If
this problem persists, please contact the website administrator.
application/controllers/login.php [503]: Undefined variable: user
I suspect, but am not sure, that defining a variable is easy enough but since I lack the knowledge I hoped to ask someone on here if they could see where I need to define the variable. Line 503 is part of a larger code block of about 100 lines, I know that it's not good practice to post larger chunks of code on here but I'm really unsure of what is and is not relevant. So forgive me. I have highlighted in bold where line 503 is. Can anyone point out what I must do here?
// OpenID Post
try
{
$openid = new OpenID;
// Retrieve the Name (if available) and Email
$openid->required = array("namePerson", "contact/email");
if( ! $openid->mode)
{
if(isset($_POST["openid_identifier"]))
{
$openid->identity = $_POST["openid_identifier"];
header("Location: " . $openid->authUrl());
}
}
elseif ($openid->mode == "cancel")
{
$openid_error = TRUE;
$message_class = 'login_error';
$message = "You have canceled authentication!";
}
else
{
if ($openid->validate())
{
// Does User Exist?
$openid_user = ORM::factory("openid")
->where("openid", $openid->identity)
->find();
if ($openid_user->loaded AND $openid_user->user)
{
// First log all other sessions out
$auth->logout();
// Initiate Ushahidi side login + AutoLogin
$auth->force_login($openid_user->user->username);
// Exists Redirect to Dashboard
**(THIS IS LINE 503)** url::redirect($user->dashboard());
}
else
{
// Does this openid have the required email??
$new_openid = $openid->getAttributes();
if ( ! isset($new_openid["contact/email"]) OR
empty($new_openid["contact/email"]))
{
$openid_error = TRUE;
$message_class = 'login_error';
$message = $openid->identity . " has not been logged in. No Email Address Found.";
}
else
{
// Create new User and save OpenID
$user = ORM::factory("user");
// But first... does this email address already exist
// in the system?
if ($user->email_exists($new_openid["contact/email"]))
{
$openid_error = TRUE;
$message_class = 'login_error';
$message = $new_openid["contact/email"] . " is already registered in our system.";
}
else
{
$username = "user".time(); // Random User Name from TimeStamp - can be changed later
$password = text::random("alnum", 16); // Create Random Strong Password
// Name Available?
$user->name = (isset($new_openid["namePerson"]) AND ! empty($new_openid["namePerson"]))
? $new_openid["namePerson"]
: $username;
$user->username = $username;
$user->password = $password;
$user->email = $new_openid["contact/email"];
// Add New Roles
$user->add(ORM::factory('role', 'login'));
$user->add(ORM::factory('role', 'member'));
$user->save();
// Save OpenID and Association
$openid_user->user_id = $user->id;
$openid_user->openid = $openid->identity;
$openid_user->openid_email = $new_openid["contact/email"];
$openid_user->openid_server = $openid->server;
$openid_user->openid_date = date("Y-m-d H:i:s");
$openid_user->save();
// Initiate Ushahidi side login + AutoLogin
$auth->login($username, $password, TRUE);
// Redirect to Dashboard
url::redirect($user->dashboard());
}
}
}
}
else
{
$openid_error = TRUE;
$message_class = 'login_error';
$message = $openid->identity . "has not been logged in.";
}
}
}
catch (ErrorException $e)
{
$openid_error = TRUE;
$message_class = 'login_error';
$message = $e->getMessage();
}
The problem is that the code is using $user several lines before it's actually defined. It might be a typo, though - maybe $openid_user->user->dashboard() at line 503 might work, though it's a WAG.
I have set of questions have you ever encounter some issues when multiple save on database?my problem is this, i have a database called "album" where fields are 'album_id,album_title,album_user'.
The html output would be there is a login section where there is a input type file where you want to add more and once you have uploaded it. and array of set of names will be stored and that array will be our que to save the file on the multiple format the problem is that. it says and error on the database whichs is sql specified twice.Do you have an idea on how to save in multiple using php?
code will be like this.
<?php
class Album extends olib{
function __construct(){
parent::olib();
}
function upload_submit() {
$allow = array("jpg","png","gif");
$directory = "upload";
$pictures = array();
$counter = 0;
$error = '';
if($this->jpost('upload')) {
for($getupload = 0;$getupload<count($_FILES['uploadpictures']['name']);$getupload++){
$extension = end(explode(".",$_FILES['uploadpictures']['name'][$getupload]));
if(in_array(strtolower($extension),$allow)){
if(move_uploaded_file($_FILES['uploadpictures']['tmp_name'][$getupload],$directory."/".$_FILES['uploadpictures']['name'][$getupload])){
$pictures[$getupload] = $_FILES['uploadpictures']['name'][$getupload];
$counter++;
// $this->save_user_album($_FILES['uploadpictures']['name'][$getupload],$this->setSession('user_id'));
}else{
$error[$getupload] = "Sorry seems some of the data invalid";
}
}else{
$error = '1';
}
}
print_r($pictures);
print_r($error);
foreach($pictures as $urpics){
$this->save_user_album($urpics,$this->setSession('user_id'));
}
}
}
function save_user_album($albumtitle,$session){
$_firewall = ($this->setSession('user_id') !=="") ? $this->setSession('user_id') : "";
$this->jfields('album_pics_title',$albumtitle);
// $this->jfields('album_pics_user',$session);
return $this->jSave('album_pics');
}
}
any response will greatly appreciated!!
Hi I'm sorry seems i have a solution for this i have unset after it has been save..
unset($this->jfields); problem has been solved