Uitwerking OWASP 1 - SQL Injection
Veilig programmeren
Veilig programmeren is een belangrijk onderdeel van Software Development, zie afbeelding:
SQL Injection
In deze les gaan we een login form maken met minimale functionaliteit. We gaan kennis maken met SQL Injection en gaan op zoek naar manieren om dit te voorkomen. Let's code!
Benodigdheden voor deze les
XAMPPSublime TextPhpMyAdmin DBBrowser
Step-by-step implementatie login form met SQL injectie
Tijdens deze les gaan we een database opzetten in PhpMyAdmin. Om dit te kunnen doen gaan we eerst XAMPP [1] downloaden zodat we php kunnen gebruiken, want we hebben e
Voor deze opdracht gaan we HTML, PHP en MySQL gebruiken. Om deze tools te gebruiken, hebben we een server nodig. Daarom beginnen we met het downloaden van XAMPP [1]. Als je XAMPP eenmaal hebt gedownload, open je XAMPP en start je de Apache - en SQL server. Nu kunnen we beginnen met het opzetten van onze database in PhpMyAdmin.
PhpMyAdmin
Open je browser en navigeer naar http://127.0.0.1/phpmyadmin/ . Maak een nieuwe database aan met daarin een tabel user. Dit tabel heeft 3 kolommen, namelijk:
idusernamepassword
Insert op minstens 2 entries in tabel user.
Code
Nu onze servers eenmaal draaien, kunnen we aan de slag! Open de xampp-folder in je document browser en open de folder htdocs. Maak een nieuwe folder genaamd veilig programmeren SQL injecties aan in folder htdocs.
Open de Sublime Text editor en maak de onderstaande drie files aan en sla deze op in folder veilig programmeren SQL injeties:
index.htmllogin.phpdb.php
De onderstaande secties leggen uit wat er wordt verwacht van je code.
index.html
De index.html file is, zoals de extensie van de file al aangeeft, een HTML document. Zorg ervoor dat dit document de HTML-skeleton bevat. De body tag van dit document moet een form bevatten, bestaande uit twee input-fields en een button.
Het form moet gebruik maken van de login.php file.
Let op! Zorg ervoor dat het wachtwoord veld het ingevoerde wachtwoord niet toont!
Als je de HTML-skeleton en form af hebt, zou je deze moeten kunnen zien in je browser. Dat doe je zo:
Open je index.html fileClick met je rechter muisknop in de fileSelecteer "Open in browser"
Merk op dat de zoekbalk een file-path toont. Klik in je zoekbalk en vervang het stukje file:///C:/xampp/htdocs met localhost. Deze stap is nodig om PHP te kunnen gebruiken.
login.php
De login file is een php document. Hieronder is een code-snippet toegevoegd. Neem deze over in je eigen login.php-file en werk de todo's uit:
<?php
include "db.php";
// todo 1: print de ingevoerde username en wachtwoord op aparte regels. Geef duidelijk aan welk van de twee de username en het wachtwoord is.
// todo 2: maak twee variabele aan en sla de ingevoerde username en wachtwoord op in deze variabelen.
$myConn = new DB;
// todo 3: include de variabele met de username in de onderstaande query, zodat deze alle data kan ophalen voor de ingevoerde username.
$query = "SELECT * FROM user WHERE ";
$result = $myConn->executeSQL($query);
// todo 4: vermeldt wat de datatype van variabele $result is. Dit kun je met behulp van een ingebouwde php functie doen.
if (!empty($result)) {
echo "<br> Login as $username <br>";
// todo 5: let uit wat de ingebouwde php functie print_r() doet en gebruik het om de result-variabele te printen.
} else {
echo "<br> Invalid login! <br>";
}
?>
db.php
De db.php file bevat een database (DB) class. Deze zorgt voor een database connectie. Neem de onderstaande code-snippet over in je db.php file en werk de todo's uit.
<?php
// Define DB Params
// todo 1: zoek uit wat de host, user, password van je database en vul ze hieronder in om de connectie te kunnen maken met je db
define("DB_HOST", "");
define("DB_USER", "");
define("DB_PASS", "");
// todo 2: vul de naam van de database in op de plek van de empty string.
define("DB_NAME", "");
class DB{
protected $dbh;
protected $stmt;
protected $resultSet;
public function __construct(){
$this->dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS);
$this->resultSet = [];
}
public function executeSQL($query){
$this->stmt = $this->dbh->prepare($query);
$result = $this->stmt->execute();
if (!$result) {
die('<pre>Oops, Error execute query '. $query .'</pre><br><pre>'.'Result code: '. $result .'</pre>');
}
$row = $this->stmt->fetchAll();
if(!empty($row)){
$this->resultSet = $row;
return $this->resultSet;
}
return $this->resultSet;
}
}
?>
Uitwerkingen
Opdracht 1: Wat is SQL?
SQL staat voor Structured Query Language en is een data manipulatie taal.
Opdracht 2: Maak de code snippets compleet door de todo's uit te werken.
index.php
<html>
<head>
<title>My pretty page</title>
</head>
<body>
<form action="login.php" method="post">
<input type="text" name="username" >
<input type="password" name="password" >
<input type="submit" name="submit" value="Login">
</form>
</body>
</html>
login.php
Let op! Hieronder zie je de content van login.php. De todo's hierin zijn al uitgewerkt.
<?php
include "db.php";
// todo 1: print de ingevoerde username en wachtwoord op aparte regels. Geef duidelijk aan welk van de twee de username en het wachtwoord is.
echo 'username"User: is ' ". $_GET[_POST['username']."<br>";
echo 'password"Wachtwoord: is ' ". $_GET[_POST['password']."<br>";
// todo 2: maakMaak twee variabele aan en sla de ingevoerde username en wachtwoord op in deze variabelen.
$username = username=$_GET[_POST['username'];
$password = password=$_GET[_POST['password'];
$myConn = new DB;
// todo 3: include de variabele met de username in de onderstaande query, zodat deze alle data kan ophalen voor de ingevoerde username.
$query = "SELECT * FROM user WHERE username='$username"username'";
$result = $myConn->executeSQL($query);
// todo 4:3: vermeldt wat de datatype van variabele $result is. Dit kun je met behulp van een ingebouwde php functie doen.
echo "Datatypedatatype vanof variabelethe result variable is " . gettype($result);
if (!empty($result)) {
echo "<br> Login as $username <br>";
// todo 5:4: let uit wat de ingebouwde php functie print_r() doet en gebruik het om de result-variabele te printen.
// onderstaande regel print de raw array
print_r($result);
} else {
echo "<br> Invalid login! <br>";
}
?>
db.php
De db.php file bevat een database (DB) class. Deze zorgt voor een database connectie. Neem de onderstaande code-snippet over in je db.php file en werk de todo's uit.
<?php
// Define DB Params
// todo 1: zoek uit wat de host, user, password van je database en vul ze hieronder in om de connectie te kunnen maken met je db
define("DB_HOST", "");
define("DB_USER", "");
define("DB_PASS", "");
// todo 2: vul de naam van de database in op de plek van de empty string.
define("DB_NAME", "");
class DB{
protected $dbh;
protected $stmt;
protected $resultSet;
public function __construct(){
$this->dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS);
$this->resultSet = [];
}
public function executeSQL($query){
$this->stmt = $this->dbh->prepare($query);
$result = $this->stmt->execute();
if (!$result) {
die('<pre>Oops, Error execute query '. $query .'</pre><br><pre>'.'Result code: '. $result .'</pre>');
}
$row = $this->stmt->fetchAll();
if(!empty($row)){
$this->resultSet = $row;
return $this->resultSet;
}
return $this->resultSet;
}
}
?>
db.php
<?php
// Define DB Params
// todo 1: zoek uit wat de host, user, password van je database en vul ze hieronder in om de connectie te kunnen maken met je db
define("DB_HOST", "localhost");
define("DB_USER", "root");
define("DB_PASS", "");
// todo 2: vul de naam van de database in op de plek van de empty string.
define("DB_NAME", "veilig_programmeren");
class DB{
protected $dbh;
protected $stmt;
protected $resultSet;
public function __construct(){
$this->dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS);
$this->resultSet = [];
}
public function executeSQL($query){
$this->stmt = $this->dbh->prepare($query);
$result = $this->stmt->execute();
if (!$result) {
die('<pre>Oops, Error execute query '. $query .'</pre><br><pre>'.'Result code: '. $result .'</pre>');
}
$row = $this->stmt->fetchAll();
if(!empty($row)){
$this->resultSet = $row;
return $this->resultSet;
}
return $this->resultSet;
}
}
?>
Opdracht 3: Als je ervoor hebt kunnen zorgen dat de juiste data print, is het zover! Voer de volgende regel in voor de username, en leg uit wat je te zien krijgt: ' OR '1'='1
.
Wanneer de bovenstaande SQL snippet wordt ingevoerd, krijg ik de volledige user-tabel (met data) te zien.
Opdracht 4: Wat is de naam van de "techniek" die je in opdracht 3 hebt toegepast?
SQL Injection (NL: SQL injectie).
Opdracht 5: Hoe zou je ervoor kunnen zorgen dat de regel uit opdracht 3 niet meer kan gebeuren?
Er zijn meerdere manieren om SQL injectie te voorkomen:
- Deze stap voorkomt SQL injecties niet perse, maar het is belangrijk om field validation toe te passen voor een input field. Zo "forceer" je de gebruiker om een waarde voor bijvoorbeeld gebruikersnaam/wachtwoord in te vullen.
- Je zou Regular Expressions (Regex) kunnen gebruiken om bepaalde karakters buiten te sluiten. Zo zou je ervoor kunnen zorgen dat een bepaalde input alleen letters en numerieke waarden kan bevatten.
- Wat je ook zou kunnen doen is het vervangen van tekens. Zo zou je ervoor kunnen zorgen dat SQL statements niet uitgevoerd kunnen worden.