0

Im doing a project in PHP, something like a help desk(im new to world of programing). Every employee($zaposlenik) can answer every question once. If he already answered selected question he should be redirected to mojaTvrtka.php (home page of his workplace) and there should be a echo saying "You allready answered that question". Im not sure why it wont work. The else part of IF works fine.

<?php 
include("baza.php");
include("header.php");

$pid=$_POST['id'];
$zaposlenik=$_SESSION['activeUserId'];
$tekst=$_POST["tekstOdgovora"];
$datum=date('Y-m-d H:i:s');

$connect=connectDB();
$query1="SELECT pitanje_id,odgovor.zaposlenik_id AS odgZap
FROM odgovor
LEFT JOIN zaposlenik
ON odgovor.pitanje_id=zaposlenik.korisnik_id
WHERE odgovor.zaposlenik_id='$activeUserId'";

$result1=queryDB($connect,$query1);

if(mysqli_num_rows($result1) == $zaposlenik))
{
        header("Location:mojaTvrtka.php");
        echo "Vec ste odgovorili na ovo pitanje!";
}
else 
{
    $query="INSERT INTO odgovor
    (pitanje_id,zaposlenik_id,tekst,datum_vrijeme_odgovora) VALUES ('$pid','$zaposlenik','$tekst','$datum')";

    $result=queryDB($connect,$query);

header("Location: detaljiPitanja.php?id=$pid");
}

disconnectDB($connect);
?>
brika28
  • 65
  • 8
  • I don't understand your if clause. num_rows returns the number of rows, which is probably 0 or 1, from your description, but you compare it to zaposlenik, which probably is almost never 0 or 1 ? also, if you're just interested in *IF* a user already answered, query with `COUNT` and just retrieve the result. it's also faster, as a side effect. – Jakumi Feb 07 '19 at 18:50
  • @Jakumi would You be kind enough to write that query? – brika28 Feb 07 '19 at 18:58

1 Answers1

0

A few things,

The web is stateless, what this means is that from one request to another things are done separately (more on this later)

In your code

if(mysqli_num_rows($result1) == $zaposlenik))
{
    $message = urlencode("Vec ste odgovorili na ovo pitanje!"); //make sure to url encode it, so it's safe for query args
    header("Location:mojaTvrtka.php?flash_message=$message"); //pass message via the url
    exit(); //always exit after header, ABSOLUTELY no output is allowed before header
}

Now because of the stateless nature of the we have to use some kind of persistent storage to pass the message. Data is not passed across requests. This is part of why your echo doesn't work. The other reason is even if you do echo it, the page redirects so no one can see it. The simplest (and ugliest) way is to just pass that message as part of the url.

It's very important to use exit after a header redirect. As doing things after it can cause "strange" things to happen.

Then On the page this is directed to you can

 $flash_message = isset($_GET['flash_message']) ? "<div class=\"flash_message\" >{$_GET['flash_message']}</div>" : '';

 echo $flash_message;

Untested, but it's simple enough.

The other part

You have this:

  if(mysqli_num_rows($result1) == $zaposlenik))

Basically, what you are saying here is the activeUserId's value should match then number of results. So if my id is 120292 then I must have exactly that number or rows returned of this condition is false. Which then triggers the else, which of course has this $query="INSERT INTO odgovor.

In reality you just want to know if they have 1 row. So

 if(mysqli_num_rows($result1) == 1)//returns exactly 1 rows

Or you can test it's truthfulness simply by ding this way

if(mysqli_num_rows($result1)) //returns some number of rows

One last thing

Even though I cant read this:

$query1="SELECT pitanje_id,odgovor.zaposlenik_id AS odgZap
     FROM odgovor
     LEFT JOIN zaposlenik
     ON odgovor.pitanje_id=zaposlenik.korisnik_id
     WHERE odgovor.zaposlenik_id='$activeUserId'";

I can tell if you need to match a user ID and a question ID, that you have only one WHERE condition. So you probably need to add something for the question ID in there. I would assume that there is some form of relationship for this (which I assume is the answer table) that relates it to the question table. Such as a foreign key (the question id). So you just need to add that in, and get the id of the question in question (see what I did there).

//Obviously this is just PSUDO code, so replace this with your actual field and value...
WHERE odgovor.zaposlenik_id='$activeUserId' AND question_id='$questionId'";

SQL Injection

Also this stuff odgovor.zaposlenik_id='$activeUserId' is venerable to SQL Injection, so please use prepared statements.

And example of injection would be this:

   $activeUserId = "' OR 1 LIMIT 1 --";

What this would do is make your query this:

   SELECT pitanje_id,odgovor.zaposlenik_id AS odgZap
   FROM odgovor
   LEFT JOIN zaposlenik
   ON odgovor.pitanje_id=zaposlenik.korisnik_id
   WHERE odgovor.zaposlenik_id='' OR 1 LIMIT 1 --'

The -- is the start of a comment in SQL so nothing after that runs. What this does is select the first row (OR 1 is always true) and return just that row.

In this case it's pretty mundane as it would just act like they answered the question, but the point is I am able to execute any SQL command I want against your DB. Assuming user input finds it's way into $activeUserId, even if it doesn't is it worth the risk?

Prepared statements allow you do your SQL it like this;

    WHERE odgovor.zaposlenik_id=? AND question_id=?

When you prepare it the DB parses the SQL, says it's ok. Then the values are added in and executed when the DB knows not to make them SQL, thereby making these attacks impossible (If done correctly).

I find showing an example helps to illustrate how bad this is. That said I don't plan to write a tutorial on how to properly prepare queries as there are plenty out there on the web.

Cheers!

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
  • 1
    This idea should work offcourse you need to protect the `$_GET['flash_message']` when displaying in the HTML against Cross Site Scripting attack vectors.. – Raymond Nijland Feb 07 '19 at 19:06
  • That's a good point, I was trying to keep it simple. As the data is only given to the user that sent it, they can run JS only against themselves. If it was displayed to other users of the site then there could be issues with Session Hijacking etc. – ArtisticPhoenix Feb 07 '19 at 19:09
  • "then there could be issues with Session Hijacking etc." The complete PHP sessions handling are by default unsafe to use especially on [shared webhosting (post off me)](https://stackoverflow.com/questions/18262878/how-to-prevent-php-sessions-being-shared-between-different-apache-vhosts/18263063#18263063) if the hoster did configure it wrong and store all vhost sessions into one directory. – Raymond Nijland Feb 07 '19 at 19:14
  • Thanks for your help but this wont go online its a college project..ca you guys help me figure out why my IF statment wont work properly? – brika28 Feb 07 '19 at 19:20
  • `but this wont go online its a college project` why not, it's the simplest way. You can also pass the data through the SESSION, but then you have to check the id, start it etc... – ArtisticPhoenix Feb 07 '19 at 19:30
  • @Raymond Nijland - I realize that, I was just saying that escaping it is "less" important because no other users can see it. AND for the sake of not having to explain it, I simply omitted it. Now if that data was stored in the DB and displayed to other users it may be an issue, but that is not the case here. `XSS enables attackers to inject client-side scripts into web pages viewed by other users` My point is the word *Other* – ArtisticPhoenix Feb 07 '19 at 19:33
  • If I am not mistaken, the worst they could do is break the display of the page for themselves, and that is if they went and manually edited the url to do so. Because of these "things" I felt it wasn't worth the time to explain escaping, but in retrospect it would have been less then this. Cheers! – ArtisticPhoenix Feb 07 '19 at 19:37
  • @ArtisticPhoenix it wont work either with your code..i can still post unlimited number of answers with same user..sorry but im still very fresh in web programing :) – brika28 Feb 07 '19 at 19:40
  • I cant really make sense of the rest of it, I can tell you this is wrong for sure `if(mysqli_num_rows($result1) == $zaposlenik))` `$zaposlenik=$_SESSION['activeUserId'];` and `mysqli_num_rows` is the number of results. So basically the activeUserId should equal the number of results, seems wrong. Pretty sure simply doing this is enough `if(mysqli_num_rows($result1))` – ArtisticPhoenix Feb 07 '19 at 19:48
  • I didnt notice the other part of the answer...I apriciate your help very much.. can you tell where I can learn more about PHP and MYsql? – brika28 Feb 07 '19 at 19:55
  • your doing most of it now `odgovor.zaposlenik_id='$activeUserId'";` I cant read whatever language that is (sorry) but there should be some kind of relationship to the question (if this is the answer) such as a forign key (the id of the question), just add that with an AND. Then if you match the question and the user and get results, they have obviously answered it. – ArtisticPhoenix Feb 07 '19 at 19:56
  • `where I can learn more about PHP and MYsql` not really, I am self taught over many years (nearly 10), But I do have a Associate Degree in Web Design. Just play with it and always try to improve the way you code. I still find ways to improve mine... – ArtisticPhoenix Feb 07 '19 at 20:13