CVE-2017-15197: Authorization Bypass Through User-Controlled Key (CWE-639)
value: In Kanboard before 1.0.47, by altering form data, an authenticated user can add a new category to a private project of another user.
CWE description: The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
CVSS v3 base score: 4.3
Published: 2017-10-11T01:32Z
Repository: https://github.com/kanboard/kanboard
Language: PHP
Files changed: app/Controller/BaseController.php, app/Controller/CommentController.php, app/Controller/SubtaskController.php, app/Controller/SubtaskConverterController.php, app/Controller/SubtaskRestrictionController.php, app/Controller/SubtaskStatusController.php, app/Controller/TaskExternalLinkController.php, app/Controller/TaskInternalLinkController.php, app/Controller/TaskModificationController.php, app/Controller/TaskRecurrenceController.php, app/Template/comment/create.php, app/Template/comment/edit.php, app/Template/subtask/create.php, app/Template/subtask/edit.php, app/Template/task_external_link/edit.php, app/Template/task_external_link/find.php, app/Template/task_external_link/form.php, app/Template/task_internal_link/create.php, app/Template/task_internal_link/edit.php, app/Template/task_modification/show.php
Commit date: 2017-09-23 20:56:54 -0700
Commit message: Do not expose IDs in forms

Diff: diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php
index 1ac7ed203..41fcef1c3 100644
--- a/app/Controller/BaseController.php
+++ b/app/Controller/BaseController.php
@@ -136,28 +136,74 @@ abstract class BaseController extends Base
         }
 
         return $user;
     }
 
-    /**
-     * Get the current subtask
-     *
-     * @access protected
-     * @return array
-     * @throws PageNotFoundException
-     */
-    protected function getSubtask()
+    protected function getSubtask(array $task)
     {
         $subtask = $this->subtaskModel->getById($this->request->getIntegerParam('subtask_id'));
 
         if (empty($subtask)) {
             throw new PageNotFoundException();
         }
 
+        if ($subtask['task_id'] != $task['id']) {
+            throw new AccessForbiddenException();
+        }
+
         return $subtask;
     }
 
+    protected function getComment(array $task)
+    {
+        $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id'));
+
+        if (empty($comment)) {
+            throw new PageNotFoundException();
+        }
+
+        if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
+            throw new AccessForbiddenException();
+        }
+
+        if ($comment['task_id'] != $task['id']) {
+            throw new AccessForbiddenException();
+        }
+
+        return $comment;
+    }
+
+    protected function getExternalTaskLink(array $task)
+    {
+        $link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id'));
+
+        if (empty($link)) {
+            throw new PageNotFoundException();
+        }
+
+        if ($link['task_id'] != $task['id']) {
+            throw new AccessForbiddenException();
+        }
+
+        return $link;
+    }
+
+    protected function getInternalTaskLink(array $task)
+    {
+        $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id'));
+
+        if (empty($link)) {
+            throw new PageNotFoundException();
+        }
+
+        if ($link['task_id'] != $task['id']) {
+            throw new AccessForbiddenException();
+        }
+
+        return $link;
+    }
+
     protected function getColumn(array $project)
     {
         $column = $this->columnModel->getById($this->request->getIntegerParam('column_id'));
 
         if (empty($column)) {
diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php
index 9a89103ee..a29491a31 100644
--- a/app/Controller/CommentController.php
+++ b/app/Controller/CommentController.php
@@ -11,33 +11,10 @@ use Kanboard\Core\Controller\PageNotFoundException;
  * @package  Kanboard\Controller
  * @author   Frederic Guillot
  */
 class CommentController extends BaseController
 {
-    /**
-     * Get the current comment
-     *
-     * @access protected
-     * @return array
-     * @throws PageNotFoundException
-     * @throws AccessForbiddenException
-     */
-    protected function getComment()
-    {
-        $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id'));
-
-        if (empty($comment)) {
-            throw new PageNotFoundException();
-        }
-
-        if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
-            throw new AccessForbiddenException();
-        }
-
-        return $comment;
-    }
-
     /**
      * Add comment form
      *
      * @access public
      * @param array $values
@@ -47,18 +24,10 @@ class CommentController extends BaseController
      */
     public function create(array $values = array(), array $errors = array())
     {
         $project = $this->getProject();
         $task = $this->getTask();
-
-        if (empty($values)) {
-            $values = array(
-                'user_id' => $this->userSession->getId(),
-                'task_id' => $task['id'],
-            );
-        }
-
         $values['project_id'] = $task['project_id'];
 
         $this->response->html($this->helper->layout->task('comment/create', array(
             'values' => $values,
             'errors' => $errors,
@@ -104,11 +73,11 @@ class CommentController extends BaseController
      * @throws PageNotFoundException
      */
     public function edit(array $values = array(), array $errors = array())
     {
         $task = $this->getTask();
-        $comment = $this->getComment();
+        $comment = $this->getComment($task);
 
         if (empty($values)) {
             $values = $comment;
         }
 
@@ -128,13 +97,17 @@ class CommentController extends BaseController
      * @access public
      */
     public function update()
     {
         $task = $this->getTask();
-        $this->getComment();
+        $comment = $this->getComment($task);
 
         $values = $this->request->getValues();
+        $values['id'] = $comment['id'];
+        $values['task_id'] = $task['id'];
+        $values['user_id'] = $comment['user_id'];
+
         list($valid, $errors) = $this->commentValidator->validateModification($values);
 
         if ($valid) {
             if ($this->commentModel->update($values)) {
                 $this->flash->success(t('Comment updated successfully.'));
@@ -155,11 +128,11 @@ class CommentController extends BaseController
      * @access public
      */
     public function confirm()
     {
         $task = $this->getTask();
-        $comment = $this->getComment();
+        $comment = $this->getComment($task);
 
         $this->response->html($this->template->render('comment/remove', array(
             'comment' => $comment,
             'task' => $task,
             'title' => t('Remove a comment')
@@ -173,11 +146,11 @@ class CommentController extends BaseController
      */
     public function remove()
     {
         $this->checkCSRFParam();
         $task = $this->getTask();
-        $comment = $this->getComment();
+        $comment = $this->getComment($task);
 
         if ($this->commentModel->remove($comment['id'])) {
             $this->flash->success(t('Comment removed successfully.'));
         } else {
             $this->flash->failure(t('Unable to remove this comment.'));
diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php
index 5fa55f6bc..b9bb09342 100644
--- a/app/Controller/SubtaskController.php
+++ b/app/Controller/SubtaskController.php
@@ -64,10 +64,11 @@ class SubtaskController extends BaseController
      */
     public function save()
     {
         $task = $this->getTask();
         $values = $this->request->getValues();
+        $values['task_id'] = $task['id'];
 
         list($valid, $errors) = $this->subtaskValidator->validateCreation($values);
 
         if ($valid) {
             if ($this->subtaskModel->create($values) !== false) {
@@ -101,11 +102,11 @@ class SubtaskController extends BaseController
      * @throws PageNotFoundException
      */
     public function edit(array $values = array(), array $errors = array())
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         $this->response->html($this->template->render('subtask/edit', array(
             'values' => empty($values) ? $subtask : $values,
             'errors' => $errors,
             'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']),
@@ -121,13 +122,16 @@ class SubtaskController extends BaseController
      * @access public
      */
     public function update()
     {
         $task = $this->getTask();
-        $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         $values = $this->request->getValues();
+        $values['id'] = $subtask['id'];
+        $values['task_id'] = $task['id'];
+
         list($valid, $errors) = $this->subtaskValidator->validateModification($values);
 
         if ($valid) {
             if ($this->subtaskModel->update($values)) {
                 $this->flash->success(t('Sub-task updated successfully.'));
@@ -147,11 +151,11 @@ class SubtaskController extends BaseController
      * @access public
      */
     public function confirm()
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         $this->response->html($this->template->render('subtask/remove', array(
             'subtask' => $subtask,
             'task' => $task,
         )));
@@ -164,11 +168,11 @@ class SubtaskController extends BaseController
      */
     public function remove()
     {
         $this->checkCSRFParam();
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         if ($this->subtaskModel->remove($subtask['id'])) {
             $this->flash->success(t('Sub-task removed successfully.'));
         } else {
             $this->flash->failure(t('Unable to remove this sub-task.'));
diff --git a/app/Controller/SubtaskConverterController.php b/app/Controller/SubtaskConverterController.php
index 404c50d0e..dafbb7e00 100644
--- a/app/Controller/SubtaskConverterController.php
+++ b/app/Controller/SubtaskConverterController.php
@@ -11,22 +11,23 @@ namespace Kanboard\Controller;
 class SubtaskConverterController extends BaseController
 {
     public function show()
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         $this->response->html($this->template->render('subtask_converter/show', array(
             'subtask' => $subtask,
             'task' => $task,
         )));
     }
 
     public function save()
     {
         $project = $this->getProject();
-        $subtask = $this->getSubtask();
+        $task = $this->getTask();
+        $subtask = $this->getSubtask($task);
 
         $task_id = $this->subtaskTaskConversionModel->convertToTask($project['id'], $subtask['id']);
 
         if ($task_id !== false) {
             $this->flash->success(t('Subtask converted to task successfully.'));
diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php
index 99315931f..315b023d1 100644
--- a/app/Controller/SubtaskRestrictionController.php
+++ b/app/Controller/SubtaskRestrictionController.php
@@ -18,11 +18,11 @@ class SubtaskRestrictionController extends BaseController
      * @access public
      */
     public function show()
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
 
         $this->response->html($this->template->render('subtask_restriction/show', array(
             'status_list' => array(
                 SubtaskModel::STATUS_TODO => t('Todo'),
                 SubtaskModel::STATUS_DONE => t('Done'),
@@ -39,11 +39,11 @@ class SubtaskRestrictionController extends BaseController
      * @access public
      */
     public function save()
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
         $values = $this->request->getValues();
 
         // Change status of the previous "in progress" subtask
         $this->subtaskModel->update(array(
             'id'      => $values['id'],
diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php
index ef16fce0f..c912848e7 100644
--- a/app/Controller/SubtaskStatusController.php
+++ b/app/Controller/SubtaskStatusController.php
@@ -16,11 +16,11 @@ class SubtaskStatusController extends BaseController
      * @access public
      */
     public function change()
     {
         $task = $this->getTask();
-        $subtask = $this->getSubtask();
+        $subtask = $this->getSubtask($task);
         $fragment = $this->request->getStringParam('fragment');
 
         $status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
         $subtask['status'] = $status;
 
@@ -41,23 +41,23 @@ class SubtaskStatusController extends BaseController
      * @access public
      */
     public function timer()
     {
         $task = $this->getTask();
-        $subtaskId = $this->request->getIntegerParam('subtask_id');
+        $subtask = $this->getSubtask($task);
         $timer = $this->request->getStringParam('timer');
 
         if ($timer === 'start') {
-            $this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId());
+            $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId());
         } elseif ($timer === 'stop') {
-            $this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId());
+            $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId());
             $this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']);
         }
 
         $this->response->html($this->template->render('subtask/timer', array(
             'task'    => $task,
-            'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId),
+            'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']),
         )));
     }
 
     /**
      * Render table
diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php
index df23f87bb..946451fcb 100644
--- a/app/Controller/TaskExternalLinkController.php
+++ b/app/Controller/TaskExternalLinkController.php
@@ -72,10 +72,12 @@ class TaskExternalLinkController extends BaseController
      */
     public function save()
     {
         $task = $this->getTask();
         $values = $this->request->getValues();
+        $values['task_id'] = $task['id'];
+
         list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
 
         if ($valid) {
             if ($this->taskExternalLinkModel->create($values) !== false) {
                 $this->flash->success(t('Link added successfully.'));
@@ -106,26 +108,18 @@ class TaskExternalLinkController extends BaseController
      * @throws \Kanboard\Core\Controller\AccessForbiddenException
      */
     public function edit(array $values = array(), array $errors = array())
     {
         $task = $this->getTask();
-        $link_id = $this->request->getIntegerParam('link_id');
-
-        if ($link_id > 0) {
-            $values = $this->taskExternalLinkModel->getById($link_id);
-        }
-
-        if (empty($values)) {
-            throw new PageNotFoundException();
-        }
-
-        $provider = $this->externalLinkManager->getProvider($values['link_type']);
+        $link = $this->getExternalTaskLink($task);
+        $provider = $this->externalLinkManager->getProvider($link['link_type']);
 
         $this->response->html($this->template->render('task_external_link/edit', array(
-            'values' => $values,
-            'errors' => $errors,
-            'task' => $task,
+            'values'       => empty($values) ? $link : $values,
+            'errors'       => $errors,
+            'task'         => $task,
+            'link'         => $link,
             'dependencies' => $provider->getDependencies(),
         )));
     }
 
     /**
@@ -134,11 +128,16 @@ class TaskExternalLinkController extends BaseController
      * @access public
      */
     public function update()
     {
         $task = $this->getTask();
+        $link = $this->getExternalTaskLink($task);
+
         $values = $this->request->getValues();
+        $values['id'] = $link['id'];
+        $values['task_id'] = $link['task_id'];
+
         list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
 
         if ($valid && $this->taskExternalLinkModel->update($values)) {
             $this->flash->success(t('Link updated successfully.'));
             return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
@@ -153,16 +152,11 @@ class TaskExternalLinkController extends BaseController
      * @access public
      */
     public function confirm()
     {
         $task = $this->getTask();
-        $link_id = $this->request->getIntegerParam('link_id');
-        $link = $this->taskExternalLinkModel->getById($link_id);
-
-        if (empty($link)) {
-            throw new PageNotFoundException();
-        }
+        $link = $this->getExternalTaskLink($task);
 
         $this->response->html($this->template->render('task_external_link/remove', array(
             'link' => $link,
             'task' => $task,
         )));
@@ -175,12 +169,13 @@ class TaskExternalLinkController extends BaseController
      */
     public function remove()
     {
         $this->checkCSRFParam();
         $task = $this->getTask();
+        $link = $this->getExternalTaskLink($task);
 
-        if ($this->taskExternalLinkModel->remove($this->request->getIntegerParam('link_id'))) {
+        if ($this->taskExternalLinkModel->remove($link['id'])) {
             $this->flash->success(t('Link removed successfully.'));
         } else {
             $this->flash->failure(t('Unable to remove this link.'));
         }
 
diff --git a/app/Controller/TaskInternalLinkController.php b/app/Controller/TaskInternalLinkController.php
index 7c8001654..02cc15c4d 100644
--- a/app/Controller/TaskInternalLinkController.php
+++ b/app/Controller/TaskInternalLinkController.php
@@ -11,28 +11,10 @@ use Kanboard\Core\Controller\PageNotFoundException;
  * @author   Olivier Maridat
  * @author   Frederic Guillot
  */
 class TaskInternalLinkController extends BaseController
 {
-    /**
-     * Get the current link
-     *
-     * @access private
-     * @return array
-     * @throws PageNotFoundException
-     */
-    private function getTaskLink()
-    {
-        $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id'));
-
-        if (empty($link)) {
-            throw new PageNotFoundException();
-        }
-
-        return $link;
-    }
-
     /**
      * Creation form
      *
      * @access public
      * @param array $values
@@ -43,13 +25,11 @@ class TaskInternalLinkController extends BaseController
     public function create(array $values = array(), array $errors = array())
     {
         $task = $this->getTask();
 
         if (empty($values)) {
-          $values = array(
-              'another_tasklink' => $this->request->getIntegerParam('another_tasklink', 0)
-          );
+          $values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0);
           $values = $this->hook->merge('controller:tasklink:form:default', $values, array('default_values' => $values));
         }
 
         $this->response->html($this->template->render('task_internal_link/create', array(
             'values' => $values,
@@ -66,10 +46,11 @@ class TaskInternalLinkController extends BaseController
      */
     public function save()
     {
         $task = $this->getTask();
         $values = $this->request->getValues();
+        $values['task_id'] = $task['id'];
 
         list($valid, $errors) = $this->taskLinkValidator->validateCreation($values);
 
         if ($valid) {
             if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id']) !== false) {
@@ -104,11 +85,11 @@ class TaskInternalLinkController extends BaseController
      * @throws \Kanboard\Core\Controller\AccessForbiddenException
      */
     public function edit(array $values = array(), array $errors = array())
     {
         $task = $this->getTask();
-        $task_link = $this->getTaskLink();
+        $task_link = $this->getInternalTaskLink($task);
 
         if (empty($values)) {
             $opposite_task = $this->taskFinderModel->getById($task_link['opposite_task_id']);
             $values = $task_link;
             $values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title'];
@@ -129,11 +110,15 @@ class TaskInternalLinkController extends BaseController
      * @access public
      */
     public function update()
     {
         $task = $this->getTask();
+        $task_link = $this->getInternalTaskLink($task);
+
         $values = $this->request->getValues();
+        $values['task_id'] = $task['id'];
+        $values['id'] = $task_link['id'];
 
         list($valid, $errors) = $this->taskLinkValidator->validateModification($values);
 
         if ($valid) {
             if ($this->taskLinkModel->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
@@ -153,11 +138,11 @@ class TaskInternalLinkController extends BaseController
      * @access public
      */
     public function confirm()
     {
         $task = $this->getTask();
-        $link = $this->getTaskLink();
+        $link = $this->getInternalTaskLink($task);
 
         $this->response->html($this->template->render('task_internal_link/remove', array(
             'link' => $link,
             'task' => $task,
         )));
@@ -170,12 +155,13 @@ class TaskInternalLinkController extends BaseController
      */
     public function remove()
     {
         $this->checkCSRFParam();
         $task = $this->getTask();
+        $link = $this->getInternalTaskLink($task);
 
-        if ($this->taskLinkModel->remove($this->request->getIntegerParam('link_id'))) {
+        if ($this->taskLinkModel->remove($link['id'])) {
             $this->flash->success(t('Link removed successfully.'));
         } else {
             $this->flash->failure(t('Unable to remove this link.'));
         }
 
diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php
index 1892a2098..338ed5401 100644
--- a/app/Controller/TaskModificationController.php
+++ b/app/Controller/TaskModificationController.php
@@ -96,10 +96,12 @@ class TaskModificationController extends BaseController
      */
     public function update()
     {
         $task = $this->getTask();
         $values = $this->request->getValues();
+        $values['id'] = $task['id'];
+        $values['project_id'] = $task['project_id'];
 
         list($valid, $errors) = $this->taskValidator->validateModification($values);
 
         if ($valid && $this->updateTask($task, $values, $errors)) {
             $this->flash->success(t('Task updated successfully.'));
diff --git a/app/Controller/TaskRecurrenceController.php b/app/Controller/TaskRecurrenceController.php
index c6fdfa378..7f14cb534 100644
--- a/app/Controller/TaskRecurrenceController.php
+++ b/app/Controller/TaskRecurrenceController.php
@@ -45,10 +45,11 @@ class TaskRecurrenceController extends BaseController
      */
     public function update()
     {
         $task = $this->getTask();
         $values = $this->request->getValues();
+        $values['id'] = $task['id'];
 
         list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
 
         if ($valid) {
             if ($this->taskModificationModel->update($values)) {
diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php
index 0e19ac196..55e972dc0 100644
--- a/app/Template/comment/create.php
+++ b/app/Template/comment/create.php
@@ -6,12 +6,10 @@
         </li>
     </ul>
 </div>
 <form method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('task_id', $values) ?>
-    <?= $this->form->hidden('user_id', $values) ?>
 
     <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?>
 
     <?= $this->modal->submitButtons() ?>
 </form>
diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php
index 04f6ffd47..db8d2921f 100644
--- a/app/Template/comment/edit.php
+++ b/app/Template/comment/edit.php
@@ -2,13 +2,10 @@
     <h2><?= t('Edit a comment') ?></h2>
 </div>
 
 <form method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off">
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('id', $values) ?>
-    <?= $this->form->hidden('task_id', $values) ?>
-    <?= $this->form->hidden('user_id', $values) ?>
 
     <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?>
 
     <?= $this->modal->submitButtons() ?>
 </form>
diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php
index 96ad7a468..bbb640053 100644
--- a/app/Template/subtask/create.php
+++ b/app/Template/subtask/create.php
@@ -1,13 +1,12 @@
 <div class="page-header">
     <h2><?= t('Add a sub-task') ?></h2>
 </div>
 
 <form method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
-
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('task_id', $values) ?>
+
     <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?>
     <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?>
     <?= $this->subtask->renderTimeEstimatedField($values, $errors) ?>
 
     <?= $this->hook->render('template:subtask:form:create', array('values' => $values, 'errors' => $errors)) ?>
diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php
index 7c0266a8e..aed57e957 100644
--- a/app/Template/subtask/edit.php
+++ b/app/Template/subtask/edit.php
@@ -2,12 +2,10 @@
     <h2><?= t('Edit a sub-task') ?></h2>
 </div>
 
 <form method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off">
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('id', $values) ?>
-    <?= $this->form->hidden('task_id', $values) ?>
 
     <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?>
     <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?>
     <?= $this->subtask->renderTimeEstimatedField($values, $errors) ?>
     <?= $this->subtask->renderTimeSpentField($values, $errors) ?>
diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php
index df10d444f..e448b10f3 100644
--- a/app/Template/task_external_link/edit.php
+++ b/app/Template/task_external_link/edit.php
@@ -1,8 +1,8 @@
 <div class="page-header">
     <h2><?= t('Edit external link') ?></h2>
 </div>
 
-<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'link_id' => $link['id'])) ?>" method="post" autocomplete="off">
     <?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?>
     <?= $this->modal->submitButtons() ?>
 </form>
diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php
index a3665c0d0..29d85101e 100644
--- a/app/Template/task_external_link/find.php
+++ b/app/Template/task_external_link/find.php
@@ -2,11 +2,10 @@
     <h2><?= t('Add a new external link') ?></h2>
 </div>
 
 <form action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
 
     <?= $this->form->label(t('External link'), 'text') ?>
     <?= $this->form->text(
         'text',
         $values,
diff --git a/app/Template/task_external_link/form.php b/app/Template/task_external_link/form.php
index 932ca5217..4ad2b2e0f 100644
--- a/app/Template/task_external_link/form.php
+++ b/app/Template/task_external_link/form.php
@@ -1,8 +1,6 @@
 <?= $this->form->csrf() ?>
-<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
-<?= $this->form->hidden('id', $values) ?>
 <?= $this->form->hidden('link_type', $values) ?>
 
 <?= $this->form->label(t('URL'), 'url') ?>
 <?= $this->form->text('url', $values, $errors, array('required')) ?>
 
diff --git a/app/Template/task_internal_link/create.php b/app/Template/task_internal_link/create.php
index c5e80f414..bab412537 100644
--- a/app/Template/task_internal_link/create.php
+++ b/app/Template/task_internal_link/create.php
@@ -3,11 +3,10 @@
 </div>
 
 <form action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
 
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
     <?= $this->form->hidden('opposite_task_id', $values) ?>
 
     <?= $this->form->label(t('Label'), 'link_id') ?>
     <?= $this->form->select('link_id', $labels, $values, $errors) ?>
 
diff --git a/app/Template/task_internal_link/edit.php b/app/Template/task_internal_link/edit.php
index 5abf7b65e..fab84d0b6 100644
--- a/app/Template/task_internal_link/edit.php
+++ b/app/Template/task_internal_link/edit.php
@@ -1,14 +1,12 @@
 <div class="page-header">
     <h2><?= t('Edit link') ?></h2>
 </div>
 
 <form action="<?= $this->url->href('TaskInternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'link_id' => $task_link['id'])) ?>" method="post" autocomplete="off">
-
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('id', $values) ?>
-    <?= $this->form->hidden('task_id', $values) ?>
+
     <?= $this->form->hidden('opposite_task_id', $values) ?>
 
     <?= $this->form->label(t('Label'), 'link_id') ?>
     <?= $this->form->select('link_id', $labels, $values, $errors) ?>
 
diff --git a/app/Template/task_modification/show.php b/app/Template/task_modification/show.php
index 710abedff..ebe9f6fd6 100644
--- a/app/Template/task_modification/show.php
+++ b/app/Template/task_modification/show.php
@@ -1,12 +1,10 @@
 <div class="page-header">
     <h2><?= $this->text->e($project['name']) ?> &gt; <?= $this->text->e($task['title']) ?></h2>
 </div>
 <form method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
     <?= $this->form->csrf() ?>
-    <?= $this->form->hidden('id', $values) ?>
-    <?= $this->form->hidden('project_id', $values) ?>
 
     <div class="task-form-container">
         <div class="task-form-main-column">
             <?= $this->task->renderTitleField($values, $errors) ?>
             <?= $this->task->renderDescriptionField($values, $errors) ?>

Vulnerable code:     /**
     * Get the current subtask
     *
     * @access protected
     * @return array
     * @throws PageNotFoundException
     */
    protected function getSubtask()
    /**
     * Get the current comment
     *
     * @access protected
     * @return array
     * @throws PageNotFoundException
     * @throws AccessForbiddenException
     */
    protected function getComment()
    {
        $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id'));

        if (empty($comment)) {
            throw new PageNotFoundException();
        }

        if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
            throw new AccessForbiddenException();
        }

        return $comment;
    }


        if (empty($values)) {
            $values = array(
                'user_id' => $this->userSession->getId(),
                'task_id' => $task['id'],
            );
        }

        $comment = $this->getComment();
        $this->getComment();
        $comment = $this->getComment();
        $comment = $this->getComment();
        $subtask = $this->getSubtask();
        $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtask = $this->getSubtask();
        $subtaskId = $this->request->getIntegerParam('subtask_id');
            $this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId());
            $this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId());
            'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId),
        $link_id = $this->request->getIntegerParam('link_id');

        if ($link_id > 0) {
            $values = $this->taskExternalLinkModel->getById($link_id);
        }

        if (empty($values)) {
            throw new PageNotFoundException();
        }

        $provider = $this->externalLinkManager->getProvider($values['link_type']);
            'values' => $values,
            'errors' => $errors,
            'task' => $task,
        $link_id = $this->request->getIntegerParam('link_id');
        $link = $this->taskExternalLinkModel->getById($link_id);

        if (empty($link)) {
            throw new PageNotFoundException();
        }
        if ($this->taskExternalLinkModel->remove($this->request->getIntegerParam('link_id'))) {
    /**
     * Get the current link
     *
     * @access private
     * @return array
     * @throws PageNotFoundException
     */
    private function getTaskLink()
    {
        $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id'));

        if (empty($link)) {
            throw new PageNotFoundException();
        }

        return $link;
    }

          $values = array(
              'another_tasklink' => $this->request->getIntegerParam('another_tasklink', 0)
          );
        $task_link = $this->getTaskLink();
        $link = $this->getTaskLink();
        if ($this->taskLinkModel->remove($this->request->getIntegerParam('link_id'))) {
    <?= $this->form->hidden('task_id', $values) ?>
    <?= $this->form->hidden('user_id', $values) ?>
    <?= $this->form->hidden('id', $values) ?>
    <?= $this->form->hidden('task_id', $values) ?>
    <?= $this->form->hidden('user_id', $values) ?>

    <?= $this->form->hidden('task_id', $values) ?>
    <?= $this->form->hidden('id', $values) ?>
    <?= $this->form->hidden('task_id', $values) ?>
<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
    <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
<?= $this->form->hidden('id', $values) ?>
    <?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>

    <?= $this->form->hidden('id', $values) ?>
    <?= $this->form->hidden('task_id', $values) ?>
    <?= $this->form->hidden('id', $values) ?>
    <?= $this->form->hidden('project_id', $values) ?>
Fixed code:     protected function getSubtask(array $task)
        if ($subtask['task_id'] != $task['id']) {
            throw new AccessForbiddenException();
        }

    protected function getComment(array $task)
    {
        $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id'));

        if (empty($comment)) {
            throw new PageNotFoundException();
        }

        if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
            throw new AccessForbiddenException();
        }

        if ($comment['task_id'] != $task['id']) {
            throw new AccessForbiddenException();
        }

        return $comment;
    }

    protected function getExternalTaskLink(array $task)
    {
        $link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id'));

        if (empty($link)) {
            throw new PageNotFoundException();
        }

        if ($link['task_id'] != $task['id']) {
            throw new AccessForbiddenException();
        }

        return $link;
    }

    protected function getInternalTaskLink(array $task)
    {
        $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id'));

        if (empty($link)) {
            throw new PageNotFoundException();
        }

        if ($link['task_id'] != $task['id']) {
            throw new AccessForbiddenException();
        }

        return $link;
    }

        $comment = $this->getComment($task);
        $comment = $this->getComment($task);
        $values['id'] = $comment['id'];
        $values['task_id'] = $task['id'];
        $values['user_id'] = $comment['user_id'];

        $comment = $this->getComment($task);
        $comment = $this->getComment($task);
        $values['task_id'] = $task['id'];
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $values['id'] = $subtask['id'];
        $values['task_id'] = $task['id'];

        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $task = $this->getTask();
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
        $subtask = $this->getSubtask($task);
            $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId());
            $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId());
            'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']),
        $values['task_id'] = $task['id'];

        $link = $this->getExternalTaskLink($task);
        $provider = $this->externalLinkManager->getProvider($link['link_type']);
            'values'       => empty($values) ? $link : $values,
            'errors'       => $errors,
            'task'         => $task,
            'link'         => $link,
        $link = $this->getExternalTaskLink($task);

        $values['id'] = $link['id'];
        $values['task_id'] = $link['task_id'];

        $link = $this->getExternalTaskLink($task);
        $link = $this->getExternalTaskLink($task);
        if ($this->taskExternalLinkModel->remove($link['id'])) {
          $values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0);
        $values['task_id'] = $task['id'];
        $task_link = $this->getInternalTaskLink($task);
        $task_link = $this->getInternalTaskLink($task);

        $values['task_id'] = $task['id'];
        $values['id'] = $task_link['id'];
        $link = $this->getInternalTaskLink($task);
        $link = $this->getInternalTaskLink($task);
        if ($this->taskLinkModel->remove($link['id'])) {
        $values['id'] = $task['id'];
        $values['project_id'] = $task['project_id'];
        $values['id'] = $task['id'];

<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'link_id' => $link['id'])) ?>" method="post" autocomplete="off">