diff options
| author | Frédéric Guillot <fred@kanboard.net> | 2014-05-28 15:14:52 -0400 | 
|---|---|---|
| committer | Frédéric Guillot <fred@kanboard.net> | 2014-05-28 15:14:52 -0400 | 
| commit | 445ef6d1481745cd4e7af7e671f534a25d4495dc (patch) | |
| tree | 7990903e398d77339587595ef5a07df8464f5a2e | |
| parent | 75ab09e28b22e9a5676ee912482027926e271515 (diff) | |
Add CSRF protections
60 files changed, 291 insertions, 132 deletions
| diff --git a/app/Controller/Action.php b/app/Controller/Action.php index 2aa85c14..11dc3b29 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -129,6 +129,7 @@ class Action extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $action = $this->action->getById($this->request->getIntegerParam('action_id'));          if ($action && $this->action->remove($action['id'])) { diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 5829fc36..9b695a82 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -3,6 +3,7 @@  namespace Controller;  use Core\Registry; +use Core\Security;  use Core\Translator;  use Model\LastLogin; @@ -161,6 +162,28 @@ abstract class Base      }      /** +     * Application forbidden page +     * +     * @access public +     */ +    public function forbidden() +    { +        $this->response->html($this->template->layout('app_forbidden', array('title' => t('Access Forbidden')))); +    } + +    /** +     * Check if the CSRF token from the URL is correct +     * +     * @access protected +     */ +    protected function checkCSRFParam() +    { +        if (! Security::validateCSRFToken($this->request->getStringParam('csrf_token'))) { +            $this->forbidden(); +        } +    } + +    /**       * Check if the current user have access to the given project       *       * @access protected @@ -171,7 +194,7 @@ abstract class Base          if ($this->acl->isRegularUser()) {              if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) { -                $this->response->redirect('?controller=project&action=forbidden'); +                $this->forbidden();              }          }      } diff --git a/app/Controller/Board.php b/app/Controller/Board.php index 53fdeab9..67072895 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -4,6 +4,7 @@ namespace Controller;  use Model\Project as ProjectModel;  use Model\User as UserModel; +use Core\Security;  /**   * Board controller @@ -20,6 +21,7 @@ class Board extends Base       */      public function moveUp()      { +        $this->checkCSRFParam();          $project_id = $this->request->getIntegerParam('project_id');          $column_id = $this->request->getIntegerParam('column_id'); @@ -35,6 +37,7 @@ class Board extends Base       */      public function moveDown()      { +        $this->checkCSRFParam();          $project_id = $this->request->getIntegerParam('project_id');          $column_id = $this->request->getIntegerParam('column_id'); @@ -344,6 +347,7 @@ class Board extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $column = $this->board->getColumn($this->request->getIntegerParam('column_id'));          if ($column && $this->board->removeColumn($column['id'])) { @@ -362,25 +366,31 @@ class Board extends Base       */      public function save()      { -        $project_id = $this->request->getIntegerParam('project_id'); -        $values = $this->request->getValues(); +        if ($this->request->isAjax()) { -        if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) { -            $this->response->text('Not Authorized', 401); -        } +            $project_id = $this->request->getIntegerParam('project_id'); +            $values = $this->request->getValues(); -        if (isset($values['positions'])) { -            $this->board->saveTasksPosition($values['positions']); -        } +            if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) { +                $this->response->text('Not Authorized', 401); +            } + +            if (isset($values['positions'])) { +                $this->board->saveTasksPosition($values['positions']); +            } -        $this->response->html( -            $this->template->load('board_show', array( -                'current_project_id' => $project_id, -                'board' => $this->board->get($project_id), -                'categories' => $this->category->getList($project_id, false), -            )), -            201 -        ); +            $this->response->html( +                $this->template->load('board_show', array( +                    'current_project_id' => $project_id, +                    'board' => $this->board->get($project_id), +                    'categories' => $this->category->getList($project_id, false), +                )), +                201 +            ); +        } +        else { +            $this->response->status(401); +        }      }      /** @@ -390,24 +400,30 @@ class Board extends Base       */      public function check()      { -        $project_id = $this->request->getIntegerParam('project_id'); -        $timestamp = $this->request->getIntegerParam('timestamp'); +        if ($this->request->isAjax()) { -        if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) { -            $this->response->text('Not Authorized', 401); -        } +            $project_id = $this->request->getIntegerParam('project_id'); +            $timestamp = $this->request->getIntegerParam('timestamp'); -        if ($this->project->isModifiedSince($project_id, $timestamp)) { -            $this->response->html( -                $this->template->load('board_show', array( -                    'current_project_id' => $project_id, -                    'board' => $this->board->get($project_id), -                    'categories' => $this->category->getList($project_id, false), -                )) -            ); +            if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) { +                $this->response->text('Not Authorized', 401); +            } + +            if ($this->project->isModifiedSince($project_id, $timestamp)) { +                $this->response->html( +                    $this->template->load('board_show', array( +                        'current_project_id' => $project_id, +                        'board' => $this->board->get($project_id), +                        'categories' => $this->category->getList($project_id, false), +                    )) +                ); +            } +            else { +                $this->response->status(304); +            }          }          else { -            $this->response->status(304); +            $this->response->status(401);          }      }  } diff --git a/app/Controller/Category.php b/app/Controller/Category.php index f96c1d4a..9b73f207 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -175,6 +175,7 @@ class Category extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $project = $this->getProject();          $category = $this->getCategory($project['id']); diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php index 47eaf6b6..a0a11fc8 100644 --- a/app/Controller/Comment.php +++ b/app/Controller/Comment.php @@ -178,6 +178,7 @@ class Comment extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $task = $this->getTask();          $comment = $this->getComment(); diff --git a/app/Controller/Config.php b/app/Controller/Config.php index b4a5b8d3..daa57790 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -76,6 +76,7 @@ class Config extends Base       */      public function downloadDb()      { +        $this->checkCSRFParam();          $this->response->forceDownload('db.sqlite.gz');          $this->response->binary($this->config->downloadDatabase());      } @@ -87,6 +88,7 @@ class Config extends Base       */      public function optimizeDb()      { +        $this->checkCSRFParam();          $this->config->optimizeDatabase();          $this->session->flash(t('Database optimization done.'));          $this->response->redirect('?controller=config'); @@ -99,6 +101,7 @@ class Config extends Base       */      public function tokens()      { +        $this->checkCSRFParam();          $this->config->regenerateTokens();          $this->session->flash(t('All tokens have been regenerated.'));          $this->response->redirect('?controller=config'); @@ -111,6 +114,7 @@ class Config extends Base       */      public function removeRememberMeToken()      { +        $this->checkCSRFParam();          $this->rememberMe->remove($this->request->getIntegerParam('id'));          $this->response->redirect('?controller=config&action=index#remember-me');      } diff --git a/app/Controller/File.php b/app/Controller/File.php index 38cb82ab..3c8c32d1 100644 --- a/app/Controller/File.php +++ b/app/Controller/File.php @@ -111,6 +111,7 @@ class File extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $task = $this->getTask();          $file = $this->file->getById($this->request->getIntegerParam('file_id')); diff --git a/app/Controller/Project.php b/app/Controller/Project.php index e539f364..0de67691 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -13,19 +13,6 @@ use Model\Task as TaskModel;  class Project extends Base  {      /** -     * Display access forbidden page -     * -     * @access public -     */ -    public function forbidden() -    { -        $this->response->html($this->template->layout('project_forbidden', array( -            'menu' => 'projects', -            'title' => t('Access Forbidden') -        ))); -    } - -    /**       * Task search for a given project       *       * @access public @@ -254,6 +241,7 @@ class Project extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $project_id = $this->request->getIntegerParam('project_id');          if ($project_id && $this->project->remove($project_id)) { @@ -272,6 +260,7 @@ class Project extends Base       */      public function enable()      { +        $this->checkCSRFParam();          $project_id = $this->request->getIntegerParam('project_id');          if ($project_id && $this->project->enable($project_id)) { @@ -290,6 +279,7 @@ class Project extends Base       */      public function disable()      { +        $this->checkCSRFParam();          $project_id = $this->request->getIntegerParam('project_id');          if ($project_id && $this->project->disable($project_id)) { @@ -353,6 +343,8 @@ class Project extends Base       */      public function revoke()      { +        $this->checkCSRFParam(); +          $values = array(              'project_id' => $this->request->getIntegerParam('project_id'),              'user_id' => $this->request->getIntegerParam('user_id'), diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index 5ef193c8..1c217fa2 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -170,6 +170,7 @@ class Subtask extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $task = $this->getTask();          $subtask = $this->getSubtask(); diff --git a/app/Controller/Task.php b/app/Controller/Task.php index 68e3728a..d44ba268 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -218,6 +218,7 @@ class Task extends Base       */      public function close()      { +        $this->checkCSRFParam();          $task = $this->getTask();          if ($this->task->close($task['id'])) { @@ -252,6 +253,7 @@ class Task extends Base       */      public function open()      { +        $this->checkCSRFParam();          $task = $this->getTask();          if ($this->task->open($task['id'])) { @@ -286,6 +288,7 @@ class Task extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $task = $this->getTask();          if ($this->task->remove($task['id'])) { diff --git a/app/Controller/User.php b/app/Controller/User.php index e3fd8253..fca33b28 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -11,25 +11,13 @@ namespace Controller;  class User extends Base  {      /** -     * Display access forbidden page -     * -     * @access public -     */ -    public function forbidden() -    { -        $this->response->html($this->template->layout('user_forbidden', array( -            'menu' => 'users', -            'title' => t('Access Forbidden') -        ))); -    } - -    /**       * Logout and destroy session       *       * @access public       */      public function logout()      { +        $this->checkCSRFParam();          $this->rememberMe->destroy($this->acl->getUserId());          $this->session->close();          $this->response->redirect('?controller=user&action=login'); @@ -42,7 +30,9 @@ class User extends Base       */      public function login()      { -        if (isset($_SESSION['user'])) $this->response->redirect('?controller=app'); +        if (isset($_SESSION['user'])) { +            $this->response->redirect('?controller=app'); +        }          $this->response->html($this->template->layout('user_login', array(              'errors' => array(), @@ -236,6 +226,7 @@ class User extends Base       */      public function remove()      { +        $this->checkCSRFParam();          $user_id = $this->request->getIntegerParam('user_id');          if ($user_id && $this->user->remove($user_id)) { @@ -298,6 +289,7 @@ class User extends Base       */      public function unlinkGoogle()      { +        $this->checkCSRFParam();          if ($this->google->unlink($this->acl->getUserId())) {              $this->session->flash(t('Your Google Account is not linked anymore to your profile.'));          } diff --git a/app/Core/Request.php b/app/Core/Request.php index 7e9f24ac..6bc738be 100644 --- a/app/Core/Request.php +++ b/app/Core/Request.php @@ -2,6 +2,8 @@  namespace Core; +use Core\Security; +  /**   * Request class   * @@ -58,7 +60,12 @@ class Request      public function getValues()      {          if (! empty($_POST)) { -            return $_POST; + +            if (Security::validateCSRFFormToken($_POST)) { +                return $_POST; +            } + +            return array();          }          $result = json_decode($this->getBody(), true); @@ -116,6 +123,19 @@ class Request       */      public function isAjax()      { -        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; +        return $this->getHeader('X-Requested-With') === 'XMLHttpRequest'; +    } + +    /** +     * Return a HTTP header value +     * +     * @access public +     * @param  string   $name   Header name +     * @return string +     */ +    public function getHeader($name) +    { +        $name = 'HTTP_'.str_replace('-', '_', strtoupper($name)); +        return isset($_SERVER[$name]) ? $_SERVER[$name] : '';      }  } diff --git a/app/Core/Response.php b/app/Core/Response.php index 11d7567a..aee029af 100644 --- a/app/Core/Response.php +++ b/app/Core/Response.php @@ -18,8 +18,10 @@ class Response      public function nocache()      {          header('Pragma: no-cache'); -        header('Cache-Control: no-cache, must-revalidate');          header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); + +        // Use no-store due to a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=28035 +        header('Cache-Control: no-store, must-revalidate');      }      /** diff --git a/app/Core/Security.php b/app/Core/Security.php new file mode 100644 index 00000000..0bd7c991 --- /dev/null +++ b/app/Core/Security.php @@ -0,0 +1,87 @@ +<?php + +namespace Core; + +/** + * Security class + * + * @package  core + * @author   Frederic Guillot + */ +class Security +{ +     /** +     * Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid() +     * +     * @static +     * @access public +     * @return string  Random token +     */ +    public static function generateToken() +    { +        if (function_exists('openssl_random_pseudo_bytes')) { +            return bin2hex(\openssl_random_pseudo_bytes(30)); +        } +        else if (ini_get('open_basedir') === '' && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { +            return hash('sha256', file_get_contents('/dev/urandom', false, null, 0, 30)); +        } + +        return hash('sha256', uniqid(mt_rand(), true)); +    } + +    /** +     * Generate and store a CSRF token in the current session +     * +     * @static +     * @access public +     * @return string  Random token +     */ +    public static function getCSRFToken() +    { +        $nonce = self::generateToken(); + +        if (empty($_SESSION['csrf_tokens'])) { +            $_SESSION['csrf_tokens'] = array(); +        } + +        $_SESSION['csrf_tokens'][$nonce] = true; + +        return $nonce; +    } + +    /** +     * Check if the token exists for the current session (a token can be used only one time) +     * +     * @static +     * @access public +     * @param  string   $token   CSRF token +     * @return bool +     */ +    public static function validateCSRFToken($token) +    { +        if (isset($_SESSION['csrf_tokens'][$token])) { +            unset($_SESSION['csrf_tokens'][$token]); +            return true; +        } + +        return false; +    } + +    /** +     * Check if the token used in a form is correct and then remove the value +     * +     * @static +     * @access public +     * @param  array    $values   Form values +     * @return bool +     */ +    public static function validateCSRFFormToken(array &$values) +    { +        if (! empty($values['csrf_token']) && self::validateCSRFToken($values['csrf_token'])) { +            unset($values['csrf_token']); +            return true; +        } + +        return false; +    } +} diff --git a/app/Model/Base.php b/app/Model/Base.php index ddc06c3d..66185aeb 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -55,23 +55,4 @@ abstract class Base          $this->db = $db;          $this->event = $event;      } - -    /** -     * Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid() -     * -     * @static -     * @access public -     * @return string  Random token -     */ -    public static function generateToken() -    { -        if (function_exists('openssl_random_pseudo_bytes')) { -            return bin2hex(\openssl_random_pseudo_bytes(16)); -        } -        else if (ini_get('open_basedir') === '' && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { -            return hash('sha256', file_get_contents('/dev/urandom', false, null, 0, 30)); -        } - -        return hash('sha256', uniqid(mt_rand(), true)); -    }  } diff --git a/app/Model/Config.php b/app/Model/Config.php index 23abd8b5..469e6447 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -5,6 +5,7 @@ namespace Model;  use SimpleValidator\Validator;  use SimpleValidator\Validators;  use Core\Translator; +use Core\Security;  /**   * Config model @@ -29,7 +30,7 @@ class Config extends Base       */      public function getTimezones()      { -        $timezones = \timezone_identifiers_list(); +        $timezones = timezone_identifiers_list();          return array_combine(array_values($timezones), $timezones);      } @@ -171,12 +172,12 @@ class Config extends Base       */      public function regenerateTokens()      { -        $this->db->table(self::TABLE)->update(array('webhooks_token' => $this->generateToken())); +        $this->db->table(self::TABLE)->update(array('webhooks_token' => Security::generateToken()));          $projects = $this->db->table(Project::TABLE)->findAllByColumn('id');          foreach ($projects as $project_id) { -            $this->db->table(Project::TABLE)->eq('id', $project_id)->update(array('token' => $this->generateToken())); +            $this->db->table(Project::TABLE)->eq('id', $project_id)->update(array('token' => Security::generateToken()));          }      }  } diff --git a/app/Model/Project.php b/app/Model/Project.php index 9fbb0806..e1465012 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -5,6 +5,7 @@ namespace Model;  use SimpleValidator\Validator;  use SimpleValidator\Validators;  use Event\TaskModification; +use Core\Security;  /**   * Project model @@ -363,7 +364,7 @@ class Project extends Base      {          $this->db->startTransaction(); -        $values['token'] = self::generateToken(); +        $values['token'] = Security::generateToken();          if (! $this->db->table(self::TABLE)->save($values)) {              $this->db->cancelTransaction(); diff --git a/app/Model/RememberMe.php b/app/Model/RememberMe.php index 1494b14a..c9ef819f 100644 --- a/app/Model/RememberMe.php +++ b/app/Model/RememberMe.php @@ -2,6 +2,8 @@  namespace Model; +use Core\Security; +  /**   * RememberMe model   * @@ -174,8 +176,8 @@ class RememberMe extends Base       */      public function create($user_id, $ip, $user_agent)      { -        $token = hash('sha256', $user_id.$user_agent.$ip.$this->generateToken()); -        $sequence = $this->generateToken(); +        $token = hash('sha256', $user_id.$user_agent.$ip.Security::generateToken()); +        $sequence = Security::generateToken();          $expiration = time() + self::EXPIRATION;          $this->cleanup($user_id); @@ -225,7 +227,7 @@ class RememberMe extends Base       */      public function update($token, $sequence)      { -        $new_sequence = $this->generateToken(); +        $new_sequence = Security::generateToken();          $this->db               ->table(self::TABLE) diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 3df38dee..5c7e256f 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -263,6 +263,6 @@ function version_1($pdo)      $pdo->exec("          INSERT INTO config          (webhooks_token) -        VALUES ('".\Model\Base::generateToken()."') +        VALUES ('".\Core\Security::generateToken()."')      ");  } diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 663c7d34..bfe81c13 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -209,7 +209,7 @@ function version_3($pdo)          foreach ($results as &$result) {              $rq = $pdo->prepare('UPDATE projects SET token=? WHERE id=?'); -            $rq->execute(array(\Model\Base::generateToken(), $result['id'])); +            $rq->execute(array(\Core\Security::generateToken(), $result['id']));          }      }  } @@ -284,6 +284,6 @@ function version_1($pdo)      $pdo->exec("          INSERT INTO config          (language, webhooks_token) -        VALUES ('en_US', '".\Model\Base::generateToken()."') +        VALUES ('en_US', '".\Core\Security::generateToken()."')      ");  } diff --git a/app/Templates/action_index.php b/app/Templates/action_index.php index b515ccaa..36c333a9 100644 --- a/app/Templates/action_index.php +++ b/app/Templates/action_index.php @@ -56,7 +56,7 @@      <h3><?= t('Add an action') ?></h3>      <form method="post" action="?controller=action&action=params&project_id=<?= $project['id'] ?>" autocomplete="off"> - +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('project_id', $values) ?>          <?= Helper\form_label(t('Event'), 'event_name') ?> diff --git a/app/Templates/action_params.php b/app/Templates/action_params.php index 15a1d420..da685860 100644 --- a/app/Templates/action_params.php +++ b/app/Templates/action_params.php @@ -9,7 +9,7 @@      <h3><?= t('Define action parameters') ?></h3>      <form method="post" action="?controller=action&action=create&project_id=<?= $project['id'] ?>" autocomplete="off"> - +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('project_id', $values) ?>          <?= Helper\form_hidden('event_name', $values) ?>          <?= Helper\form_hidden('action_name', $values) ?> diff --git a/app/Templates/action_remove.php b/app/Templates/action_remove.php index b90136e8..13679eab 100644 --- a/app/Templates/action_remove.php +++ b/app/Templates/action_remove.php @@ -9,7 +9,7 @@          </p>          <div class="form-actions"> -            <a href="?controller=action&action=remove&action_id=<?= $action['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +            <a href="?controller=action&action=remove&action_id=<?= $action['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>              <?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $action['project_id'] ?>"><?= t('cancel') ?></a>          </div>      </div> diff --git a/app/Templates/user_forbidden.php b/app/Templates/app_forbidden.php index 853159ba..0c035404 100644 --- a/app/Templates/user_forbidden.php +++ b/app/Templates/app_forbidden.php @@ -4,6 +4,6 @@      </div>      <p class="alert alert-error"> -        <?= t('Only administrators can access to this page.') ?> +        <?= t('Access Forbidden') ?>      </p>  </section>
\ No newline at end of file diff --git a/app/Templates/board_assign.php b/app/Templates/board_assign.php index 74448a5c..6f92b375 100644 --- a/app/Templates/board_assign.php +++ b/app/Templates/board_assign.php @@ -18,7 +18,7 @@      <section>          <h3><?= t('Change assignee for the task "%s"', $values['title']) ?></h3>          <form method="post" action="?controller=board&action=assignTask" autocomplete="off"> - +            <?= Helper\form_csrf() ?>              <?= Helper\form_hidden('id', $values) ?>              <?= Helper\form_hidden('project_id', $values) ?> diff --git a/app/Templates/board_edit.php b/app/Templates/board_edit.php index 575536a8..05d9a6f6 100644 --- a/app/Templates/board_edit.php +++ b/app/Templates/board_edit.php @@ -9,7 +9,7 @@      <h3><?= t('Change columns') ?></h3>      <form method="post" action="?controller=board&action=update&project_id=<?= $project['id'] ?>" autocomplete="off"> - +        <?= Helper\form_csrf() ?>          <?php $i = 0; ?>          <table>              <tr> @@ -27,12 +27,12 @@                      <ul>                          <?php if ($column['position'] != 1): ?>                          <li> -                            <a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Move Up') ?></a> +                            <a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Up') ?></a>                          </li>                          <?php endif ?>                          <?php if ($column['position'] != count($columns)): ?>                          <li> -                            <a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Move Down') ?></a> +                            <a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Down') ?></a>                          </li>                          <?php endif ?>                          <li> @@ -52,7 +52,7 @@      <h3><?= t('Add a new column') ?></h3>      <form method="post" action="?controller=board&action=add&project_id=<?= $project['id'] ?>" autocomplete="off"> - +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('project_id', $values) ?>          <?= Helper\form_label(t('Title'), 'title') ?>          <?= Helper\form_text('title', $values, $errors, array('required')) ?> diff --git a/app/Templates/board_remove.php b/app/Templates/board_remove.php index b406eb38..76c217b3 100644 --- a/app/Templates/board_remove.php +++ b/app/Templates/board_remove.php @@ -10,7 +10,7 @@          </p>          <div class="form-actions"> -            <a href="?controller=board&action=remove&column_id=<?= $column['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +            <a href="?controller=board&action=remove&column_id=<?= $column['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>              <?= t('or') ?> <a href="?controller=board&action=edit&project_id=<?= $column['project_id'] ?>"><?= t('cancel') ?></a>          </div>      </div> diff --git a/app/Templates/board_show.php b/app/Templates/board_show.php index e5cd9ceb..6a138bf9 100644 --- a/app/Templates/board_show.php +++ b/app/Templates/board_show.php @@ -1,4 +1,4 @@ -<table id="board" data-project-id="<?= $current_project_id ?>" data-time="<?= time() ?>" data-check-interval="<?= BOARD_CHECK_INTERVAL ?>"> +<table id="board" data-project-id="<?= $current_project_id ?>" data-time="<?= time() ?>" data-check-interval="<?= BOARD_CHECK_INTERVAL ?>" data-csrf-token=<?= \Core\Security::getCSRFToken() ?>>  <tr>      <?php $column_with = round(100 / count($board), 2); ?>      <?php foreach ($board as $column): ?> diff --git a/app/Templates/category_edit.php b/app/Templates/category_edit.php index 99ba0c7c..1339f6da 100644 --- a/app/Templates/category_edit.php +++ b/app/Templates/category_edit.php @@ -8,7 +8,7 @@      <section>      <form method="post" action="?controller=category&action=update&project_id=<?= $project['id'] ?>" autocomplete="off"> - +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('id', $values) ?>          <?= Helper\form_hidden('project_id', $values) ?> diff --git a/app/Templates/category_index.php b/app/Templates/category_index.php index db986143..7fb923ba 100644 --- a/app/Templates/category_index.php +++ b/app/Templates/category_index.php @@ -34,6 +34,7 @@      <h3><?= t('Add a new category') ?></h3>      <form method="post" action="?controller=category&action=save&project_id=<?= $project['id'] ?>" autocomplete="off"> +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('project_id', $values) ?>          <?= Helper\form_label(t('Category Name'), 'name') ?> diff --git a/app/Templates/category_remove.php b/app/Templates/category_remove.php index cc2eb678..cfc23e07 100644 --- a/app/Templates/category_remove.php +++ b/app/Templates/category_remove.php @@ -9,7 +9,7 @@          </p>          <div class="form-actions"> -            <a href="?controller=category&action=remove&project_id=<?= $project['id'] ?>&category_id=<?= $category['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +            <a href="?controller=category&action=remove&project_id=<?= $project['id'] ?>&category_id=<?= $category['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>              <?= t('or') ?> <a href="?controller=category&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>          </div>      </div> diff --git a/app/Templates/comment_create.php b/app/Templates/comment_create.php index a566d9c8..f598532d 100644 --- a/app/Templates/comment_create.php +++ b/app/Templates/comment_create.php @@ -3,7 +3,7 @@  </div>  <form method="post" action="?controller=comment&action=save&task_id=<?= $task['id'] ?>" autocomplete="off"> - +    <?= Helper\form_csrf() ?>      <?= Helper\form_hidden('task_id', $values) ?>      <?= Helper\form_hidden('user_id', $values) ?>      <?= Helper\form_textarea('comment', $values, $errors, array('required', 'placeholder="'.t('Leave a comment').'"'), 'comment-textarea') ?><br/> diff --git a/app/Templates/comment_edit.php b/app/Templates/comment_edit.php index 0a17a95e..fdf3db54 100644 --- a/app/Templates/comment_edit.php +++ b/app/Templates/comment_edit.php @@ -4,6 +4,7 @@  <form method="post" action="?controller=comment&action=update&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'] ?>" autocomplete="off"> +    <?= Helper\form_csrf() ?>      <?= Helper\form_hidden('id', $values) ?>      <?= Helper\form_textarea('comment', $values, $errors, array('required', 'placeholder="'.t('Leave a comment').'"')) ?><br/> diff --git a/app/Templates/comment_remove.php b/app/Templates/comment_remove.php index 6409d7c0..7b117781 100644 --- a/app/Templates/comment_remove.php +++ b/app/Templates/comment_remove.php @@ -10,7 +10,7 @@      <?= Helper\template('comment_show', array('comment' => $comment, 'task' => $task, 'preview' => true)) ?>      <div class="form-actions"> -        <a href="?controller=comment&action=remove&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=comment&action=remove&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>#comment-<?= $comment['id'] ?>"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/config_index.php b/app/Templates/config_index.php index 6c610d2b..602e2070 100644 --- a/app/Templates/config_index.php +++ b/app/Templates/config_index.php @@ -7,6 +7,8 @@          <section>          <form method="post" action="?controller=config&action=save" autocomplete="off"> +            <?= Helper\form_csrf() ?> +              <?= Helper\form_label(t('Language'), 'language') ?>              <?= Helper\form_select('language', $languages, $values, $errors) ?><br/> @@ -39,7 +41,7 @@          </div>          <section class="settings">              <ul> -                <li><a href="?controller=config&action=tokens"><?= t('Reset all tokens') ?></a></li> +                <li><a href="?controller=config&action=tokens<?= Helper\param_csrf() ?>"><?= t('Reset all tokens') ?></a></li>                  <li>                      <?= t('Webhooks token:') ?>                      <strong><?= Helper\escape($values['webhooks_token']) ?></strong> @@ -50,11 +52,11 @@                          <strong><?= Helper\format_bytes($db_size) ?></strong>                      </li>                      <li> -                        <a href="?controller=config&action=downloadDb"><?= t('Download the database') ?></a> +                        <a href="?controller=config&action=downloadDb<?= Helper\param_csrf() ?>"><?= t('Download the database') ?></a>                          <?= t('(Gzip compressed Sqlite file)') ?>                      </li>                      <li> -                        <a href="?controller=config&action=optimizeDb"><?= t('Optimize the database') ?></a> +                        <a href="?controller=config&action=optimizeDb <?= Helper\param_csrf() ?>"><?= t('Optimize the database') ?></a>                           <?= t('(VACUUM command)') ?>                      </li>                  <?php endif ?> @@ -112,7 +114,7 @@              <td><?= dt('%B %e, %G at %k:%M %p', $session['expiration']) ?></td>              <td><?= Helper\escape($session['ip']) ?></td>              <td><?= Helper\escape($session['user_agent']) ?></td> -            <td><a href="?controller=config&action=removeRememberMeToken&id=<?= $session['id'] ?>"><?= t('Remove') ?></a></td> +            <td><a href="?controller=config&action=removeRememberMeToken&id=<?= $session['id'].Helper\param_csrf() ?>"><?= t('Remove') ?></a></td>          </tr>          <?php endforeach ?>          </table> diff --git a/app/Templates/file_new.php b/app/Templates/file_new.php index 643f340d..7f7f1d1c 100644 --- a/app/Templates/file_new.php +++ b/app/Templates/file_new.php @@ -3,6 +3,7 @@  </div>  <form action="?controller=file&action=save&task_id=<?= $task['id'] ?>" method="post" enctype="multipart/form-data"> +    <?= Helper\form_csrf() ?>      <input type="file" name="files[]" multiple />      <div class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? Helper\format_bytes($max_size) : $max_size ?></div>      <div class="form-actions"> diff --git a/app/Templates/file_remove.php b/app/Templates/file_remove.php index 1d26c15e..af77591c 100644 --- a/app/Templates/file_remove.php +++ b/app/Templates/file_remove.php @@ -8,7 +8,7 @@      </p>      <div class="form-actions"> -        <a href="?controller=file&action=remove&task_id=<?= $task['id'] ?>&file_id=<?= $file['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=file&action=remove&task_id=<?= $task['id'] ?>&file_id=<?= $file['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/layout.php b/app/Templates/layout.php index 3e3b2e89..aa430477 100644 --- a/app/Templates/layout.php +++ b/app/Templates/layout.php @@ -45,7 +45,7 @@                          <a href="?controller=config"><?= t('Settings') ?></a>                      </li>                      <li> -                        <a href="?controller=user&action=logout"><?= t('Logout') ?></a> +                        <a href="?controller=user&action=logout<?= Helper\param_csrf() ?>"><?= t('Logout') ?></a>                          (<?= Helper\escape(Helper\get_username()) ?>)                      </li>                  </ul> diff --git a/app/Templates/project_edit.php b/app/Templates/project_edit.php index 557986bf..a882fbc6 100644 --- a/app/Templates/project_edit.php +++ b/app/Templates/project_edit.php @@ -8,6 +8,7 @@      <section>      <form method="post" action="?controller=project&action=update&project_id=<?= $values['id'] ?>" autocomplete="off"> +        <?= Helper\form_csrf() ?>          <?= Helper\form_hidden('id', $values) ?>          <?= Helper\form_label(t('Name'), 'name') ?> diff --git a/app/Templates/project_forbidden.php b/app/Templates/project_forbidden.php deleted file mode 100644 index 1cba7b58..00000000 --- a/app/Templates/project_forbidden.php +++ /dev/null @@ -1,9 +0,0 @@ -<section id="main"> -    <div class="page-header"> -        <h2><?= t('Forbidden') ?></h2> -    </div> - -    <p class="alert alert-error"> -        <?= t('You are not allowed to access to this project.') ?> -    </p> -</section>
\ No newline at end of file diff --git a/app/Templates/project_index.php b/app/Templates/project_index.php index 1a3dbd49..927924a5 100644 --- a/app/Templates/project_index.php +++ b/app/Templates/project_index.php @@ -78,9 +78,9 @@                          </li>                          <li>                              <?php if ($project['is_active']): ?> -                                <a href="?controller=project&action=disable&project_id=<?= $project['id'] ?>"><?= t('Disable') ?></a> +                                <a href="?controller=project&action=disable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Disable') ?></a>                              <?php else: ?> -                                <a href="?controller=project&action=enable&project_id=<?= $project['id'] ?>"><?= t('Enable') ?></a> +                                <a href="?controller=project&action=enable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Enable') ?></a>                              <?php endif ?>                          </li>                          <li> diff --git a/app/Templates/project_new.php b/app/Templates/project_new.php index 2026d461..b4ed9990 100644 --- a/app/Templates/project_new.php +++ b/app/Templates/project_new.php @@ -8,6 +8,7 @@      <section>      <form method="post" action="?controller=project&action=save" autocomplete="off"> +        <?= Helper\form_csrf() ?>          <?= Helper\form_label(t('Name'), 'name') ?>          <?= Helper\form_text('name', $values, $errors, array('autofocus', 'required')) ?> diff --git a/app/Templates/project_remove.php b/app/Templates/project_remove.php index e9f213b5..e25efa2f 100644 --- a/app/Templates/project_remove.php +++ b/app/Templates/project_remove.php @@ -9,7 +9,7 @@          </p>          <div class="form-actions"> -            <a href="?controller=project&action=remove&project_id=<?= $project['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +            <a href="?controller=project&action=remove&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>              <?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>          </div>      </div> diff --git a/app/Templates/project_users.php b/app/Templates/project_users.php index 0448004f..8afac709 100644 --- a/app/Templates/project_users.php +++ b/app/Templates/project_users.php @@ -10,6 +10,8 @@      <?php if (! empty($users['not_allowed'])): ?>          <form method="post" action="?controller=project&action=allow&project_id=<?= $project['id'] ?>" autocomplete="off"> +            <?= Helper\form_csrf() ?> +              <?= Helper\form_hidden('project_id', array('project_id' => $project['id'])) ?>              <?= Helper\form_label(t('User'), 'user_id') ?> @@ -32,7 +34,7 @@          <?php foreach ($users['allowed'] as $user_id => $username): ?>              <li>                  <strong><?= Helper\escape($username) ?></strong> -                (<a href="?controller=project&action=revoke&project_id=<?= $project['id'] ?>&user_id=<?= $user_id ?>"><?= t('revoke') ?></a>) +                (<a href="?controller=project&action=revoke&project_id=<?= $project['id'] ?>&user_id=<?= $user_id.Helper\param_csrf() ?>"><?= t('revoke') ?></a>)              </li>          <?php endforeach ?>          </ul> diff --git a/app/Templates/subtask_create.php b/app/Templates/subtask_create.php index a456aa37..f1b27ab9 100644 --- a/app/Templates/subtask_create.php +++ b/app/Templates/subtask_create.php @@ -4,6 +4,8 @@  <form method="post" action="?controller=subtask&action=save&task_id=<?= $task['id'] ?>" autocomplete="off"> +    <?= Helper\form_csrf() ?> +      <?= Helper\form_hidden('task_id', $values) ?>      <?= Helper\form_label(t('Title'), 'title') ?> diff --git a/app/Templates/subtask_edit.php b/app/Templates/subtask_edit.php index 3080cdad..fc65d3b3 100644 --- a/app/Templates/subtask_edit.php +++ b/app/Templates/subtask_edit.php @@ -4,6 +4,8 @@  <form method="post" action="?controller=subtask&action=update&task_id=<?= $task['id'] ?>&subtask_id=<?= $subtask['id'] ?>" autocomplete="off"> +    <?= Helper\form_csrf() ?> +      <?= Helper\form_hidden('id', $values) ?>      <?= Helper\form_hidden('task_id', $values) ?> diff --git a/app/Templates/subtask_remove.php b/app/Templates/subtask_remove.php index 2862176c..12c99cf1 100644 --- a/app/Templates/subtask_remove.php +++ b/app/Templates/subtask_remove.php @@ -10,7 +10,7 @@      <p><strong><?= Helper\escape($subtask['title']) ?></strong></p>      <div class="form-actions"> -        <a href="?controller=subtask&action=remove&task_id=<?= $task['id'] ?>&subtask_id=<?= $subtask['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=subtask&action=remove&task_id=<?= $task['id'] ?>&subtask_id=<?= $subtask['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>#subtasks"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/task_close.php b/app/Templates/task_close.php index 6843c2f6..5c75b72b 100644 --- a/app/Templates/task_close.php +++ b/app/Templates/task_close.php @@ -8,7 +8,7 @@      </p>      <div class="form-actions"> -        <a href="?controller=task&action=close&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=task&action=close&task_id=<?= $task['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/task_edit.php b/app/Templates/task_edit.php index d698c21d..c03c7d9a 100644 --- a/app/Templates/task_edit.php +++ b/app/Templates/task_edit.php @@ -8,6 +8,8 @@      <section>      <form method="post" action="?controller=task&action=update&task_id=<?= $task['id'] ?>" autocomplete="off"> +        <?= Helper\form_csrf() ?> +          <div class="form-column">              <?= Helper\form_label(t('Title'), 'title') ?> diff --git a/app/Templates/task_edit_description.php b/app/Templates/task_edit_description.php index 0bdc40a2..550dac73 100644 --- a/app/Templates/task_edit_description.php +++ b/app/Templates/task_edit_description.php @@ -4,6 +4,8 @@  <form method="post" action="?controller=task&action=saveDescription&task_id=<?= $task['id'] ?>" autocomplete="off"> +    <?= Helper\form_csrf() ?> +      <?= Helper\form_hidden('id', $values) ?>      <?= Helper\form_textarea('description', $values, $errors, array('required', 'placeholder="'.t('Leave a description').'"'), 'description-textarea') ?><br/>      <div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div> diff --git a/app/Templates/task_new.php b/app/Templates/task_new.php index d233efd2..2938c4ca 100644 --- a/app/Templates/task_new.php +++ b/app/Templates/task_new.php @@ -5,6 +5,8 @@      <section>      <form method="post" action="?controller=task&action=save" autocomplete="off"> +        <?= Helper\form_csrf() ?> +          <div class="form-column">              <?= Helper\form_label(t('Title'), 'title') ?>              <?= Helper\form_text('title', $values, $errors, array('autofocus', 'required')) ?><br/> diff --git a/app/Templates/task_open.php b/app/Templates/task_open.php index 59ea0b54..3526ec81 100644 --- a/app/Templates/task_open.php +++ b/app/Templates/task_open.php @@ -8,7 +8,7 @@      </p>      <div class="form-actions"> -        <a href="?controller=task&action=open&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=task&action=open&task_id=<?= $task['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/task_remove.php b/app/Templates/task_remove.php index 60e4e8e7..dd4841db 100644 --- a/app/Templates/task_remove.php +++ b/app/Templates/task_remove.php @@ -8,7 +8,7 @@      </p>      <div class="form-actions"> -        <a href="?controller=task&action=remove&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +        <a href="?controller=task&action=remove&task_id=<?= $task['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>          <?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>      </div>  </div>
\ No newline at end of file diff --git a/app/Templates/user_edit.php b/app/Templates/user_edit.php index c857fe1c..6b83f748 100644 --- a/app/Templates/user_edit.php +++ b/app/Templates/user_edit.php @@ -8,6 +8,8 @@      <section>      <form method="post" action="?controller=user&action=update" autocomplete="off"> +        <?= Helper\form_csrf() ?> +          <div class="form-column">              <?= Helper\form_hidden('id', $values) ?> @@ -48,9 +50,9 @@              <?php if (GOOGLE_AUTH && Helper\is_current_user($values['id'])): ?>                  <?php if (empty($values['google_id'])): ?> -                    <a href="?controller=user&action=google"><?= t('Link my Google Account') ?></a> +                    <a href="?controller=user&action=google<?= Helper\param_csrf() ?>"><?= t('Link my Google Account') ?></a>                  <?php else: ?> -                    <a href="?controller=user&action=unlinkGoogle"><?= t('Unlink my Google Account') ?></a> +                    <a href="?controller=user&action=unlinkGoogle<?= Helper\param_csrf() ?>"><?= t('Unlink my Google Account') ?></a>                  <?php endif ?>              <?php endif ?> diff --git a/app/Templates/user_login.php b/app/Templates/user_login.php index 878170e3..49902ebb 100644 --- a/app/Templates/user_login.php +++ b/app/Templates/user_login.php @@ -8,6 +8,8 @@  <form method="post" action="?controller=user&action=check" class="form-login"> +    <?= Helper\form_csrf() ?> +      <?= Helper\form_label(t('Username'), 'username') ?>      <?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/> diff --git a/app/Templates/user_new.php b/app/Templates/user_new.php index 6ad976f2..3e22b7ee 100644 --- a/app/Templates/user_new.php +++ b/app/Templates/user_new.php @@ -8,6 +8,8 @@      <section>      <form method="post" action="?controller=user&action=save" autocomplete="off"> +        <?= Helper\form_csrf() ?> +          <div class="form-column">              <?= Helper\form_label(t('Username'), 'username') ?> diff --git a/app/Templates/user_remove.php b/app/Templates/user_remove.php index a4db2e4a..61d4163b 100644 --- a/app/Templates/user_remove.php +++ b/app/Templates/user_remove.php @@ -7,7 +7,7 @@          <p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['username']) ?></p>          <div class="form-actions"> -            <a href="?controller=user&action=remove&user_id=<?= $user['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a> +            <a href="?controller=user&action=remove&user_id=<?= $user['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a>              <?= t('or') ?> <a href="?controller=user"><?= t('cancel') ?></a>          </div>      </div> diff --git a/app/helpers.php b/app/helpers.php index d22a4869..2df4d839 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -2,6 +2,11 @@  namespace Helper; +function param_csrf() +{ +    return '&csrf_token='.\Core\Security::getCSRFToken(); +} +  function js($filename)  {      return '<script type="text/javascript" src="'.$filename.'?'.filemtime($filename).'"></script>'; @@ -163,6 +168,11 @@ function form_value($values, $name)      return isset($values[$name]) ? 'value="'.escape($values[$name]).'"' : '';  } +function form_csrf() +{ +    return '<input type="hidden" name="csrf_token" value="'.\Core\Security::getCSRFToken().'"/>'; +} +  function form_hidden($name, $values = array())  {      return '<input type="hidden" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).'/>'; diff --git a/assets/js/board.js b/assets/js/board.js index 49dab9fa..7ff7445b 100644 --- a/assets/js/board.js +++ b/assets/js/board.js @@ -70,8 +70,9 @@          });          $.ajax({ +            cache: false,              url: "?controller=board&action=save&project_id=" + projectId, -            data: {positions: data}, +            data: {"positions": data, "csrf_token": $("#board").attr("data-csrf-token")},              type: "POST",              success: function(data) {                  $("#board").remove(); @@ -90,6 +91,7 @@          if (is_visible() && projectId != undefined && timestamp != undefined) {              $.ajax({ +                cache: false,                  url: "?controller=board&action=check&project_id=" + projectId + "×tamp=" + timestamp,                  statusCode: {                      200: function(data) { diff --git a/tests/Base.php b/tests/Base.php index ce088b22..d4065982 100644 --- a/tests/Base.php +++ b/tests/Base.php @@ -4,6 +4,8 @@ if (version_compare(PHP_VERSION, '5.5.0', '<')) {      require __DIR__.'/../vendor/password.php';  } +require_once __DIR__.'/../app/Core/Security.php'; +  require_once __DIR__.'/../vendor/PicoDb/Database.php';  require_once __DIR__.'/../app/Schema/Sqlite.php'; | 
