I am requesting a review of a portion of a 600 lines of code. This portion of the code process a filter that is farther down the page. It takes in the parameters and formats them into a query. Now I have included two bits. First the code, and then the portion of my DB class that makes the call to the database.
Code:
if (Token::check(Input::get('token'))) {
$sql = "SELECT ads.id, ads.charName, ads.charId, ads.adTitle, ads.adDescription, ads.adStartingBid, ads.adCurrentBid FROM ads, characterSheet WHERE ads.charId = characterSheet.characterID AND ads.adPublished = 1";
$stmt = "";
if (Input::exists('race')) {
$race = array_keys(Input::get('race'));
if (count($race)==1) {
$stmt .= " AND characterSheet.race = '{$race[0]}'";
} else {
$x = 1;
$stmt .= " AND (";
foreach ($race as $y) {
$stmt .= "characterSheet.race = '".$y."'";
if ($x < count($race)) {
$stmt .= " OR ";
}
$x++;
}
$stmt .= ")";
}
}
if (Input::exists('sp')) {
$sp = explode(":", Input::get('sp'));
$spMultiplier = Config::get('filter/spMultiplier');
$lowSP = $sp[0] * $spMultiplier;
$highSP = $sp[1] * $spMultiplier;
$stmt .= " AND characterSheet.skillPoints BETWEEN {$lowSP} AND {$highSP}";
}
if (Input::exists('sBid')) {
$sBid = explode(":", Input::get('sBid'));
$sBidMultiplier = Config::get('filter/iskMultiplier');
$lowSBid = $sBid[0] * $sBidMultiplier;
$highSBid = $sBid[1] * $sBidMultiplier;
$stmt .= " AND ads.adStartingBid BETWEEN {$lowSBid} AND {$highSBid}";
}
$sql .= $stmt;
}
The Filter:
<h4 class="text-center"><strong>Filter</strong></h4>
<div class="panel-group" id="filter" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterByRace">
<h4 class="panel-title text-center">
<a role="button" data-toggle="collapse" data-parent="#filter" href="#filterByRaceBody" aria-expanded="true" aria-controls="filterByRaceBody">
Filter By Race
</a>
</h4>
</div>
<div id="filterByRaceBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterByRace">
<ul class="list-group">
<li class="list-group-item">
<label for="filterByRaceAmarr"><input type="checkbox" name="race[Amarr]" id="filterByRaceAmarr"/> Amarr</label>
</li>
<li class="list-group-item">
<label for="filterByRaceCaldari"><input type="checkbox" name="race[Caldari]" id="filterByRaceCaldari"/> Caldari</label>
</li>
<li class="list-group-item">
<label for="filterByRaceGallente"><input type="checkbox" name="race[Gallente]" id="filterByRaceGallente"/> Gallente</label>
</li>
<li class="list-group-item">
<label for="filterByRaceMinmatar"><input type="checkbox" name="race[Minmatar]" id="filterByRaceMinmatar"/> Minmatar</label>
</li>
</ul>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterBySP">
<h4 class="panel-title text-center">
<a role="button" data-toggle="collapse" data-parent="#filter" href="#filterBySPBody" aria-expanded="true" aria-controls="filterBySPBody">
Filter By Skill Points
</a>
</h4>
</div>
<div id="filterBySPBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterBySP">
<ul class="list-group">
<li class="list-group-item">
<label for="filterBySP1"><input type="radio" name="sp" value="0:1"/> >1M</label>
</li>
<li class="list-group-item">
<label for="filterBySP2"><input type="radio" name="sp" value="1:10"/> 1M - 10M</label>
</li>
<li class="list-group-item">
<label for="filterBySP3"><input type="radio" name="sp" value="10:50"/> 10M - 50M</label>
</li>
<li class="list-group-item">
<label for="filterBySP4"><input type="radio" name="sp" value="50:100"/> 50M - 100M</label>
</li>
<li class="list-group-item">
<label for="filterBySP5"><input type="radio" name="sp" value="100:999"/> <100M</label>
</li>
</ul>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterByISK">
<h4 class="panel-title text-center">
<a role="button" data-toggle="collapse" data-parent="#filter" href="#filterByISKBody" aria-expanded="true" aria-controls="filterByISKBody">
Filter By Starting Bid
</a>
</h4>
</div>
<div id="filterByISKBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterByISK">
<ul class="list-group">
<li class="list-group-item">
<label for="filterByISK1"><input type="radio" name="sBid" value="0:1"/> >1B</label>
</li>
<li class="list-group-item">
<label for="filterByISK2"><input type="radio" name="sBid" value="1:10"/> 1B - 10B</label>
</li>
<li class="list-group-item">
<label for="filterByISK3"><input type="radio" name="sBid" value="10:50"/> 10B - 50B</label>
</li>
<li class="list-group-item">
<label for="filterByISK4"><input type="radio" name="sBid" value="50:100"/> 50B - 100B</label>
</li>
<li class="list-group-item">
<label for="filterByISK5"><input type="radio" name="sBid" value="100:999"/> <100B</label>
</li>
</ul>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="filterBySkills">
<h4 class="panel-title text-center">
<a role="button" data-toggle="collapse" data-parent="#filter" href="#filterBySkillsBody" aria-expanded="true" aria-controls="filterBySkillsBody">
Filter By Skills
</a>
</h4>
</div>
<div id="filterBySkillsBody" class="panel-collapse collapse" role="tabpanel" aria-labelledby="filterBySkills">
<div class="panel-body">
<p>This filter need more room to be displayed than this sidebar can be offer. Please click on the button below to display the skills filter.</p>
<button class="btn btn-primary btn-block" type="button" data-toggle="collapse" data-target="#filterBySkillsHideAway" aria-expanded="false" aria-controls="filterBySkillsHideAway">
Display Skills Filter
</button>
</div>
</div>
</div>
</div>
<input type="hidden" name="token" value="<?=Token::generate();?>" />
<button type="submit" class="btn btn-primary btn-block">Filter Results</button>
</form>
</div>
Finally the Object of my DB Class that queries the DB:
public function query($sql, $params = array()) {
$this->error = false;
if ($this->query = $this->conn->prepare($sql)) {
$x = 1;
if ($params && count($params) > 0) {
foreach($params as $param) {
$this->query->bindValue($x, $param);
$x++;
}
}
if ($this->query->execute()) {
$breakItUp = explode(' ',$sql);
if ($breakItUp[0] === "SELECT") {
$this->results = $this->query->fetchAll(PDO::FETCH_OBJ);
}
if ($breakItUp[0] === "INSERT") {
$this->lastInsertId = $this->conn->lastInsertId();
}
$this->count = $this->query->rowCount();
} else {
$this->error = true;
}
}
return $this;
}
I am not requesting review on the DB Object or the HTML Form. Those are fine. I included them for background more than anything. My question is this:
Am I opening myself up to SQL Injection with the current way I am building the SQL? I am not sure since the all of the parameters are static values. The only problem I can see is if the user uses Chrome or Firefox to change the form values and then submits the form. That may be a way to inject some SQL, but I am not sure. I have no experience with SQL Injection nor how to thwart it.