2
\$\begingroup\$

I am very new to PHP coding, hence just wanted to know if my coding is prone to any SQL injection attack or any other kind of threat. Below is the code for adding client to the database:

<?php
$response = array();
include 'db/db_connect.php';
include 'functions.php';

//Get the input request parameters
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); //convert JSON into array

//Check for Mandatory parameters
if(isset($input['clientName']) && isset($input['siteName'])){
    $clientName = $input['clientName'];
  $siteName = $input['siteName'];
    //Check if client already exist
    if(!clientExists($clientName, $siteName)){
        //Query to add new material
        $insertQuery  = "INSERT INTO clients(clientName, siteName) VALUES (?,?)";
        if($stmt = $con->prepare($insertQuery)){
            $stmt->bind_param("ss",$clientName,$siteName);
            $stmt->execute();
            $response["status"] = 0;
            $response["message"] = "Client created";
            $stmt->close();
        }
    }
    else{
        $response["status"] = 1;
        $response["message"] = "Client exists";
    }
}
else{
    $response["status"] = 2;
    $response["message"] = "Missing mandatory parameters";
}
echo json_encode($response);
?>

Code for clientExists() function is as follows:

function clientExists($clientName,$siteName){
    $query = "SELECT clientName FROM clients WHERE clientName = ? AND siteName=?";
    global $con;
    if($stmt = $con->prepare($query)){
        $stmt->bind_param("ss",$clientName,$siteName);
        $stmt->execute();
        $stmt->store_result();
        $stmt->fetch();
        if($stmt->num_rows == 1){
            $stmt->close();
            return true;
        }
        $stmt->close();
    }
    return false;
}
\$\endgroup\$
5
  • \$\begingroup\$ What is your threat model? \$\endgroup\$
    – Mast
    Commented Jan 28, 2020 at 7:24
  • 1
    \$\begingroup\$ I did not get what you mean by threat model. I am using php to prepare web service for my android app. This app has some menus one of which is to create a client. I'll be grateful if you can elaborate a bit more on this... \$\endgroup\$ Commented Jan 28, 2020 at 7:40
  • \$\begingroup\$ "if my coding is prone to any SQL injection attack or any other kind of threat" What threats are you protecting against? Protecting a database is like locking up a house. A simple lock will deter some criminals but if they really want to, they will get in. The bigger the threat, the heavier the security. Hence, real security uses a threat model. If you're going to store client information of consumers, you'll have to deal with data protection laws as well. \$\endgroup\$
    – Mast
    Commented Jan 28, 2020 at 7:46
  • \$\begingroup\$ Input validation and prepared statements should, in theory, prevent SQL injection. So if you've done that properly that part is covered. But depending on your app there's also XSS, frame injection, redirection, session troubles, lack of encryption (or poorly executed), passing privileged information as clear text, etc. \$\endgroup\$
    – Mast
    Commented Jan 28, 2020 at 7:48
  • \$\begingroup\$ Thank you so much for the information. I will definitely study more about threat model and incorporate things which apply to my code. Once again thank you very much for your quick response... \$\endgroup\$ Commented Jan 28, 2020 at 8:00

2 Answers 2

3
\$\begingroup\$

SQL injection should not be a problem, as long as you pass any user input to queries through placeholders, which you do.

I have a few other remarks though

Single Responsibility Principle

In the first code snippet you do too much things. You decode request json, you talk to db. you handle the logic, you maintain response structure. It would be wise to separate those things into their own scopes (functions/classes). Of course if this is all you ever intend to have there may be no point. But in general, in future you will want to reuse the individual pieces.

Global Scope vs. Classes and Dependency Injection

You may want to avoid definind global variables. It's generaly considered bad practise for several reason which I'm not gonna repeat here as there is plenty of information sources for this already.

You are probably not very familiar with the concept of OOP, but wrapping those function into class scopes and working with objects may be something for you to learn.

If you want to stick with procedural style, maybe at least get rid of the globals by passing the db connection always as function parameter.

if (clientExists($connection, $clientName)) {...}

It is annoying but it is still better then globals, in some ways anyway. That where the objects could help if you get used to it.

$users->exists($clientName)

Data Validation

You check that $input['clientName'] is set. Is it enough? What if it is empty? What if it is array or number? Is it ok? What if $input is not an array in the first place? All that should be handled.

Validation is btw, yet another responsibility that might be separated to its own function.

Useless Statements

In the clientExists() function I think you fetch the row for now reason. It should be able to tell count without fetching it. You can also simplify the select from SELECT clientName to SELECT 1 which is, IMO, cleaner as you could actualy select any of the table's column for it to work and if that column ever changes name this would have to change too.

Public API

At this point your api is public in whichever network in which it is deployed. As this network is probably going to be the internet (I suppose), anyone will be able to call it. Is it ok if any anonymous client can succesfully call this endpoint and so create db entries? You may want to have some way of authentication, but this depends on your use-case.

\$\endgroup\$
0
0
\$\begingroup\$

in addition to what has already been said, I'd suggest not using clientExists at all. just have a UNIQUE constraint on clientName, siteName and let the database enforce this for you.

the reason is that your current code is both doing more work than is needed (the check for existence) as well not enough (the gap between the existence check and inserting a new row is a race condition). generally the best place to handle this sort of check is the database

see e.g. https://stackoverflow.com/q/3146838/1358308 for how to handle this with MySQL. I've not programmed PHP for a few years so don't know if there are any nice ways to handle this that generalise to other database engines.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.