0

pals, got a quite disgusting situation here with my submit form. So, the problem: once user submits form his data goes to database and THEN if he refreshes the page the form submits again and he can do it infinite times and my DB will be full of useless data. So, how to prevent form submition after f5? I tried to use header('Location: success.php'); , but it doesn't help. Here is my server.php code:

<?php
session_start();
$message = "Wrong input";
$username = "";
$email    = "";
$errors = array(); 

$db = mysqli_connect('localhost', 'root', 'root', 'example');


if(isset($_POST['register'])) {
    $username = mysqli_real_escape_string($db, $_POST['username']);
    $email = mysqli_real_escape_string($db, $_POST['email']);
    $password = mysqli_real_escape_string($db, $_POST['password']);

    if(empty($username) || empty($email) || empty($password)) {
        echo "<script type='text/javascript'> alert('$message');</script>";
        array_push($errors, "err");
    }

}

if(count($errors) == 0) {
    $pass = md5($password);
    $query = "INSERT INTO users (username, email, password) VALUES ('$username', '$email', '$pass')";

    mysqli_query($db, $query);
    header('Location: success.php');
}


?>
  • Possible duplicate of [Stop browsers asking to resend form data on refresh?](https://stackoverflow.com/questions/4327236/stop-browsers-asking-to-resend-form-data-on-refresh) – Savvas Radevic Oct 09 '17 at 23:02
  • **WARNING**: When using `mysqli` you should be using [parameterized queries](http://php.net/manual/en/mysqli.quickstart.prepared-statements.php) and [`bind_param`](http://php.net/manual/en/mysqli-stmt.bind-param.php) to add user data to your query. **DO NOT** use manual escaping and string interpolation or concatenation to accomplish this because you will create severe [SQL injection bugs](http://bobby-tables.com/). Accidentally unescaped data is a serious risk. Using bound parameters is less verbose and easier to review to check you’re doing it properly. – tadman Oct 09 '17 at 23:45
  • Note: The object-oriented interface to `mysqli` is significantly less verbose, making code easier to read and audit, and is not easily confused with the obsolete `mysql_query` interface. Before you get too invested in the procedural style it’s worth switching over. Example: `$db = new mysqli(…)` and `$db->prepare("…”)` The procedural interface is an artifact from the PHP 4 era when `mysqli` API was introduced and should not be used in new code. – tadman Oct 09 '17 at 23:45
  • **WARNING**: Writing your own access control layer is not easy and there are many opportunities to get it severely wrong. Please, do not write your own authentication system when any modern [development framework](http://codegeekz.com/best-php-frameworks-for-developers/) like [Laravel](http://laravel.com/) comes with a robust [authentication system](https://laravel.com/docs/master/authentication) built-in. At the absolute least follow [recommended security best practices](http://www.phptherightway.com/#security) and **never store passwords with a weak, high-speed hash like SHA1 or MD5**. – tadman Oct 09 '17 at 23:46
  • thanks, it's only for training purposes, just tryin to figure out what's goin on – FeelsBadMan Oct 09 '17 at 23:48
  • Learning is fine, don't get me wrong, but you're fumbling around in the dark here for lack of good examples to work from. I've added an answer with more explanation. Capturing user data and saving it in a database is a huge responsibility, so it's important to learn how to do it properly, not just learn the quickest way that produces results. – tadman Oct 09 '17 at 23:56
  • A good way of fixing this is shown here: https://stackoverflow.com/a/570069/286994 – Savvas Radevic Oct 10 '17 at 00:04
  • calm, buddy, I got your point – FeelsBadMan Oct 10 '17 at 00:07

2 Answers2

1

f5 will just send the last request again, so you can't stop it

if you want to prevent that, you should add some test before creating a new user (check if the user is new or not, validate email...) and storing it inside the db

and change your md5 hash for the password. you should use a salt + sha* hash solution. you should considere to update your code with preparedStatement too

sheplu
  • 2,937
  • 3
  • 24
  • 21
  • The first to points are valid. The SHA part is **extremely, super, mega wrong**. Use [`password_hash`](https://secure.php.net/manual/en/function.password-hash.php) and **only** that unless you have a very compelling reason, like using something better. – tadman Oct 09 '17 at 23:48
0

The reason you're getting junk data here is because you have zero validation logic. Every application must do at least some sort of superficial parameter cleanup, such as removing extraneous spaces, forcing lower or upper case for certain fields, and stripping any unwanted characters.

Then you'll need to check that the required fields are present, and are in a form that's acceptable. For example, an email address must contain at least an @, a name must contain at least one letter, and so on. Different field types have different requirements, where generally passwords have the most constraints, as you might need to enforce minimum/maximum lengths, presence of capital letters and/or numbers, and other such considerations.

If and only if the data's passed that scruitiny do you move on to the creation phase where applications must do is verify uniqueness of certain fields. This usually involves a quick check of the form:

SELECT COUNT(*) FROM users WHERE username=?

Note that this check is considered advisory only, that the information gleaned here is immediately considered obsolete. It can be used to present information to the user in the form of errors. It cannot be trusted going forward in the code, that is, your database can and will change after that statement is executed, rendering any tests invalid.

The third step is to commit the record, actually insert it. To prevent duplication at this point you'll need to have a UNIQUE constraint on any fields that must be unique. You'll also need to have code that captures these errors and re-surfaces them to the user in a form they can understand.

If this sounds like a lot of work, it's because it is. Don't write all this code by hand, it's a huge waste of time and you will get it wrong. Use a development framework to give you the foundation for this sort of thing. They come in a variety of flavours, from very light-weight like Fat-Free Framework to extremely full-featured like Laravel and all shades between. These implement everything I've talked about in different ways, the syntax and methodology can vary considerably, but the principles are the same.

Find one that you like, learn it well, and follow their community best-practices.

tadman
  • 208,517
  • 23
  • 234
  • 262