0

I found some code for a simple database search using PHP and MySQL and I was able to make it work. However, I decided it might not be such a great idea to leave my database username and password in the PHP code within the root folder. So, I found another code sample that demonstrated how to create a config.ini file outside the root, and use a dbconnect.php file to access the database credentials. I tried to implement this, but I'm not having any luck and was wondering if someone could show me what I'm missing (I only have a rudimentary grasp of coding and am trying to learn little by little). I have included the code for all of the component files (and I changed any username/passwords/servernames to generic placeholders). Below the code I have pasted the errors that are currently being shown when I submit the search form. Thanks!

index.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Search</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <form action="search.php" method="GET">
            <input type="text" name="query" />
            <input type="submit" value="Search" />
        </form>
    </body>
</html>

search.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Search</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <?php require_once('./includes/dbconnect.php'); ?>
    </head>
    <body>
        <?php
            $query = $_GET['query']; 

            // gets value sent over search form
            $min_length = 3;

            // you can set minimum length of the query if you want
            if(strlen($query) >= $min_length) { // if query length is more or equal minimum length then
                $query = htmlspecialchars($query); 

                // changes characters used in html to their equivalents, for example: < to &gt;
                $query = mysql_real_escape_string($query);

                // makes sure nobody uses SQL injection
                $raw_results = mysql_query("SELECT * FROM details WHERE (`title` LIKE '%".$query."%') OR (`text` LIKE '%".$query."%')") or die(mysql_error());

                // * means that it selects all fields
                // details is the name of our table
                // '%$query%' is what we're looking for, % means anything, for example if $query is Hello
                if(mysql_num_rows($raw_results) > 0) { // if one or more rows are returned do following
                    while($results = mysql_fetch_array($raw_results)) {

                        // $results = mysql_fetch_array($raw_results) puts data from database into array, while it's valid it does the loop
                        echo "<p><h3>".$results['title']."</h3>".$results['text']."</p>";

                        // posts results gotten from database(title and text) you can also show id ($results['id'])
                    }

                } else { // if there is no matching rows do following
                    echo "No results";
                }
            } else { // if query length is less than minimum
                echo "Minimum length is ".$min_length;
            }
    ?>
    </body>
</html>

dbconnect.php:

<?php
    function db_connect() {
        // Define connection as a static variable, to avoid connecting more than once 
        static $connection;

        // Try and connect to the database, if a connection has not been established yet
        if(!isset($connection)) {
            // Load configuration as an array. Use the actual location of your configuration file
            $config = parse_ini_file('/home/cpanelusername/private/config.ini');
            $connection = mysqli_connect($config['servername'],$config['username'],
            $config['password'],$config['dbname']);
        }

        // If connection was not successful, handle the error
        if($connection === false) {
            // Handle error - notify administrator, log to a file, show an error screen, etc.
            return mysqli_connect_error(); 
        }

        return $connection;
    }

    // Connect to the database
    $connection = db_connect();

    // Check connection
    if ($connection->connect_error) {
        die("Connection failed: " . $connection->connect_error);
    }
?> 

config.ini:

[database]
servername = localhost
username = username
password = password
dbname = username_database

Warning: mysql_real_escape_string(): Access denied for user 'root'@'localhost' (using password: NO) in /home4/cpanelusername/public_html/...../search.php on line 29

Warning: mysql_real_escape_string(): A link to the server could not be established in /home4/cpanelusername/public_html/......./search.php on line 29

Warning: mysql_query(): Access denied for user 'root'@'localhost' (using password: NO) in /home4/cpanelusername/public_html/......../search.php on line 33

Warning: mysql_query(): A link to the server could not be established in /home4/cpanelusername/public_html/......./search.php on line 33 Access denied for user 'root'@'localhost' (using password: NO)

M0ns1f
  • 2,705
  • 3
  • 15
  • 25
dlc3172
  • 123
  • 3
  • 13
  • You might have more important concerns... your code is highly vulnerable to sql injection. Google it and you will see many tutorials on how to fix it. – Elias Soares Sep 07 '18 at 02:30
  • About your main question, you may use the dotenv library to import your environment variables (mysql credentials and any other variables) from a file named `.env`. – Elias Soares Sep 07 '18 at 02:31
  • 1
    the code seems OK. The fact that you are getting an access denied error proves that you're actually reaching the DB server, so that's OK. Your first problem is you're trying to connect to the DB as `root` . By default, the root user in mysql is not allowed to connect from anywhere other than the DB server itself so if the DB is not on the same server as the application, it'll never work. The other key is the `using password: NO` error. It appears you're not passing the db user's password upon connecting – Javier Larroulet Sep 07 '18 at 02:31
  • regarding SQL injection, there is a line of code in there that supposedly prevents against it, but I guess you are saying it's not enough. Knowing how to improve it is beyond my skillset. If anyone knows of a pre-written code sample that I could use for a database search form that is secure, that would be appreciated. – dlc3172 Sep 07 '18 at 13:25
  • This code is a bit of a security nightmare. Firstly why are you using the long-deprecated `mysql_` code library? It was discontinued many years ago and removed entirely in PHP7. No new code should be written using this library. It leaves you vulnerable to SQL injection attacks (due to the lack of parameterised query support) and potentially other unpatched vulnerabilities. – ADyson Mar 08 '19 at 13:11
  • Switch to using `mysqli` or `PDO` as soon as possible, and then learn how to write parameterised queries to protect your data from malicious input. See http://bobby-tables.com for a simple explanation of the risks and some sample PHP code to write queries safely. If you found this code online perhaps you didn't realise it was out of date...but mysqli at least has been around since PHP5.0, which is well over a decade ago. – ADyson Mar 08 '19 at 13:12
  • Also, don't make your web app login to the database as root. Root can do whatever it likes, so on top of the SQL injection vulnerabilities this just leaves your database an open book for hackers. Instead create a separate user account specifically for this application which has only the permissions it actually _needs_ in order to work properly. Don't even use the root account as a shortcut during development or testing, because you need to test your account permissions as well - otherwise when you go live you might have unexpected errors relating to the user account setup. – ADyson Mar 08 '19 at 13:12
  • Actually I just noticed that you are using `mysqli_connect()` in db_connect() and `mysql_**` functions elsewhere. Again you may not have realised but [mysqli_](http://php.net/manual/en/intro.mysql.php) and [mysql_](http://php.net/manual/en/intro.mysqli.php) are different code libraries. You can't switch between them arbitrarily in your code. `mysql_` is dead as I mentioned so you should switch to using `mysqli_` throughout. That might help with your errors - even if you've established a connection to the DB server, that connection is being done via mysqli, so the mysql_ functions can't use it. – ADyson Mar 08 '19 at 13:27
  • In summary, I don't think your problem has anything to do with the use of the .ini file. You can always prove it by putting back the hard-coded values, and I predict you'll still have the same error. – ADyson Mar 08 '19 at 13:32

2 Answers2

-1

This is because mysql_real_escape_string takes into account the current character set of the connection. As such, it needs a connection. :-)

Please try to connect database using below code and please let me know if you have any problem.

$servername = "localhost";
$username = "root";
$password = "";
$dbname = "test";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
Hiren Spaculus
  • 733
  • 5
  • 6
  • There's nothing wrong with the way OP is connecting to the DB, by the looks of it. Your code doesn't improve that. But you seem not to have noticed that OP is using `mysqli_*` functions to connect, yet uses `mysql_*` functions to run queries...so it's no wonder the mysql_ functions don't know about the connection details. Your answer itself is still conflating the two libraries. They can't be mixed together like this. `mysqli_` should be used throughout. – ADyson Mar 08 '19 at 13:30
-2

Scrubbing the strings that come from userland isn't sufficient to prevent SQL injection attacks. The standard way is to use a prepared query. E.g.:

    $stmt = mysqli_stmt_init($this);
    $result = array();
    $okay = mysqli_stmt_prepare($stmt, "SELECT * FROM details WHERE (title LIKE ?) OR (text LIKE ?)");
    if ($okay)
        $okay = mysqli_stmt_bind_param($stmt, "ss", "%$query%", "%$text%")
    else
        [handle error]
    if ($okay)
        $okay = mysqli_stmt_execute($stmt);
    else
        [handle error]
    if ($okay)
        $okay = mysqli_bind_result($stmt, $row);
    else
        [handle error]
    if ($okay)
        while (mysqli_stmt_fetch($stmt))
            array_push($result, $row);
    else
        [handle error]
    mysqli_stmt_close($stmt);

Walter Oney
  • 163
  • 5
  • This is perfectly true and good advice in general, but it's not actually a solution to the OP's question or error messages. It doesn't answer the questions posed. – ADyson Mar 08 '19 at 13:28
  • Understand. However, the first reply to this post pointed out the vulnerability, and the OP expressed lack of knowledge. A complete answer to his post should address that concern. – Walter Oney Mar 08 '19 at 20:18
  • Agreed also. But my point was that you posted this in the Answers section but it's not an answer to the question... – ADyson Mar 08 '19 at 21:04