Anda perlu membuat sistem mysql Anda aman dari penyerang, apa yang tidak seharusnya Anda lakukan

Artikel ini difokuskan untuk memberikan panduan yang jelas, sederhana, dan dapat ditindaklanjuti untuk mencegah kelemahan SQL Injection pada aplikasi Anda. Sayangnya, serangan SQL Injection sangat umum terjadi, dan ini disebabkan oleh dua faktor

  1. prevalensi yang signifikan dari kerentanan SQL Injection, dan
  2. daya tarik target (i. e. , database biasanya berisi semua data menarik/penting untuk aplikasi Anda)

Cacat SQL Injection diperkenalkan ketika pengembang perangkat lunak membuat kueri basis data dinamis yang dibangun dengan rangkaian string yang menyertakan input yang disediakan pengguna. Untuk menghindari kelemahan injeksi SQL itu sederhana. Pengembang juga perlu. a) berhenti menulis kueri dinamis dengan penggabungan string;

Artikel ini menyediakan sekumpulan teknik sederhana untuk mencegah kerentanan SQL Injection dengan menghindari dua masalah ini. Teknik-teknik ini dapat digunakan dengan hampir semua jenis bahasa pemrograman dengan semua jenis database. Ada jenis database lain, seperti database XML, yang dapat memiliki masalah serupa (mis. g. , XPath dan injeksi XQuery) dan teknik ini juga dapat digunakan untuk melindunginya

Pertahanan Primer

  • Pilihan 1. Penggunaan Pernyataan yang Disiapkan (dengan Kueri Parameter)
  • pilihan 2. Penggunaan Prosedur Tersimpan yang Dibangun dengan Benar
  • Opsi 3. Validasi Input daftar yang diizinkan
  • Opsi 4. Keluar dari Semua Input yang Disediakan Pengguna

Pertahanan Tambahan

  • Juga. Menegakkan Hak Istimewa Terkecil
  • Juga. Melakukan Validasi Input Daftar Izin sebagai Pertahanan Sekunder

Contoh Tidak Aman

Cacat injeksi SQL biasanya terlihat seperti ini

Contoh (Java) berikut adalah UNSAFE, dan akan memungkinkan penyerang menyuntikkan kode ke dalam kueri yang akan dijalankan oleh database. Parameter "customerName" yang tidak divalidasi yang hanya ditambahkan ke kueri memungkinkan penyerang memasukkan kode SQL apa pun yang mereka inginkan. Sayangnya, metode untuk mengakses database ini terlalu umum

String query = "SELECT account_balance FROM user_data WHERE user_name = "
             + request.getParameter("customerName");
try {
    Statement statement = connection.createStatement( ... );
    ResultSet results = statement.executeQuery( query );
}
...

Pertahanan Primer

Opsi Pertahanan 1. Pernyataan yang Disiapkan (dengan Kueri Parameter)

Penggunaan pernyataan yang disiapkan dengan pengikatan variabel (alias kueri berparameter) adalah bagaimana semua pengembang pertama-tama harus diajari cara menulis kueri basis data. Mereka sederhana untuk ditulis, dan lebih mudah dipahami daripada kueri dinamis. Kueri berparameter memaksa pengembang untuk terlebih dahulu menentukan semua kode SQL, lalu meneruskan setiap parameter ke kueri nanti. Gaya pengkodean ini memungkinkan database untuk membedakan antara kode dan data, terlepas dari masukan pengguna apa yang diberikan

Pernyataan yang disiapkan memastikan bahwa penyerang tidak dapat mengubah maksud kueri, bahkan jika perintah SQL dimasukkan oleh penyerang. Dalam contoh aman di bawah ini, jika penyerang memasukkan ID pengguna

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
8, kueri berparameter tidak akan rentan dan akan mencari nama pengguna yang benar-benar cocok dengan seluruh string
// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
8

Rekomendasi khusus bahasa

  • Java EE – gunakan
    String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
    try {
      OleDbCommand command = new OleDbCommand(query, connection);
      command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
      OleDbDataReader reader = command.ExecuteReader();
      // …
    } catch (OleDbException se) {
      // error handling
    }
    
    _0 dengan variabel ikat
  • NET – gunakan kueri berparameter seperti
    String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
    try {
      OleDbCommand command = new OleDbCommand(query, connection);
      command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
      OleDbDataReader reader = command.ExecuteReader();
      // …
    } catch (OleDbException se) {
      // error handling
    }
    
    1 atau
    String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
    try {
      OleDbCommand command = new OleDbCommand(query, connection);
      command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
      OleDbDataReader reader = command.ExecuteReader();
      // …
    } catch (OleDbException se) {
      // error handling
    }
    
    2 dengan variabel ikat
  • PHP – gunakan PDO dengan kueri berparameter yang diketik dengan kuat (menggunakan bindParam())
  • Hibernasi - gunakan
    String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
    try {
      OleDbCommand command = new OleDbCommand(query, connection);
      command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
      OleDbDataReader reader = command.ExecuteReader();
      // …
    } catch (OleDbException se) {
      // error handling
    }
    
    _3 dengan variabel ikat (disebut parameter bernama di Hibernasi)
  • SQLite - gunakan
    String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
    try {
      OleDbCommand command = new OleDbCommand(query, connection);
      command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
      OleDbDataReader reader = command.ExecuteReader();
      // …
    } catch (OleDbException se) {
      // error handling
    }
    
    _4 untuk membuat objek pernyataan

Dalam keadaan yang jarang terjadi, pernyataan yang disiapkan dapat merusak kinerja. Ketika dihadapkan dengan situasi ini, yang terbaik adalah a) memvalidasi semua data dengan kuat atau b) melarikan diri dari semua input yang disediakan pengguna menggunakan rutinitas pelolosan khusus untuk vendor database Anda seperti yang dijelaskan di bawah ini, daripada menggunakan pernyataan yang disiapkan

Contoh Pernyataan yang Disiapkan Java Aman

Contoh kode berikut menggunakan

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}
_5, implementasi Java dari kueri berparameter, untuk mengeksekusi kueri basis data yang sama

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_

C# aman. Contoh Pernyataan Disiapkan NET

Dengan. NET, bahkan lebih mudah. Pembuatan dan eksekusi kueri tidak berubah. Yang harus Anda lakukan hanyalah meneruskan parameter ke kueri menggunakan panggilan

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}
6 seperti yang ditunjukkan di sini

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}

Kami telah menunjukkan contoh di Jawa dan. NET tetapi hampir semua bahasa lain, termasuk Cold Fusion, dan Classic ASP, mendukung antarmuka kueri berparameter. Bahkan lapisan abstraksi SQL, seperti Hibernate Query Language (HQL) memiliki jenis masalah injeksi yang sama (yang kami sebut Injeksi HQL). HQL juga mendukung kueri berparameter, sehingga kami dapat menghindari masalah ini

Hibernate Query Language (HQL) Disiapkan Pernyataan (Parameter Bernama) Contoh

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);

Untuk contoh kueri berparameter dalam bahasa lain, termasuk Ruby, PHP, Cold Fusion, dan Perl, lihat Cheat Sheet Parameterisasi Kueri atau situs ini

Pengembang cenderung menyukai pendekatan Pernyataan Disiapkan karena semua kode SQL tetap berada di dalam aplikasi. Ini membuat aplikasi Anda relatif independen dari basis data

Opsi Pertahanan 2. Prosedur Tersimpan

Prosedur tersimpan tidak selalu aman dari injeksi SQL. Namun, konstruksi pemrograman prosedur tersimpan standar tertentu memiliki efek yang sama seperti penggunaan kueri berparameter saat diterapkan dengan aman yang merupakan norma untuk sebagian besar bahasa prosedur tersimpan

Mereka membutuhkan pengembang untuk hanya membangun pernyataan SQL dengan parameter yang secara otomatis berparameter kecuali pengembang melakukan sesuatu yang sebagian besar di luar norma. Perbedaan antara pernyataan yang disiapkan dan prosedur tersimpan adalah bahwa kode SQL untuk prosedur tersimpan ditentukan dan disimpan dalam database itu sendiri, dan kemudian dipanggil dari aplikasi. Kedua teknik ini memiliki keefektifan yang sama dalam mencegah injeksi SQL sehingga organisasi Anda harus memilih pendekatan mana yang paling masuk akal bagi Anda

Catatan. "Diimplementasikan dengan aman" berarti prosedur tersimpan tidak menyertakan pembuatan SQL dinamis yang tidak aman. Pengembang biasanya tidak menghasilkan SQL dinamis di dalam prosedur tersimpan. Namun, itu bisa dilakukan, tetapi harus dihindari. Jika tidak dapat dihindari, prosedur tersimpan harus menggunakan validasi input atau pelolosan yang tepat seperti yang dijelaskan dalam artikel ini untuk memastikan bahwa semua input yang diberikan pengguna ke prosedur tersimpan tidak dapat digunakan untuk menyuntikkan kode SQL ke dalam kueri yang dibuat secara dinamis. Auditor harus selalu mencari penggunaan

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}
7,
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}
8 atau
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}
9 dalam prosedur tersimpan SQL Server. Pedoman audit serupa diperlukan untuk fungsi serupa untuk vendor lain

Ada juga beberapa kasus di mana prosedur tersimpan dapat meningkatkan risiko. Misalnya, di server MS SQL, Anda memiliki 3 peran default utama.

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
0,
//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
1 dan
//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
2. Sebelum prosedur tersimpan mulai digunakan, DBA akan memberikan hak db_datareader atau db_datawriter kepada pengguna layanan web, tergantung pada persyaratan. Namun, prosedur tersimpan memerlukan hak eksekusi, peran yang tidak tersedia secara default. Beberapa pengaturan di mana manajemen pengguna terpusat, tetapi terbatas pada 3 peran tersebut, menyebabkan semua aplikasi web berjalan di bawah hak db_owner sehingga prosedur tersimpan dapat berfungsi. Secara alami, itu berarti bahwa jika server dibobol, penyerang memiliki hak penuh atas database, di mana sebelumnya mereka mungkin hanya memiliki akses baca.

Contoh Prosedur Tersimpan Java yang Aman

Contoh kode berikut menggunakan

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
_3, implementasi Java dari interface stored procedure, untuk mengeksekusi query database yang sama. Prosedur tersimpan
//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
_4 harus ditentukan sebelumnya dalam database dan menerapkan fungsi yang sama seperti kueri yang ditentukan di atas

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}

VB aman. Contoh Prosedur Tersimpan .NET

Contoh kode berikut menggunakan

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
5,. Implementasi NET dari interface stored procedure, untuk mengeksekusi query database yang sama. Prosedur tersimpan
//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
_4 harus ditentukan sebelumnya dalam database dan menerapkan fungsi yang sama seperti kueri yang ditentukan di atas

 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try

Opsi Pertahanan 3. Validasi Input daftar yang diizinkan

Berbagai bagian kueri SQL bukan merupakan lokasi yang sah untuk penggunaan variabel ikat, seperti nama tabel atau kolom, dan indikator tata urutan (ASC atau DESC). Dalam situasi seperti itu, validasi input atau desain ulang kueri adalah pertahanan yang paling tepat. Untuk nama tabel atau kolom, idealnya nilai tersebut berasal dari kode, dan bukan dari parameter pengguna

Namun jika nilai parameter pengguna digunakan untuk menargetkan nama tabel dan nama kolom yang berbeda, maka nilai parameter harus dipetakan ke nama tabel atau kolom yang sah/diharapkan untuk memastikan input pengguna yang tidak divalidasi tidak berakhir dalam kueri. Harap dicatat, ini adalah gejala desain yang buruk dan penulisan ulang penuh harus dipertimbangkan jika waktu memungkinkan

Berikut adalah contoh validasi nama tabel

String tableName;
switch(PARAM):
  case "Value1": tableName = "fooTable";
                 break;
  case "Value2": tableName = "barTable";
                 break;
  ...
  default      : throw new InputValidationException("unexpected value provided"
                                                  + " for table name");

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
_7 kemudian dapat langsung ditambahkan ke kueri SQL karena sekarang dikenal sebagai salah satu nilai legal dan nilai yang diharapkan untuk nama tabel dalam kueri ini. Perlu diingat bahwa fungsi validasi tabel umum dapat mengakibatkan hilangnya data karena nama tabel digunakan dalam kueri yang tidak diharapkan

Untuk sesuatu yang sederhana seperti tata urutan, akan lebih baik jika masukan yang diberikan pengguna diubah menjadi boolean, lalu boolean tersebut digunakan untuk memilih nilai aman untuk ditambahkan ke kueri. Ini adalah kebutuhan yang sangat standar dalam pembuatan kueri dinamis

Misalnya

public String someMethod(boolean sortOrder) {
 String SQLquery = "some SQL ... order by Salary " + (sortOrder ? "ASC" : "DESC");`
 ...

Setiap kali input pengguna dapat dikonversi ke non-String, seperti tanggal, numerik, boolean, jenis enumerasi, dll. sebelum ditambahkan ke kueri, atau digunakan untuk memilih nilai yang akan ditambahkan ke kueri, ini memastikan aman untuk melakukannya

Validasi input juga direkomendasikan sebagai pertahanan sekunder dalam SEMUA kasus, bahkan saat menggunakan variabel bind seperti yang akan dibahas nanti di artikel ini. Lebih banyak teknik tentang cara menerapkan validasi input yang kuat dijelaskan dalam Lembar Curang Validasi Input

Opsi Pertahanan 4. Keluar dari Semua Input yang Disediakan Pengguna

Teknik ini sebaiknya hanya digunakan sebagai upaya terakhir, jika tidak ada cara di atas yang memungkinkan. Validasi input mungkin merupakan pilihan yang lebih baik karena metodologi ini lemah dibandingkan dengan pertahanan lain dan kami tidak dapat menjamin ini akan mencegah semua Injeksi SQL dalam semua situasi

Teknik ini untuk menghindari input pengguna sebelum memasukkannya ke dalam kueri. Ini sangat spesifik basis data dalam implementasinya. Biasanya hanya disarankan untuk melakukan retrofit kode lawas saat menerapkan validasi masukan tidak efektif biaya. Aplikasi yang dibuat dari awal, atau aplikasi yang memerlukan toleransi risiko rendah harus dibuat atau ditulis ulang menggunakan kueri berparameter, prosedur tersimpan, atau semacam Object Relational Mapper (ORM) yang membuat kueri Anda untuk Anda

Teknik ini bekerja seperti ini. Setiap DBMS mendukung satu atau lebih skema pelolosan karakter khusus untuk jenis kueri tertentu. Jika Anda kemudian keluar dari semua input yang diberikan pengguna menggunakan skema pelolosan yang tepat untuk database yang Anda gunakan, DBMS tidak akan mengacaukan input tersebut dengan kode SQL yang ditulis oleh pengembang, sehingga menghindari kemungkinan kerentanan injeksi SQL.

OWASP Enterprise Security API (ESAPI) adalah pustaka kontrol keamanan aplikasi web gratis, sumber terbuka, yang memudahkan pemrogram untuk menulis aplikasi berisiko rendah. Pustaka ESAPI dirancang untuk memudahkan pemrogram memasang kembali keamanan ke dalam aplikasi yang sudah ada. Perpustakaan ESAPI juga berfungsi sebagai dasar yang kuat untuk pengembangan baru

  • Detail lengkap tentang ESAPI tersedia di sini di OWASP
  • Javadoc untuk ESAPI 2. x (Legacy) tersedia. Kode ini dimigrasikan ke GitHub pada November 2014
  • ESAPI lama untuk Java di GitHub membantu memahami penggunaannya saat Javadoc tampaknya tidak mencukupi
  • Upaya ESAPI lain untuk Java GitHub memiliki pendekatan lain dan tidak ada pengujian atau codec konkret

Untuk menemukan javadoc khusus untuk pembuat enkode basis data, klik kelas

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
8 di sisi kiri. Ada banyak Codec yang diterapkan. Dua codec khusus Database adalah
//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
9, dan
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
0

Cukup klik nama mereka di

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
_1 di bagian atas halaman Interface Codec

Saat ini, ESAPI saat ini memiliki pembuat enkode basis data

  • Peramal
  • MySQL (Mendukung ANSI dan mode asli)

Pembuat enkode basis data akan segera hadir

  • Server SQL
  • PostgreSQL

Jika encoder basis data Anda hilang, beri tahu kami

Detail Pelarian Khusus Basis Data

Jika Anda ingin membuat rutinitas pelolosan Anda sendiri, berikut detail pelolosan untuk setiap database tempat kami mengembangkan ESAPI Encoder untuk

  • Peramal
  • Server SQL
  • DB2
Oracle Melarikan diri

Informasi ini berdasarkan pada

Keluar dari Kueri Dinamis¶

Untuk menggunakan codec database ESAPI cukup sederhana. Contoh Oracle terlihat seperti

ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );

Jadi, jika Anda memiliki kueri Dinamis yang ada yang dihasilkan dalam kode Anda yang akan dikirim ke Oracle yang terlihat seperti ini

String query = "SELECT user_id FROM user_data WHERE user_name = '"
              + req.getParameter("userID")
              + "' and user_password = '" + req.getParameter("pwd") +"'";
try {
    Statement statement = connection.createStatement(  );
    ResultSet results = statement.executeQuery( query );
}

Anda akan menulis ulang baris pertama agar terlihat seperti ini

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_0

Dan sekarang akan aman dari injeksi SQL, terlepas dari input yang diberikan

Untuk keterbacaan kode maksimum, Anda juga dapat membuat

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
2 Anda sendiri

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_1

Dengan jenis solusi ini, Anda hanya perlu membungkus setiap parameter yang disediakan pengguna yang diteruskan ke panggilan

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
3 atau apa pun yang Anda beri nama panggilan itu dan Anda akan selesai

Matikan penggantian karakter¶

Gunakan

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
_4 atau
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
5 untuk memastikan penggantian karakter otomatis dinonaktifkan. Jika penggantian karakter ini diaktifkan, karakter & akan diperlakukan seperti awalan variabel SQLPlus yang memungkinkan penyerang mengambil data pribadi

Lihat dan di sini untuk informasi lebih lanjut

Melarikan diri dari karakter Wildcard di Like Clauses¶

Kata kunci

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
_6 memungkinkan pencarian pemindaian teks. Di Oracle, karakter garis bawah
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
_7 hanya cocok dengan satu karakter, sedangkan ampersand
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
8 digunakan untuk mencocokkan nol atau lebih kemunculan karakter apa pun. Karakter ini harus diloloskan dalam kriteria klausa LIKE

Misalnya

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_2

Oracle 10g melarikan diri¶

Alternatif untuk Oracle 10g dan yang lebih baru adalah menempatkan

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}
9 dan
 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
0 di sekitar string untuk keluar dari seluruh string. Namun, Anda harus berhati-hati karena belum ada
 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
0 karakter dalam string. Anda harus mencari ini dan jika ada, maka Anda harus menggantinya dengan
 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
2. Jika tidak, karakter tersebut akan mengakhiri pelarian lebih awal, dan dapat menimbulkan kerentanan

MySQL Melarikan diri

MySQL mendukung dua mode pelolosan

  1.  Try
       Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
       command.CommandType = CommandType.StoredProcedure
       command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
       Dim reader As SqlDataReader = command.ExecuteReader()
       '...
     Catch se As SqlException
       'error handling
     End Try
    
    3 SQL mode, dan mode dengan off ini, yang kita sebut
  2.  Try
       Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
       command.CommandType = CommandType.StoredProcedure
       command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
       Dim reader As SqlDataReader = command.ExecuteReader()
       '...
     Catch se As SqlException
       'error handling
     End Try
    
    4 modus

 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
5 mode. Cukup enkodekan semua
 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
6 (tanda centang tunggal) karakter dengan
 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
7 (dua tanda centang tunggal)

 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
4 mode, lakukan hal berikut

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_3

Informasi ini didasarkan pada informasi karakter MySQL Escape

Pelarian SQL Server

Kami belum menerapkan rutinitas pelolosan SQL Server, tetapi yang berikut ini memiliki petunjuk dan tautan yang bagus ke artikel yang menjelaskan cara mencegah serangan injeksi SQL pada server SQL, lihat di sini

DB2 Melarikan diri

Informasi ini didasarkan pada karakter khusus WebQuery DB2 serta beberapa informasi dari driver JDBC DB2 Oracle

Informasi mengenai perbedaan antara beberapa driver DB2 Universal

Hex-encoding semua masukan

Kasus pelolosan yang agak khusus adalah proses hex-encode seluruh string yang diterima dari pengguna (ini dapat dilihat sebagai pelolosan setiap karakter). Aplikasi web harus melakukan hex-encode input pengguna sebelum memasukkannya ke dalam pernyataan SQL. Pernyataan SQL harus mempertimbangkan fakta ini, dan dengan demikian membandingkan datanya

Misalnya, jika kita harus mencari catatan yang cocok dengan ID sesi, dan pengguna mengirimkan string abc123 sebagai ID sesi, pernyataan pilih akan menjadi

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_4

 Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try
9 harus diganti dengan fasilitas khusus untuk database yang digunakan. String 606162313233 adalah versi hex encoded dari string yang diterima dari pengguna (ini adalah urutan nilai hex dari kode ASCII/UTF-8 dari data pengguna)

Jika penyerang mengirimkan string yang berisi karakter kutipan tunggal diikuti dengan upaya mereka untuk menyuntikkan kode SQL, pernyataan SQL yang dibuat hanya akan terlihat seperti

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_5

String tableName;
switch(PARAM):
  case "Value1": tableName = "fooTable";
                 break;
  case "Value2": tableName = "barTable";
                 break;
  ...
  default      : throw new InputValidationException("unexpected value provided"
                                                  + " for table name");
0 menjadi kode ASCII (dalam hex) dari kutipan tunggal, yang hanya dikodekan hex seperti karakter lain dalam string. SQL yang dihasilkan hanya dapat berisi angka dan huruf numerik
String tableName;
switch(PARAM):
  case "Value1": tableName = "fooTable";
                 break;
  case "Value2": tableName = "barTable";
                 break;
  ...
  default      : throw new InputValidationException("unexpected value provided"
                                                  + " for table name");
1 hingga
String tableName;
switch(PARAM):
  case "Value1": tableName = "fooTable";
                 break;
  case "Value2": tableName = "barTable";
                 break;
  ...
  default      : throw new InputValidationException("unexpected value provided"
                                                  + " for table name");
2, dan tidak pernah ada karakter khusus yang dapat mengaktifkan injeksi SQL

Melarikan diri dari SQLi di PHP

Gunakan pernyataan yang disiapkan dan kueri berparameter. Ini adalah pernyataan SQL yang dikirim ke dan diuraikan oleh server database secara terpisah dari parameter apa pun. Dengan cara ini, penyerang tidak mungkin menyuntikkan SQL berbahaya

Anda pada dasarnya memiliki dua opsi untuk mencapai ini

  1. Menggunakan PDO (untuk semua driver database yang didukung)

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_6

  1. Menggunakan MySQLi (untuk MySQL)

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
_7

PDO adalah opsi universal. Jika Anda terhubung ke database selain MySQL, Anda dapat merujuk ke opsi kedua khusus driver (mis. g. pg_prepare() dan pg_execute() untuk PostgreSQL)

Pertahanan Tambahan

Selain mengadopsi salah satu dari empat pertahanan utama, kami juga merekomendasikan mengadopsi semua pertahanan tambahan ini untuk menyediakan pertahanan secara mendalam. Pertahanan tambahan ini adalah

  • Hak Istimewa Paling Sedikit
  • Validasi Input daftar yang diizinkan

Hak Istimewa Paling Sedikit

Untuk meminimalkan potensi kerusakan dari serangan injeksi SQL yang berhasil, Anda harus meminimalkan hak istimewa yang diberikan ke setiap akun database di lingkungan Anda. Jangan tetapkan hak akses jenis DBA atau admin ke akun aplikasi Anda. Kami memahami bahwa ini mudah, dan semuanya hanya "berfungsi" jika Anda melakukannya dengan cara ini, tetapi sangat berbahaya

Mulai dari bawah ke atas untuk menentukan hak akses apa yang diperlukan akun aplikasi Anda, daripada mencoba mencari tahu hak akses apa yang perlu Anda ambil. Pastikan bahwa akun yang hanya memerlukan akses baca hanya diberikan akses baca ke tabel yang aksesnya diperlukan

Jika akun hanya memerlukan akses ke bagian tabel, pertimbangkan untuk membuat tampilan yang membatasi akses ke bagian data tersebut dan menetapkan akses akun ke tampilan, bukan tabel yang mendasarinya. Jarang, jika pernah, memberikan akses buat atau hapus ke akun basis data

Jika Anda menerapkan kebijakan di mana Anda menggunakan prosedur tersimpan di mana saja, dan tidak mengizinkan akun aplikasi untuk secara langsung menjalankan kueri mereka sendiri, maka batasi akun tersebut agar hanya dapat menjalankan prosedur tersimpan yang mereka perlukan. Jangan beri mereka hak apa pun secara langsung ke tabel di database

Injeksi SQL bukan satu-satunya ancaman bagi data database Anda. Penyerang dapat dengan mudah mengubah nilai parameter dari salah satu nilai legal yang diberikan kepada mereka, menjadi nilai yang tidak sah bagi mereka, tetapi aplikasi itu sendiri mungkin diizinkan untuk mengakses. Dengan demikian, meminimalkan hak istimewa yang diberikan ke aplikasi Anda akan mengurangi kemungkinan upaya akses tidak sah tersebut, bahkan ketika penyerang tidak mencoba menggunakan injeksi SQL sebagai bagian dari eksploit mereka.

Saat Anda melakukannya, Anda harus meminimalkan hak istimewa dari akun sistem operasi yang dijalankan oleh DBMS. Jangan jalankan DBMS Anda sebagai root atau sistem. Sebagian besar DBMS kehabisan kotak dengan akun sistem yang sangat kuat. Misalnya, MySQL berjalan sebagai sistem di Windows secara default. Ubah akun OS DBMS menjadi sesuatu yang lebih sesuai, dengan hak istimewa terbatas

Banyak Pengguna DB

Perancang aplikasi web harus menghindari penggunaan akun pemilik/admin yang sama di aplikasi web untuk terhubung ke database. Pengguna DB yang berbeda harus digunakan untuk aplikasi web yang berbeda

Secara umum, setiap aplikasi web terpisah yang memerlukan akses ke database harus memiliki akun pengguna database yang ditunjuk yang akan digunakan aplikasi untuk terhubung ke DB. Dengan begitu, perancang aplikasi dapat memiliki perincian yang baik dalam kontrol akses, sehingga mengurangi hak istimewa sebanyak mungkin. Setiap pengguna DB kemudian akan memiliki akses pilih ke apa yang dibutuhkannya saja, dan akses tulis sesuai kebutuhan

Sebagai contoh, halaman login memerlukan akses baca ke bidang nama pengguna dan sandi tabel, tetapi tidak ada akses tulis dalam bentuk apa pun (tidak boleh menyisipkan, memperbarui, atau menghapus). Namun, halaman pendaftaran tentu membutuhkan hak istimewa untuk dimasukkan ke tabel itu;

Tampilan

Anda dapat menggunakan tampilan SQL untuk lebih meningkatkan perincian akses dengan membatasi akses baca ke bidang tabel tertentu atau gabungan tabel. Ini berpotensi memiliki manfaat tambahan. misalnya, misalkan sistem diperlukan (mungkin karena beberapa persyaratan hukum tertentu) untuk menyimpan kata sandi pengguna, bukan kata sandi yang diasinkan.

Perancang dapat menggunakan tampilan untuk mengkompensasi batasan ini; . Setiap serangan injeksi SQL yang berhasil mencuri informasi DB akan dibatasi untuk mencuri hash kata sandi (bahkan bisa berupa hash yang dikunci), karena tidak ada pengguna DB untuk aplikasi web mana pun yang memiliki akses ke tabel itu sendiri

Validasi Input daftar yang diizinkan

Selain menjadi pertahanan utama saat tidak ada lagi yang memungkinkan (mis. g. , ketika variabel bind tidak legal), validasi input juga bisa menjadi pertahanan sekunder yang digunakan untuk mendeteksi input yang tidak sah sebelum diteruskan ke kueri SQL. Untuk informasi lebih lanjut, silakan lihat Lembar Cheat Validasi Input. Lanjutkan dengan hati-hati di sini. Data yang divalidasi belum tentu aman untuk dimasukkan ke dalam kueri SQL melalui pembuatan string

Artikel Terkait

Lembar Curang Serangan Injeksi SQL

Artikel berikut menjelaskan cara mengeksploitasi berbagai jenis Kerentanan Injeksi SQL pada berbagai platform yang artikel ini dibuat untuk membantu Anda menghindarinya

Bagaimana cara membuat database MySQL saya lebih aman?

Praktik Terbaik Keamanan MySQL .
Hapus Akun Default, Pemetaan Port, dan Pengaturan Lainnya. .
Batasi Akses Jarak Jauh. .
Berikan Pengguna Hanya Keistimewaan yang Mereka Butuhkan. .
Gunakan Akun Non-Root. .
Menjaga Server Secara Fisik Aman. .
Pastikan Anda Menjaga Audit & Pemantauan yang Tepat. .
Nilai Keamanan Basis Data Anda Secara Rutin

Bagaimana cara mengamankan koneksi MySQL?

MySQL memerlukan sertifikat dan file kunci untuk mengaktifkan sambungan yang aman. .
ssl-ca=ca. pem. Mengidentifikasi sertifikat Certificate Authority (CA).
ssl-cert=server-cert. pem. Mengidentifikasi sertifikat kunci publik server
ssl-key=kunci-server. pem. Mengidentifikasi kunci pribadi server

Bagaimana cara mengamankan MySQL Windows?

Mengamankan Server MySQL di Windows .
Langkah 1. Instal MySQL pada Windows versi terbaru berbasis NT. .
Langkah 2. Instal MySQL pada Sistem File NTFS. .
Langkah 3. Instal MySQL di Mesin Mandiri. .
Langkah 4. Instal MySQL Versi Produksi Terbaru. .
Langkah 5. Amankan Akun Pengguna MySQL. .
Langkah 6. Nonaktifkan Akses TCP/IP

Apa keamanan sistem file di MySQL?

1. Keamanan Sistem Operasi. Tugas sistem operasi adalah melindungi instalasi MySQL dan file-filenya dari akses eksternal ilegal . Singkatnya, ini berarti bahwa hanya proses MySQL dan administrator database yang diautentikasi yang dapat menyentuh instalasi MySQL.