Bagaimana saya bisa membersihkan input pengguna dengan PHP?

1124

Apakah ada fungsi catchall di suatu tempat yang berfungsi dengan baik untuk membersihkan input pengguna untuk injeksi SQL dan serangan XSS, sementara masih memungkinkan jenis tag HTML tertentu?

Brent
sumber
42
Saat ini, untuk menghindari injeksi sql, gunakan PDO atau MySQLi.
Francisco Presencia
76
Menggunakan PDO atau MySQLi tidak cukup. Jika Anda membuat pernyataan SQL dengan data yang tidak tepercaya, seperti select * from users where name='$name', maka tidak masalah jika Anda menggunakan PDO atau MySQLi atau MySQL. Anda masih dalam bahaya. Anda harus menggunakan kueri parametrized atau, jika Anda harus, menggunakan mekanisme melarikan diri pada data Anda, tetapi itu jauh lebih disukai.
Andy Lester
26
@AndyLester Apakah Anda menyiratkan bahwa seseorang menggunakan PDO tanpa pernyataan yang disiapkan? :)
64
Saya mengatakan bahwa "Gunakan PDO atau MySQLi" tidak cukup informasi untuk menjelaskan kepada pemula tentang cara aman menggunakannya. Anda dan saya tahu bahwa pernyataan yang dipersiapkan itu penting, tetapi saya tidak menganggap bahwa setiap orang yang membaca pertanyaan ini akan mengetahuinya. Itu sebabnya saya menambahkan instruksi eksplisit.
Andy Lester
30
Komentar Andy sepenuhnya valid. Saya mengkonversi situs web mysql saya ke PDO baru-baru ini berpikir bahwa saya sekarang entah bagaimana aman dari serangan injeksi. Hanya selama proses saya menyadari bahwa beberapa pernyataan sql saya masih dibangun menggunakan input pengguna. Saya kemudian memperbaikinya dengan menggunakan pernyataan yang disiapkan. Untuk seorang pemula yang lengkap, itu tidak sepenuhnya jelas bahwa ada perbedaan karena banyak ahli membuang komentar tentang menggunakan PDO tetapi tidak menentukan kebutuhan untuk pernyataan yang disiapkan. Asumsinya adalah bahwa ini jelas. Tapi tidak untuk pemula.
GhostRider

Jawaban:

1184

Ini adalah kesalahpahaman umum bahwa input pengguna dapat difilter. PHP bahkan memiliki "fitur" yang sekarang sudah tidak digunakan lagi, yang disebut kutipan ajaib , yang dibangun berdasarkan ide ini. Ini omong kosong. Lupakan penyaringan (atau pembersihan, atau apa pun orang menyebutnya).

Apa yang harus Anda lakukan, untuk menghindari masalah, cukup sederhana: setiap kali Anda memasukkan string ke dalam kode asing, Anda harus menghindarinya, sesuai dengan aturan bahasa itu. Misalnya, jika Anda menyematkan string dalam beberapa SQL yang menargetkan MySQL, Anda harus melarikan diri string dengan fungsi MySQL untuk tujuan ini ( mysqli_real_escape_string). (Atau, dalam hal database, menggunakan pernyataan yang disiapkan adalah pendekatan yang lebih baik, bila mungkin.)

Contoh lain adalah HTML: Jika Anda menanamkan string dalam markup HTML, Anda harus menghindarinya htmlspecialchars. Ini berarti bahwa setiap echoatau printpernyataan harus menggunakan htmlspecialchars.

Contoh ketiga bisa berupa perintah shell: Jika Anda akan menanamkan string (seperti argumen) ke perintah eksternal, dan memanggilnya dengan exec, maka Anda harus menggunakan escapeshellcmddan escapeshellarg.

Dan seterusnya dan seterusnya ...

Satu- satunya kasus di mana Anda perlu memfilter data secara aktif, adalah jika Anda menerima input yang telah diformat sebelumnya. Misalnya, jika Anda membiarkan pengguna memposting markup HTML, yang Anda rencanakan untuk ditampilkan di situs. Namun, Anda harus bijaksana untuk menghindari ini di semua biaya, karena tidak peduli seberapa baik Anda menyaringnya, itu akan selalu menjadi lubang keamanan potensial.

troelskn
sumber
245
"Ini berarti bahwa setiap pernyataan gema atau cetak harus menggunakan htmlspecialchars" - tentu saja, maksud Anda "setiap ... pernyataan menghasilkan input pengguna"; htmlspecialchars () - ifying "echo 'Hello, world!';" akan menjadi gila;)
Bobby Jack
10
Ada satu kasus di mana saya pikir penyaringan adalah solusi yang tepat: UTF-8. Anda tidak ingin urutan UTF-8 tidak valid di seluruh aplikasi Anda (Anda mungkin mendapatkan pemulihan kesalahan yang berbeda tergantung pada jalur kode), dan UTF-8 dapat difilter (atau ditolak) dengan mudah.
Kornel
6
@jbyrd - tidak, LIKE menggunakan bahasa regexp khusus. Anda harus keluar dari string input Anda dua kali - sekali untuk regexp dan satu kali untuk pengkodean string mysql. Ini kode dalam kode di dalam kode.
troelskn
6
Saat ini mysql_real_escape_stringsudah usang. Ini dianggap praktik yang baik saat ini untuk menggunakan pernyataan yang disiapkan untuk mencegah injeksi SQL. Jadi beralihlah ke MySQLi atau PDO.
Marcel Korpel
4
Karena Anda membatasi permukaan serangan. Jika Anda membersihkan awal (saat input), Anda harus yakin bahwa tidak ada lubang lain di aplikasi di mana data buruk bisa masuk. Sedangkan jika Anda melakukannya terlambat, maka fungsi output Anda tidak harus "percaya" bahwa itu diberikan data yang aman - itu hanya menganggap bahwa semuanya tidak aman.
troelskn
217

Jangan mencoba untuk mencegah injeksi SQL dengan membersihkan data input.

Sebaliknya, jangan izinkan data untuk digunakan dalam membuat kode SQL Anda . Gunakan Pernyataan Disiapkan (yaitu menggunakan parameter dalam kueri templat) yang menggunakan variabel terikat. Ini adalah satu-satunya cara untuk dijamin terhadap injeksi SQL.

Silakan lihat situs web saya http://bobby-tables.com/ untuk lebih lanjut tentang mencegah injeksi SQL.

Andy Lester
sumber
18
Atau kunjungi dokumentasi resmi dan pelajari PDO dan pernyataan yang disiapkan. Kurva pembelajaran kecil, tetapi jika Anda tahu SQL dengan cukup baik, Anda tidak akan kesulitan beradaptasi.
seorang coder
2
Untuk kasus khusus SQL Injection, ini adalah jawaban yang benar!
Scott Arciszewski
4
Perhatikan bahwa pernyataan yang disiapkan tidak menambahkan keamanan apa pun, kueri parameterised lakukan. Mereka kebetulan sangat mudah digunakan bersama dalam PHP.
Dasar
Ini bukan satu-satunya cara yang dijamin. Hex input dan unhex pada query akan mencegahnya juga. Juga serangan hex tidak mungkin jika Anda menggunakan hexing benar.
Ramon Bakker
Bagaimana jika Anda memasukkan sesuatu yang khusus, seperti alamat email atau nama pengguna?
Abraham Brookes
79

Tidak. Anda tidak dapat secara umum memfilter data tanpa konteks apa tujuannya. Terkadang Anda ingin mengambil kueri SQL sebagai input dan terkadang Anda ingin mengambil HTML sebagai input.

Anda perlu memfilter input pada daftar putih - memastikan bahwa data cocok dengan spesifikasi yang Anda harapkan. Maka Anda perlu menghindarinya sebelum menggunakannya, tergantung pada konteks di mana Anda menggunakannya.

Proses melarikan diri data untuk SQL - untuk mencegah injeksi SQL - sangat berbeda dari proses melarikan diri data untuk (X) HTML, untuk mencegah XSS.

Daniel Papasian
sumber
52

PHP memiliki fungsi filter_input baru yang bagus sekarang, yang misalnya membebaskan Anda dari menemukan 'regex email utama' sekarang karena ada tipe FILTER_VALIDATE_EMAIL bawaan

Kelas filter saya sendiri (menggunakan JavaScript untuk menyorot bidang yang salah) dapat dimulai dengan permintaan ajax atau posting bentuk normal. (lihat contoh di bawah)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

Tentu saja, perlu diingat bahwa Anda perlu melakukan kueri sql Anda juga tergantung pada jenis db yang Anda gunakan (mysql_real_escape_string () misalnya, tidak berguna untuk server sql). Anda mungkin ingin menangani ini secara otomatis pada lapisan aplikasi yang sesuai seperti ORM. Juga, seperti disebutkan di atas: untuk menghasilkan ke html, gunakan fungsi khusus php lainnya seperti htmlspecialchars;)

Untuk benar-benar mengizinkan input HTML dengan kelas dan / atau tag yang dilucuti seperti bergantung pada salah satu paket validasi xss khusus. JANGAN MENULIS REGEX ANDA SENDIRI KE PARSE HTML!

SchizoDuckie
sumber
18
Ini sepertinya script yang berguna untuk memvalidasi input, tetapi sama sekali tidak relevan dengan pertanyaan.
rjmunro
43

Tidak, tidak ada.

Pertama-tama, injeksi SQL adalah masalah pemfilteran input, dan XSS adalah output yang lolos - jadi Anda bahkan tidak akan menjalankan dua operasi ini pada saat yang sama dalam siklus hidup kode.

Aturan dasar praktis

  • Untuk kueri SQL, ikat parameter (seperti dengan PDO) atau gunakan fungsi pelolosan driver-native untuk variabel kueri (seperti mysql_real_escape_string())
  • Gunakan strip_tags()untuk menyaring HTML yang tidak diinginkan
  • Lepaskan semua output lainnya dengan htmlspecialchars()dan perhatikan parameter ke-2 dan ke-3 di sini.
Peter Bailey
sumber
1
Jadi Anda hanya menggunakan strip_tags () atau htmlspecialchars () ketika Anda tahu bahwa inputnya memiliki HTML yang ingin Anda singkirkan atau hindari - Anda tidak menggunakannya untuk tujuan keamanan apa pun, bukan? Juga, ketika Anda melakukan ikatan, apa fungsinya untuk hal-hal seperti Bobby Tables? "Robert '); Siswa DROP TABEL; -" Apakah itu hanya lolos dari kutipan?
Robert Mark Bram
2
Jika Anda memiliki data pengguna yang akan masuk ke database dan kemudian ditampilkan di halaman web, bukankah itu biasanya lebih banyak dibaca daripada yang tertulis? Bagi saya, lebih masuk akal untuk memfilternya sekali (sebagai input) sebelum Anda menyimpannya, daripada harus memfilternya setiap kali Anda menampilkannya. Apakah saya melewatkan sesuatu atau apakah sekelompok orang memilih untuk melakukan overhead kinerja yang tidak perlu dalam hal ini dan jawaban yang diterima?
jbo5112
2
Jawaban terbaik untuk saya. Ini singkat dan menjawab pertanyaan dengan baik jika Anda bertanya kepada saya. Apakah mungkin untuk menyerang PHP entah bagaimana melalui $ _POST atau $ _GET dengan beberapa suntikan atau apakah ini tidak mungkin?
Jo Smo
oh ya, array $ post dan $ get menerima semua karakter, tetapi beberapa karakter tersebut dapat digunakan untuk melawan Anda jika karakter tersebut diizinkan untuk disebutkan di halaman php yang diposting. jadi jika Anda tidak terhindar dari karakter enkapsulasi (seperti ", 'dan`), ia dapat membuka vektor serangan. karakter `sering terlewatkan, dan dapat digunakan untuk membentuk peretasan eksekusi baris perintah. Sanitasi akan mencegah peretasan input pengguna, tetapi tidak akan membantu Anda dengan peretasan firewall aplikasi web
drtechno
22

Untuk mengatasi masalah XSS, lihat Pemurni HTML . Ini cukup dapat dikonfigurasi dan memiliki rekam jejak yang baik.

Adapun serangan injeksi SQL, pastikan Anda memeriksa input pengguna, dan kemudian jalankan meskipun mysql_real_escape_string (). Namun, fungsi ini tidak akan mengalahkan semua serangan injeksi, jadi Anda harus memeriksa data sebelum membuangnya ke string kueri Anda.

Solusi yang lebih baik adalah dengan menggunakan pernyataan yang disiapkan. The perpustakaan PDO dan ekstensi mysqli mendukung ini.

jasonbar
sumber
tidak ada "cara terbaik" untuk melakukan sesuatu seperti membersihkan input .. Gunakan perpustakaan, pemurni html baik. Perpustakaan-perpustakaan ini telah berkali-kali ditumbuk. Jadi itu jauh lebih antipeluru daripada apa pun yang kamu dapat
kemukakan
Lihat juga bioinformatics.org/phplabware/internal_utilities/htmLawed . Dari pemahaman saya, WordPress menggunakan versi yang lebih lama, core.trac.wordpress.org/browser/tags/2.9.2/wp-includes/kses.php
Steve Clay
Masalah dengan wordpress adalah bahwa itu bukan serangan injeksi php-sql yang menyebabkan pelanggaran basis data. Kehilangan plugin yang diprogram yang menyimpan data yang kueri xml mengungkapkan rahasia lebih bermasalah.
drtechno
17

Salah satu trik yang dapat membantu dalam keadaan tertentu di mana Anda memiliki halaman seperti /mypage?id=53dan Anda menggunakan id dalam klausa WHERE adalah untuk memastikan bahwa id jelas adalah bilangan bulat, seperti:

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

Tapi tentu saja itu hanya memotong satu serangan spesifik, jadi bacalah semua jawaban lainnya. (Dan ya saya tahu bahwa kode di atas tidak bagus, tetapi itu menunjukkan pertahanan spesifik.)

Hamish Downer
sumber
11
Saya menggunakan $ id = intval ($ id) sebagai gantinya :)
Duc Tran
Casting integer adalah cara yang baik untuk memastikan hanya data numerik yang dimasukkan.
Tes
1
$id = (int)$_GET['id']dan $que = sprintf('SELECT ... WHERE id="%d"', $id)juga baik
vladkras
16

Metode untuk membersihkan input pengguna dengan PHP:

  • Gunakan Versi Modern MySQL dan PHP.

  • Setel rangkaian karakter secara eksplisit:

    • $ mysqli-> set_charset ("utf8");
      manual
    • $ pdo = PDO baru ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password);
      manual
    • $ pdo-> exec ("set names utf8");
      manual
    • $ pdo = PDO baru (
      "mysql: host = $ host; dbname = $ db", $ user, $ pass, 
      Himpunan(
      PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION,
      PDO :: MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      manual
    • mysql_set_charset ('utf8')
      [ditinggalkan dalam PHP 5.5.0, dihapus dalam PHP 7.0.0].
  • Gunakan rangkaian aman:

    • Pilih utf8, latin1, ascii .., jangan gunakan rangkaian karakter rentan big5, cp932, gb2312, gbk, sjis.
  • Gunakan fungsi spasial:

    • Pernyataan yang disiapkan MySQLi:
      $ stmt = $ mysqli-> ready ('SELECT * FROM test WHERE name =? LIMIT 1'); 
      $ param = "'ATAU 1 = 1 / *";
      $ stmt-> bind_param ('s', $ param);
      $ stmt-> execute ();
    • PDO :: quote () - menempatkan kutipan di sekitar string input (jika diperlukan) dan lolos dari karakter khusus dalam string input, menggunakan gaya kutipan yang sesuai dengan driver yang mendasarinya:

      $ pdo = PDO baru ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); setel karakter secara eksplisit
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); nonaktifkan meniru pernyataan yang disiapkan untuk mencegah fallback ke meniru pernyataan bahwa MySQL tidak dapat mempersiapkan secara asli (untuk mencegah injeksi)
      $ var = $ pdo-> quote ("'OR 1 = 1 / *"); tidak hanya lolos dari literal, tetapi juga mengutipnya (dalam karakter tanda kutip tunggal) $ stmt = $ pdo-> query ("SELECT * FROM test WHERE name = $ var LIMIT 1");

    • Pernyataan Disiapkan PDO : pernyataan yang dipersiapkan vs MySQLi mendukung lebih banyak driver basis data dan parameter bernama:

      $ pdo = PDO baru ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); setel karakter secara eksplisit
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); nonaktifkan meniru pernyataan yang disiapkan untuk mencegah fallback ke meniru pernyataan bahwa MySQL tidak dapat mempersiapkan secara asli (untuk mencegah injeksi) $ stmt = $ pdo-> ready ('SELECT * FROM test WHERE name =? LIMIT 1'); $ stmt-> execute (["'OR 1 = 1 / *"]);

    • mysql_real_escape_string [usang dalam PHP 5.5.0, dihapus dalam PHP 7.0.0].
    • mysqli_real_escape_string Mengosongkan karakter khusus dalam string untuk digunakan dalam pernyataan SQL, dengan mempertimbangkan charset koneksi saat ini. Tetapi disarankan untuk menggunakan Pernyataan Disiapkan karena mereka tidak hanya lolos string, pernyataan muncul dengan rencana eksekusi permintaan lengkap, termasuk tabel dan indeks yang akan digunakan, itu adalah cara yang dioptimalkan.
    • Gunakan tanda kutip tunggal ('') di sekitar variabel Anda di dalam kueri Anda.
  • Periksa variabel berisi apa yang Anda harapkan:

    • Jika Anda mengharapkan bilangan bulat, gunakan:
      ctype_digit - Periksa karakter numerik; 
      $ value = (int) $ value;
      $ value = intval ($ value);
      $ var = filter_var ('0755', FILTER_VALIDATE_INT, $ options);
    • Untuk Strings gunakan:
      is_string () - Temukan apakah tipe variabel adalah string

      Gunakan Fungsi Filter filter_var () - memfilter variabel dengan filter yang ditentukan:
      $ email = filter_var ($ email, FILTER_SANITIZE_EMAIL);
      $ newstr = filter_var ($ str, FILTER_SANITIZE_STRING);
      filter yang lebih standar
    • filter_input () - Mendapat variabel eksternal spesifik dengan nama dan secara opsional memfilternya:
      $ search_html = filter_input (INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () - Lakukan kecocokan ekspresi reguler;
    • Tulis fungsi validasi Anda sendiri.
Mark Martin
sumber
11

Apa yang Anda uraikan di sini adalah dua masalah terpisah:

  1. Sanitasi / pemfilteran data input pengguna.
  2. Menghindari output.

1) Input pengguna harus selalu dianggap buruk.

Menggunakan pernyataan yang disiapkan, atau / dan memfilter dengan mysql_real_escape_string jelas merupakan suatu keharusan. PHP juga memiliki filter_input bawaan yang merupakan tempat yang baik untuk memulai.

2) Ini adalah topik besar, dan itu tergantung pada konteks data yang dikeluarkan. Untuk HTML ada solusi seperti htmlpurifier di luar sana. sebagai aturan praktis, selalu luput dari apapun yang Anda hasilkan.

Kedua masalah ini terlalu besar untuk dimasukkan ke dalam satu posting, tetapi ada banyak posting yang membahas lebih detail:

Metode keluaran PHP

Output PHP lebih aman

Andrew
sumber
9

Jika Anda menggunakan PostgreSQL, input dari PHP dapat diloloskan dengan pg_escape_string ()

 $username = pg_escape_string($_POST['username']);

Dari dokumentasi ( http://php.net/manual/es/function.pg-escape-string.php ):

pg_escape_string () lolos dari string untuk query database. Ia mengembalikan sebuah string yang lolos dalam format PostgreSQL tanpa tanda kutip.

Alejandro Silva
sumber
1
pg_escape_literal () adalah fungsi yang direkomendasikan untuk digunakan untuk PostgreSQL.
samar
8

Tidak ada fungsi catchall, karena ada beberapa masalah yang harus diatasi.

  1. SQL Injection - Hari ini, umumnya, setiap proyek PHP harus menggunakan pernyataan yang disiapkan melalui PHP Data Objects (PDO) sebagai praktik terbaik, mencegah kesalahan dari kutipan nyasar serta solusi berfitur lengkap terhadap injeksi . Ini juga cara paling fleksibel & aman untuk mengakses database Anda.

    Lihatlah tutorial PDO (Satu-satunya yang tepat) untuk mengetahui hampir semua yang perlu Anda ketahui tentang PDO. (Terima kasih yang tulus kepada kontributor SO teratas, @YourCommonSense, untuk sumber yang hebat tentang subjek ini.)

  2. XSS - Sanitasi data di jalan di ...

    • Pemurni HTML sudah ada sejak lama dan masih diperbarui secara aktif. Anda dapat menggunakannya untuk membersihkan input berbahaya, sambil tetap mengizinkan daftar putih tag yang murah hati & dapat dikonfigurasi. Berfungsi bagus dengan banyak editor WYSIWYG, tetapi mungkin berat untuk beberapa kasus penggunaan.

    • Dalam kasus lain, di mana kami sama sekali tidak ingin menerima HTML / Javascript, saya menemukan fungsi sederhana ini bermanfaat (dan telah melewati beberapa audit terhadap XSS):

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS - Data membersihkan di jalan keluar ... kecuali jika Anda menjamin data itu benar dibersihkan sebelum Anda menambahkannya ke database Anda, Anda harus membersihkan sebelum menampilkan kepada pengguna Anda, kami dapat memanfaatkan fungsi-fungsi PHP yang berguna:

    • Saat Anda menelepon echoatau printmenampilkan nilai yang disediakan pengguna, gunakan htmlspecialcharskecuali jika data telah dibersihkan dengan aman dan diizinkan untuk menampilkan HTML.
    • json_encode adalah cara aman untuk memberikan nilai yang disediakan pengguna dari PHP ke Javascript
  4. Apakah Anda memanggil perintah shell eksternal menggunakan exec()atau system()fungsi, atau ke backtickoperator? Jika demikian, selain SQL Injection & XSS Anda mungkin memiliki masalah tambahan untuk diatasi, pengguna yang menjalankan perintah jahat di server Anda . Anda perlu menggunakan escapeshellcmdjika Anda ingin melarikan diri dari seluruh perintah ATAU escapeshellarguntuk menghindari argumen individual.

webaholik
sumber
bisakah mb_encode_numericentity digunakan sebagai gantinya? Karena itu mengkodekan semuanya?
drtechno
@drtechno - mb_encode_numericentitydibahas dalam htmlspecialcharstautan di # 3 XSS
webaholik
5

Cara termudah untuk menghindari kesalahan dalam membersihkan input dan melarikan diri data menggunakan kerangka kerja PHP seperti Symfony , Nette dll. Atau bagian dari kerangka kerja itu (mesin templating, lapisan basis data, ORM).

Mesin templating seperti Twig atau Latte memiliki output yang keluar secara default - Anda tidak harus menyelesaikan secara manual jika Anda telah benar-benar keluar dari output Anda tergantung pada konteks (HTML atau bagian Javascript dari halaman web).

Kerangka kerja secara otomatis membersihkan input dan Anda tidak boleh menggunakan variabel $ _POST, $ _GET atau $ _SESSION secara langsung, tetapi melalui mekanisme seperti perutean, penanganan sesi, dll.

Dan untuk lapisan basis data (model) ada kerangka kerja ORM seperti Doctrine atau pembungkus di sekitar PDO seperti Nette Database.

Anda dapat membaca lebih lanjut tentang ini di sini - Apa itu kerangka kerja perangkat lunak?

Ondřej Šotek
sumber
3

Hanya ingin menambahkan bahwa pada subjek keluaran melarikan diri, jika Anda menggunakan php DOMDocument untuk membuat output html Anda maka secara otomatis akan keluar dalam konteks yang benar. Atribut (nilai = "") dan teks bagian dalam <span> tidak sama. Untuk aman terhadap XSS baca ini: OWASP XSS Prevention Cheat Sheet

pengguna138720
sumber
2

Anda tidak pernah membersihkan input.

Anda selalu membersihkan output.

Transformasi yang Anda terapkan pada data untuk membuatnya aman untuk dimasukkan dalam pernyataan SQL benar-benar berbeda dari yang Anda ajukan untuk dimasukkan dalam HTML benar-benar berbeda dari yang Anda ajukan untuk dimasukkan dalam Javascript sama sekali berbeda dari yang Anda ajukan untuk dimasukkan dalam LDIF adalah sama sekali berbeda dari yang Anda terapkan pada inklusi di CSS sama sekali berbeda dari yang Anda terapkan pada inklusi dalam Email ....

Dengan segala cara, validasikan input - putuskan apakah Anda harus menerimanya untuk diproses lebih lanjut atau memberi tahu pengguna bahwa itu tidak dapat diterima. Tapi jangan terapkan perubahan apa pun pada representasi data sampai akan meninggalkan tanah PHP.

Beberapa waktu yang lalu seseorang mencoba untuk menemukan satu ukuran cocok untuk semua mekanisme untuk melarikan diri data dan kami berakhir dengan " magic_quotes " yang tidak melarikan diri dengan baik data untuk semua target keluaran dan menghasilkan instalasi berbeda yang memerlukan kode berbeda untuk bekerja.

symcbean
sumber
satu masalah dengan itu adalah tidak selalu merupakan serangan basis data, dan semua input pengguna harus dilindungi dari sistem. bukan hanya satu jenis bahasa. Jadi di situs Anda, ketika Anda menghitung $ _POST data Anda, bahkan dengan menggunakan binding, itu bisa keluar cukup untuk mengeksekusi shell atau bahkan kode php lainnya.
drtechno
"itu tidak selalu merupakan serangan basis data": "Transformasi yang Anda terapkan pada data untuk membuatnya aman untuk dimasukkan dalam pernyataan SQL benar-benar berbeda dari yang ...."
symcbean
"semua input pengguna harus dilindungi dari sistem": tidak ada sistem yang harus dilindungi dari input pengguna.
symcbean
baik saya kehabisan kata-kata, tapi ya input perlu dicegah dari mempengaruhi operasi sistem. untuk memperjelas ini ...
drtechno
Baik input maupun output harus disanitasi.
Tajni
1

Jangan pernah mempercayai data pengguna.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

The trim()Menghapus fungsi spasi dan karakter yang telah ditetapkan lainnya dari kedua sisi string.

The stripslashes()fungsi menghilangkan backslashes

The htmlspecialchars()Fungsi mengkonversi beberapa karakter yang telah ditetapkan untuk entitas HTML.

Karakter yang ditentukan sebelumnya adalah:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;
Erik Thiart
sumber
1
Apa yang akan dilindungi dari ini? Apakah ini untuk XSS? Mengapa disebut clean_inputdemikian? Mengapa Anda ingin menghapus garis miring?
Dharman
5
PERINGATAN: Ini secara ajaib tidak membuat data pengguna aman. Fungsi ini akan merusak data Anda secara tidak perlu tanpa melindungi dari apa pun. JANGAN GUNAKAN ITU!
Dharman
Pernyataan Anda salah.
Erik Thiart
0

Ada ekstensi filter ( tautan howto , manual ), yang berfungsi dengan baik dengan semua variabel GPC. Ini bukan hal yang ajaib, Anda masih harus menggunakannya.

Sampai
sumber