[SOLVED] Proyek Pengolahan Database PHP MySQL

Hai devilzc0der’s :metal:

Gini gue mau nanya, gue mau buat sistem pengolahan data untuk nyimpen data akun atau nama kerennya account manager. Jadi disini gue mau nanya masalah keamanan khususnya gimana caranya supaya proyek yang gue buat bisa aman dari serangan SQL Injection dan XSS Attack. Gue yakin para mastah disini udah banyak yang tau arti kedua serangan website tersebut.

Oke deh, gw kasih liat scriptnya. Jadi ada 8 file, diantaranya:

  1. index.php // halaman utama

  2. form_input.php // halaman input data

  3. form_update.php // halaman update data

  4. form_delete.php // halaman menghapus data

  5. process_form_input.php // script untuk input data

  6. process_form_update.php // script untuk update data

  7. process_form_delete.php // script untuk menghapus data

  8. show_data.php // script untuk melihat data

  9. index.php
    Code:

<html>
<body>
<p>Menu</p>
<ul>
<li><a href="form_input.php">Form Input</a></li>
<li><a href="form_update.php">Form Update</a></li>
<li><a href="form_delete.php">Form Delete</a></li>
<li><a href="show_data.php">Show Data</a></li>
</ul>
</body>
</html>
  1. form_input.php
    Code:
<head>
<h1>Form Input</h1>
</head>
<body>
<p><a href='index.php'>Kembali ke Halaman Utama</a></p>
<form action="process_form_input.php" method="post">
<input type="text" name="email" placeholder="email">
<input type="password" name="password" placeholder="password">
<input type="submit" name="submit">
</form>
</body>
</html>
  1. form_update.php
    Code:
    <html>
    <head>
    <h1>Form Update</h1>
    </head>
    <body>
    <p><a href='index.php'>Kembali ke Halaman Utama</a></p>
    <form action="process_form_update.php" method="post">
    <input type="text" name="id" placeholder="id">
    <input type="text" name="email" placeholder="email">
    <input type="password" name="password" placeholder="password">
    <input type="submit" name="submit">
    </form>
    </body>
    </html>
  1. form_delete.php
    Code:
    <!DOCTYPE html>
    <html>
    <head>
    <h1>Form Delete</h1>
    </head>
    <body>
    <p><a href='index.php'>Kembali ke Halaman Utama</a></p>
    <form action="process_form_delete.php" method="post">
    <input type="text" name="id" placeholder="id">
    <input type="submit" name="submit">
    </form>
    </body>
    </html>
  1. process_form_input.php
    Code:
    <?php

    echo "<p><a href='form_input.php'>Kembali ke Form Input</a></p>";

    $link = mysqli_connect("localhost", "root", " ", "mydatabase");

    if($link === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
    }

    $email = mysqli_real_escape_string($link, $_REQUEST['email']);
    $email = htmlspecialchars($email, ENT_COMPAT, 'ISO-8859-1', true);
    $password = mysqli_real_escape_string($link, $_REQUEST['password']);
    $password = htmlspecialchars($password, ENT_COMPAT, 'ISO-8859-1', true);

    $sql = "INSERT INTO mytbl (email, password) values ('$email','$password')";

    if(mysqli_query($link, $sql)){
    echo "<p>Penyimpanan berhasil.</p>";
    } else{
    echo "Penyimpanan tidak berhasil $sql.";
    }

    mysqli_close($link);
    ?>
  1. process_form_update.php
    Code:
    <?php

    echo "<p><a href='form_update.php'>Kembali ke Form Update</a></p>";

    $link = mysqli_connect("localhost", "root", " ", "mydatabase");

    if($link === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
    }

    $id = mysqli_real_escape_string($link, $_REQUEST['id']);
    $id = htmlspecialchars($id, ENT_COMPAT, 'ISO-8859-1', true);
    $email = mysqli_real_escape_string($link, $_REQUEST['email']);
    $email = htmlspecialchars($email, ENT_COMPAT, 'ISO-8859-1', true);
    $password = mysqli_real_escape_string($link, $_REQUEST['password']);
    $password = htmlspecialchars($password, ENT_COMPAT, 'ISO-8859-1', true);

    $sql = "UPDATE mytbl ". "SET email = '$email', password = '$password' ". "WHERE id = $id";

    if(mysqli_query($link, $sql)){
    echo "<p>Penyimpanan berhasil.</p>";
    } else{
    echo "Penyimpanan tidak berhasil.";
    //echo "Penyimpanan tidak berhasil . $sql.";
    }

    mysqli_close($link);
    ?>
  1. process_form_delete.php
    Code:
    <?php

    echo "<p><a href='form_delete.php'>Kembali ke Form Delete</a></p>";

    $link = mysqli_connect("localhost", "root", " ", "mydatabase");

    if($link === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
    }

    $id = mysqli_real_escape_string($link, $_REQUEST['id']);
    $id = htmlspecialchars($id, ENT_COMPAT, 'ISO-8859-1', true);

    $sql = "DELETE FROM mytbl WHERE id='$id'";

    if ($link->query($sql) === TRUE) {
    echo "<p>Data telah dihapus.</p>";
    } else {
    echo "Gagal Menghapus Data: " . $link->error;
    }

    $link->close();
    ?>
  1. show_data.php
    Code:
    <table>
    <?php

    echo "<p><a href='index.php'>Kembali ke Halaman Utama</a></p>";

    $link = mysqli_connect("localhost", "root", " ", "mydatabase");

    if($link === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
    }

    $query = "SELECT * from mytbl";
    echo "<h3>Isi Data</h3>";

    if ($link->connect_error) {
    die("Connection failed: " . $link->connect_error);
    }

    $sql = "SELECT id, email, password FROM mytbl";
    $result = mysqli_query($link, $sql);

    if (mysqli_num_rows($result) > 0) {
    // output data of each row
    while($row = mysqli_fetch_assoc($result)) {
    echo "<tr><td>" . $row["id"]. "</td><td>" . $row["email"] . "</td><td>"
    . $row["password"]. "</td></tr>";
    }
    echo "</table>";
    } else { echo "0 results"; }

    mysqli_close($link);
    ?>

    </table>

Mohon dikoreksi om kodenya kalo ada yang salah. Gue udah coba nutup beberapa bug xss dan sql injection pada ketiga file process. Untuk demo, gue buat di situs ini http://webtestingsrv01.gear.host/home/index.php

Mohon bantuannya dan masukkannya kalo masih ada bug.

Terima kasih banyak :mohon:

berantakan om posting markdown nya. :hammer:

  1. Pake MySQL server yang up 2 date
  2. Pake PHP yg up 2 date (>7.3)
  3. Pake PDO
  4. Database charsetnya pake utf8 atau utf8mb4

Buat file php isinya kinek ke sql buat diinclude di setiap page yg ada query sqlnya, taruh kata global.php
global.php

<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'root');
define('DB_USER', 'mydatabase');
define('DB_PASS', '');

$db = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER , DB_PASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAME'utf8mb4'");
?>

process_form_input.php

<?php
require('global.php');

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$query = $db->prepare("INSERT INTO mytbl (email, password) VALUES (?, ?)");
$result = $query->execute(array(
    $_POST['email'],
    $_POST['password']
));

if ($result) {
    echo "<p>Penyimpanan berhasil.</p>";
    exit;
}
echo "Penyimpanan tidak berhasil.";
exit;
?>

process_form_update.php

<?php
require('global.php');
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$query = $db->prepare("UPDATE mytbl SET email = ?, password = ? WHERE id = ? LIMIT 1");
$result = $query->execute(array(
    $_POST['email'],
    $_POST['password'],
    (int)$_POST['id']
));

if ($result) {
    echo "<p>Penyimpanan berhasil.</p>";
    exit;
}
echo "Penyimpanan tidak berhasil.";
exit;
?>

process_form_delete.php

<?php
require('global.php');
$query = $db->prepare("DELETE FROM mytbl WHERE id = ? LIMIT 1");
$result = $query->execute(array(
    (int)$_POST['id']
));
?>

show_data.php

<?php
require('global.php');

function html($text)
{
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}

$query = $db->query("SELECT * FROM mytbl");
$result = $query->fetchAll(PDO::FETCH_ASSOC);
if (count($result) != '0') {
    echo '<table>';
    foreach ($result as $key=>$val) {
        ?>
        <tr>
            <td><?php echo (int)$val['id']; ?></td>
            <td><?php echo html($val['email']); ?></td>
            <td><?php echo html($val['password']); ?></td>
        </tr>
        <?php
    }
    echo '</table>';
}
?>

Itu simplenya gitu. Gue ga sempet test error apa kaga, tp kurang lebih gitu.
Dan itu ga ada yg jamin 100% safe sql injection ya, apa lagi versi db & phpnya versi lama.

  • :bulb:: Gue ganti semua $_REQUEST ke $_POST karena form methodnya POST. klo request nanti jadi rancu, dia eksekusi semua method GET, POST dan COOKIE. https://stackoverflow.com/questions/1924939/among-request-get-and-post-which-one-is-the-fastest
  • Note: Liat kadang gue pake PDO prepare, kadang gue cuma pake PDO query. Salah 1 yang pake query itu yg di show_data.php karena disana ga ada user input. jd asal hajar tanpa prepare ga masalah. Yg masalah itu filter outputnya (XSSnya)

Note buat XSS, function html() di show_data.php yg gue buat diatas itu makin ga jamin lagi karena menurut gue XSS emang susah ditangkal. Tp paling engga sedikit menyusahkan script kid. =)

Referensi PDO: https://www.php.net/manual/en/book.pdo.php

Edit: Tambah saran, itu jangan lupa tambahin validasi buat tiap input POSTnya yang dibutuhkan. Klo ngga nti keluar error notice undefined.

1 Like

Makasih banyak atas masukkannya om. Kemaren mau pindah ke pdo tapi gak jadi karena gue gak begitu paham :mewek:
Trus alasan ane kemaren pake method request daripada post karena gagal input, update, sama deletenya :hammer:

Sebenernya plus-minus juga antara pdo sama mysqli. Klo urusan kecepatan, mysqli lebih cepet. Klo urusan support database, PDO menang. Klo gue lebih pilih PDO karena banyak digunain dan ga terlalu susah klo kedepannya mau pindah dari driver 1 ke driver lain.

Yg bingung dimana coba ditanyain aja. Btw itu usah bisa belum querynya?

querynya berjalan dengan mulus om dita :kalem:
ada beberapa hal yang mau ditanyain nih om

  1. Pada file show_data.php itu ada function kayak gini:
function html($text)
{
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}

itu berlaku pada saat menampilkan data dalam bentuk html atau bisa digunain untuk hal lainnya misalnya kalo gue masukin function tersebut di file process_form_input.php atau process_form_update.php?

  1. Pada file process_form_input.php, process_form_update.php, dan process_form_delete.php menggunakan array dibaris ini:
    process_form_input.php:
$result = $query->execute(array(
    $_POST['email'],
    $_POST['password']
));

Pertanyaannya itu apa beda fungsinya dengan script contoh dibawah ini yang gue ambil dari referensinya https://www.w3schools.com/php/php_mysql_insert.asp:

 <?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";

try {
  $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
  // set the PDO error mode to exception
  $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $sql = "INSERT INTO MyGuests (firstname, lastname, email)
  VALUES ('John', 'Doe', 'john@example.com')";
  // use exec() because no results are returned
  $conn->exec($sql);
  echo "New record created successfully";
} catch(PDOException $e) {
  echo $sql . "<br>" . $e->getMessage();
}

$conn = null;
?> 
  1. Fungsi dari perintah “LIMIT 1” pada ketiga process form input, update, dan delete itu apakah wajib untuk digunakan?
    Mohon penjelasannya om dita :mohon:

Bebas kok om itu mah. Tergantung situasi. Kadang data yang masuk di database gue filter, kadang gue biarin apa adanya. Yg jelas sanitasi cukup sekali aja. Gue biasanya filter kalau pas gue tampilin ke client. Yg ke database gue biarin masuk apa adanya untuk kepentingan troubleshoot.
Real kasusnya ky gini:
Gue ada website yg ambil data AS number via API dari situs bgpview. Salah 1 AS number di database mereka itu ASN descriptionnya ada XSS dan SQL injection.


Data itu ya gue masukkan ke database apa adanya, tanpa sanitasi / filter.

Baru pas gue tampilin ke website, gue filter biar ga keluar XSSnya.

Ini live urlnya => https://www.ditatompel.com/tools/asn-info/AS205610.

Jadi tergantung kebutuhan dan penggunaan.
Klo untuk database login misal create username, itu gue validasi, biasanya cuma alpha numeric. Klo ga alpha numeric ya gue tolak, ga masuk ke database alias ga create user. Jadi databasenya juga bersih.

Beda dong. PDO::exec() itu ga bisa dipake buat SELECT (Ini salah 1 alasan gue benci w3school, info disana ga pernah lengkap dan itu2 aja).

PDO::exec() does not return results from a SELECT statement. For a SELECT statement that you only need to issue once during your program, consider issuing PDO::query(). For a statement that you need to issue multiple times, prepare a PDOStatement object with PDO::prepare() and issue the statement with PDOStatement::execute().
Source : https://www.php.net/manual/en/pdo.exec.php

Dan yang gue pake itu PDO::prepare()

Also, calling PDO::prepare() and PDOStatement::execute() helps to prevent SQL injection attacks by eliminating the need to manually quote and escape the parameters.
Source: https://www.php.net/manual/en/pdo.prepare.php

Di PDO::prepare() yg gue buat itu kan pake tanda ? itu nanti diassign ke array PDO::execute(). Jumlah array harus sesuai dengan jumlah parameter ?.

Atau kalau bingung pake parameter ?, bisa pake name based param.
Misal:

$query = $db->prepare("UPDATE mytbl SET email = :imel, password = :paswort WHERE id = :aidi LIMIT 1");
$result = $query->execute(array(
    ':imel' => $_POST['email'],
    ':paswort' => $_POST['password'],
    ':aidi' => (int)$_POST['id']
));

Jadi dengan prepare trus execute, asal versi PHP SQL nya ga versi lama dan penggunaan charset bener, insya Allah aman dari SQL Injection (kecuali klo bugnya dari PHP itu sendiri, makannya ngikutin update dari core app yg kita pake itu penting).

Tergantung. Itu LIMIT 1 artinya cuma bisa update, select, atau insert maksimal 1 record doang. Karena basisnya id (yang seharusnya unique dan auto increment) penggunaan LIMIT 1 itu ga wajib dan ga diperluin. (kebiasaan gue doang sih itu pake LIMIT) :chaer: