Search code examples
javascriptphpsecuritymysqliwebsecurity

PHP and JavaScript variable passing and security


I'm trying to make a notification system for my essay review website here is the basic flow of what should happen:

flow

and here are my tables' structures(MySQL):

schema

The problem I'm having is that I can't figure out how to update the status in the relations table securely once a user accepts or declines a review request. I could set the values of the accept/decline buttons to the id of the relationship and use ajax to update the status of that relationship, but that doesn't seem secure as a user could just change the value with inspect element.

Here is an example of what I have:
request.php

<?php
//define global variables
$dbhost = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbdatabase = "test";
$conn = new mysqli($dbhost, $dbusername, $dbpassword, $dbdatabase);
?>
<a href="user43notifs.php">Click here to go to user 43's notifications</a><br><br>

<!--Example requests-->

<form action="request.inc.php" method="post">
  op_id: 43<br>
  reviewer_id: 42<br>
  essay_id: 34<br>
  <input type="hidden" name="op_id" value="43">
  <input type="hidden" name="reviewer_id" value="42">
  <input type="hidden" name="essay_id" value="34">
  <input type="submit" name="submit">
</form>
<form action="request.inc.php" method="post">
  op_id: 43<br>
  reviewer_id: 16<br>
  essay_id: 135<br>
  <input type="hidden" name="op_id" value="43">
  <input type="hidden" name="reviewer_id" value="16">
  <input type="hidden" name="essay_id" value="135">
  <input type="submit" name="submit">
</form>
<form action="request.inc.php" method="post">
  op_id: 78<br>
  reviewer_id: 12<br>
  essay_id: 25<br>
  <input type="hidden" name="op_id" value="78">
  <input type="hidden" name="reviewer_id" value="12">
  <input type="hidden" name="essay_id" value="25">
  <input type="submit" name="submit">
</form>

request.inc.php

<?php
//define global variables
$dbhost = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbdatabase = "test";
$conn = new mysqli($dbhost, $dbusername, $dbpassword, $dbdatabase);

$op = mysqli_real_escape_string($conn, $_POST['op_id']);
$reviewer = mysqli_real_escape_string($conn, $_POST['reviewer_id']);
$essay = mysqli_real_escape_string($conn, $_POST['essay_id']);

$sql = "INSERT INTO `reviewer_relations` (`reviewer_id`, `essay_id`, `status`)
VALUES ('$reviewer', '$essay', 0)";

$result=mysqli_query($conn, $sql);
if($result === TRUE){
  $title = mysqli_real_escape_string($conn, $reviewer." has requested to review your essay: essay#".$essay.".");
  $message = mysqli_real_escape_string($conn, '<button onclick="location.href=\'scripts/review_request.php?confirmation=accept\'" class="review-accept">Accept</button><button onclick="location.href=\'scripts/review_request.php?confirmation=decline\'" class="review-decline">Decline</button>');
  $sql = "INSERT INTO `notifications` (`user_id`, `title`, `message`)
  VALUES ('$op', '$title', '$message')";

  $result=mysqli_query($conn, $sql);

  if($result === TRUE){
    echo 'notification and relation insert success';
  }
  else{
    echo 'notification insert fail: '.mysqli_error($conn);
  }
}
else{
  echo 'relation insert fail: '.mysqli_error($conn);
}
?>

user43notifs.php

<?php
$dbhost = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbdatabase = "test";
$conn = new mysqli($dbhost, $dbusername, $dbpassword, $dbdatabase);

$sql="SELECT *
FROM notifications
WHERE user_id = 43";
$result = mysqli_query($conn, $sql);
while($row = mysqli_fetch_assoc($result)){
  echo '**********************************************<br>';
  echo $row['title'].'<br>';
  echo $row['message'].'<br>';
}
?>

using these two table tables setup up with PHPMyAdmin:
reviewer_relations
reviewer_relations
notifications
enter image description here

I need a secure way to update the status column of the reviewer_relation that is represented by the notification when the user clicks on said notification's accept or decline button.

The problem is I can't figure out a way to associate the relationship id (or the reviewer_id and essay_id that describe the relationship) to it's notification without putting it directly into the notification's HTML where it's vulnerable to be changed.

I don't often ask questions, so any critique on how the question is titled, written, or stated is greatly appreciated. If any additional information is needed please ask. Thanks!


Solution

  • I believe I've found a solution:
    I've added a token column to the user-essay relations table so that each request can be identified with a unique, cryptographically secure token (See Scott's updated answer here for token generating code).
    Then when the notification is inserted into the notification table I set the value of the accept button as the token. That way when the notifications page is loaded the token can be retrieved by using .val() and used with ajax to update the status of the appropriate request. So, while a user could change the value of the button the chances of them guessing the 100-character-long token of another request is astronomically small.
    Please let me know if this is not as secure as I think it to be.