-3
<a href="rate.php?winner=<?=$images[0]->image_id?>&loser=<?=$images[1]->image_id?>"></a>

This is main page index.php

This was my main page. Before uploading to php file parameters can be changed using inspect element and this is a problem.

This is rate.php

<?php


include('mysql.php');
include('functions.php');


// If rating - update the database
if ($_GET['winner'] && $_GET['loser']) {


// Get the winner
$result = $conn->query("SELECT * FROM images WHERE image_id = ".$_GET['winner']." ");
$winner = $result->fetch_object();


// Get the loser
$result = $conn->query("SELECT * FROM images WHERE image_id = ".$_GET['loser']." ");
$loser = $result->fetch_object();


// Update the winner score
$winner_expected = expected($loser->score, $winner->score);
$winner_new_score = win($winner->score, $winner_expected);
    //test print "Winner: ".$winner->score." - ".$winner_new_score." - ".$winner_expected."<br>";
$conn->query("UPDATE images SET score = ".$winner_new_score.", wins = wins+1 WHERE image_id = ".$_GET['winner']);


// Update the loser score
$loser_expected = expected($winner->score, $loser->score);
$loser_new_score = loss($loser->score, $loser_expected);
    //test print "Loser: ".$loser->score." - ".$loser_new_score." - ".$loser_expected."<br>";
$conn->query("UPDATE images SET score = ".$loser_new_score.", losses = losses+1  WHERE image_id = ".$_GET['loser']);


// Insert battle
$conn->query("INSERT INTO battles SET winner = ".$_GET['winner'].", loser = ".$_GET['loser']." ");


// Back to the frontpage
header('location: /');

}


?>

I just want that parameters can be modified while sending data to php file

Swarnveer
  • 490
  • 5
  • 23
  • 6
    You have a _much_ bigger issue than someone changing an ID. You are wide open to [SQL Injections](http://php.net/manual/en/security.database.sql-injection.php) and should really use [Prepared Statements](http://php.net/manual/en/mysqli.quickstart.prepared-statements.php) instead of concatenating your queries. Specially since you're not escaping the user inputs at all! – M. Eriksson Dec 08 '17 at 18:40
  • 1
    If you tell us what this actually is and who/when someone clicks the URL, it would be easier to recommend a solution. As it looks, you're using the wrong technique for what you're doing. A GET request should never_change anything on the server. GET is for getting data, not setting. – M. Eriksson Dec 08 '17 at 18:45
  • Someone changing some ID in a request should _never_ be a problem. You are offering a public API. That has to be robust enough against any request send against it _per definition_. If it is not, and you rely on some cheap obfuscation (which is what you ask for), then your architecture is fundamentally wrong. – arkascha Dec 08 '17 at 18:46
  • There are two photos. user can click on one single photo. whichever photo is clicked its won count gets increased and the lose count increase of photo which is not clicked – Swarnveer Dec 08 '17 at 18:47
  • So I can make a three-line-script that "clicks" a photo a million times in a second and then I have "won"? – arkascha Dec 08 '17 at 18:49
  • Sounds like he's doing a hotornot clone. – catbadger Dec 08 '17 at 19:00
  • The images that are appearing are random – Swarnveer Dec 08 '17 at 19:05
  • It is exploited by others. They changed the score and win count to whatever value they want – Swarnveer Dec 08 '17 at 19:05
  • Possibly duplicate: https://stackoverflow.com/questions/20129647/how-can-i-eliminate-cheating-for-an-online-survey – Quentin Dec 08 '17 at 19:30

2 Answers2

3

You need to add some extra verification/validation to your code. That's regardless if you're using GET or POST to pass the data.

You can set a session per call that defines what ID's the user are allowed to pass. It works like a basic CSRF protection:

It can be something like the below:

On the voting page:

<?php 
// Start sessions (should always be in the top
session_start();

// Get the image id's some how. Let's use these as an example
// This could just as well be strings or what ever it is you're posting
$image1 = 1;
$image2 = 2;

// Generate a pseudo random token
$token = bin2hex(random_bytes(16));

// Store the image references in a session with the token as name
$_SESSION[$token] = [$image1, $image2];
?>

// HTML that sends the image references and the token (important)

On the page that receives the data:

<?php
// Again, start sessions;
session_start();

// Check that all parameters are there
if (!isset($_POST['winner'], $_POST['loser'], $_POST['token'])) {
    die('Invalid request');
}

$winner = $_POST['winner'];
$looser = $_POST['loser'];
$token  = $_POST['token'];

// Check if the session is set. If not, then the call didn't come from your page
if (!$token || empty($_SESSION[$token])) {
    die('We have a CSRF attack');
}

// Check if both image references exists in session. If not, then someone have change the values
if (!in_array($winner, $_SESSION[$token]) || !in_array($loser, $_SESSION[$token])) {
    die('Invalid image references! We have a cheater!');
}

// Remove the token from the session so the user can't repeat the call
unset($_SESSION[$token]);

// Do your DB stuff using Prepared Statements.

This is an untested example, so it might not work straight out of the gate, but it shows you a technique that can be used.

Important

You are currently wide open to SQL Injections and should really use Prepared Statements instead of concatenating your queries. Specially since you're not escaping the user inputs at all!

M. Eriksson
  • 13,450
  • 4
  • 29
  • 40
  • There is no user based registration. It is just a normal page which consistantly shows 2 random images and user have to click on any one of them. Any user can do n number of clicks. But the images are randomly generating – Swarnveer Dec 08 '17 at 19:15
  • The above doesn't require any users to be registered. When I refer to a "user", it's just someone that uses your app. – M. Eriksson Dec 08 '17 at 19:16
  • then what's the id all about ? how will it get the value ? It cant be always 1 and 2 – Swarnveer Dec 08 '17 at 19:18
  • It's just an example. It's the image id's. You can get those how ever you want, but you need to get what images to show, right? I updated the answer to be clearer. – M. Eriksson Dec 08 '17 at 19:21
  • In the voting page can I send that token and id through that rate.php link ? with the get parameters as specified ? Or how to send it ? – Swarnveer Dec 08 '17 at 19:26
  • As I mentioned in the second sentence in the answer, it doesn't matter how you send them. You can use a link, like you're doing now or as a post. Since we've added the token, the link can only be used ones since it then expires and if someone tries to change the values, it simply won't work. – M. Eriksson Dec 08 '17 at 19:28
  • I saw here I don't need to send it. Session is automatically sent ? https://stackoverflow.com/a/7045355/6006704 I set the id's as $image1 = $images[0]->image_id; $image2 = $images[1]->image_id; And it's giving me invalid request even in valid clicks – Swarnveer Dec 08 '17 at 19:49
  • I uploaded my index.php page which is the first page (voting page) – Swarnveer Dec 08 '17 at 19:58
  • What do you mean by "send the sessions". When you start and set a session, PHP will then send a cookie with the session id to the browser and store the data on the server. On the next request, the server gets the session id and retrieves the data. You don't need to do anything other than start sessions and set the session values. – M. Eriksson Dec 08 '17 at 20:03
  • I solved that issue. But about sql injection. How can I check? Or how can I try to hack mine own and correct it. I saw about prepared statement but it's not clear – Swarnveer Dec 08 '17 at 21:04
  • Just read the manual. It's pretty easy, Just a couple of more rows: http://php.net/manual/en/mysqli.prepare.php – M. Eriksson Dec 08 '17 at 22:48
  • I searched over internet and people are advising it's not good idea to use preapared statement to SELECT * queries. Can you give an example to convert the following https://pastebin.com/9AxA7dVV – Swarnveer Dec 09 '17 at 03:19
  • Every time you're using user data (from GET/POST, from a file or where ever), you should use prepared statements. Prepared statements are by far the best way you can protect yourself from SQL injection attacks. That's not just my opinion. It's simply because how they work (sending the query and the parameters separately, which removes the risk). Regardless, that's a bit off topic. If the above helped you with your actual question, please mark it as accepted. – M. Eriksson Dec 09 '17 at 11:47
-2

I suggest you to use $_POST instead of $_GET and add some validations on your images ids in your rate.php. Add a jQuery click function to your <a> in your html and within the anonymous function of the click, create winner_image_id and loser_image_id variables and send them to your php by using AJAX.

  • 1
    It's still very prone to attacks. I can just create a cURL call in a loop and post what ever I want, how many times I want to. For it to be safe, you need to add in some CSRF techniques and validate the POST and the id's when it's received. – M. Eriksson Dec 08 '17 at 18:54
  • That's right, some validation needs to be done on in the php once the post is received – Michael Tétreault Dec 08 '17 at 19:06
  • You should make sure that the ids are set by using "if isset()" and then make sure that they are integers by using "if is_numeric()". – Michael Tétreault Dec 08 '17 at 19:20
  • `if((!isset($_POST["winner_image_id"]) || $_POST["winner_image_id"] == "") && !is_numeric($_POST["winner_image_id"])) { die("The winner_image_id posted value is not valid."); }` do the same for the loser_image_id – Michael Tétreault Dec 08 '17 at 19:25
  • Even if you do all the above, anyone can still change any value they want in the front end so I'm not sure how this solves the actual issue. – M. Eriksson Dec 08 '17 at 19:35
  • No, If the variables are set in the anonymous function of the click as i said in my post and not global, it is safe, and even if their is a trick to change them, php will die and return an error. Anyways, as i also said, some validations must be done in the php before writing in the database table such as validating if they're set and not empty, that they are integers and if it exist in the actual table before updating and inserting any rows. – Michael Tétreault Dec 08 '17 at 19:48
  • _"It is safe"_ - No it isn't. 1. You don't know if the data comes from your page or if the user sends it from a script. 2. _All_ javascript that lives in the browser can be manipulated. You should _never_ trust _any_ user data (which is what it's called if it comes from a user, regardless how it was sent). – M. Eriksson Dec 08 '17 at 20:07
  • As long as the php validate the data.. does it really matter? – Michael Tétreault Dec 08 '17 at 20:16
  • That was the OP's question. How to make sure it's not manipulated. This doesn't answer that at all. – M. Eriksson Dec 09 '17 at 11:54