PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana cara kerja tampilan keamanan_barrier PostgreSQL?

Anda mungkin telah melihat dukungan yang ditambahkan untuk security_barrier tampilan di PostgreSQL 9.2. Saya telah mempelajari kode tersebut dengan tujuan untuk menambahkan dukungan pembaruan otomatis untuk mereka sebagai bagian dari kemajuan pekerjaan keamanan tingkat baris untuk proyek AXLE, dan saya pikir saya akan mengambil kesempatan untuk menjelaskan cara kerjanya.

Robert sudah menjelaskan mengapa mereka berguna dan apa yang mereka lindungi. (Ternyata itu juga dibahas di apa yang baru di 9.2). Sekarang saya ingin masuk ke bagaimana mereka bekerja dan mendiskusikan bagaimana security_barrier tampilan berinteraksi dengan tampilan yang dapat diperbarui secara otomatis.

Tampilan normal

Tampilan sederhana yang normal diperluas dalam mode seperti makro sebagai subkueri yang kemudian biasanya dioptimalkan dengan menarik predikatnya ke atas dan menambahkannya ke kualitas kueri yang berisi. Itu mungkin lebih masuk akal dengan sebuah contoh. Tabel yang diberikan:

CREATE TABLE t AS SELECT n, 'secret'||n AS secret FROM generate_series(1,20) n;

dan lihat:

CREATE VIEW t_odd AS SELECT n, secret FROM t WHERE n % 2 = 1;

kueri seperti:

SELECT * FROM t_odd WHERE n < 4

adalah tampilan yang diperluas di dalam kueri yang ditulis ulang menjadi representasi pohon parse dari kueri seperti:

SELECT * FROM (SELECT * FROM t WHERE n % 2 = 1) t_odd WHERE n < 4

yang kemudian diratakan oleh pengoptimal menjadi kueri lintasan tunggal dengan menghilangkan subkueri dan menambahkan WHERE istilah klausa ke kueri luar, menghasilkan:

SELECT * FROM t t_odd WHERE (n % 2 = 1) AND (n < 4)

Meskipun Anda tidak dapat melihat kueri perantara secara langsung dan kueri tersebut tidak pernah ada sebagai SQL nyata, Anda dapat mengamati proses ini dengan mengaktifkan debug_print_parse =on , debug_print_rewrite =aktif dan debug_print_plan =aktif di postgresql.conf . Saya tidak akan mereproduksi pohon parse dan plan di sini karena cukup besar dan mudah dibuat berdasarkan contoh di atas.

Masalah dengan menggunakan tampilan untuk keamanan

Anda mungkin berpikir bahwa memberi seseorang akses ke tampilan tanpa memberi mereka akses ke tabel yang mendasarinya akan menghentikan mereka melihat baris bernomor genap. Awalnya sepertinya itu benar:

regress=> SELECT * FROM t_odd WHERE n < 4;
 n | secret  
---+---------
 1 | secret1
 3 | secret3
(2 rows)

tetapi ketika Anda melihat rencananya, Anda mungkin melihat potensi masalah:

regress=> EXPLAIN SELECT * FROM t_odd WHERE n < 4;
                    QUERY PLAN                     
---------------------------------------------------
 Seq Scan on t  (cost=0.00..31.53 rows=2 width=36)
   Filter: ((n < 4) AND ((n % 2) = 1))
(2 rows)

Subkueri tampilan telah dioptimalkan, dengan qualifier tampilan ditambahkan langsung ke kueri luar.

Dalam SQL, DAN dan ATAU tidak dipesan. Pengoptimal/pelaksana bebas menjalankan cabang mana pun yang menurut mereka lebih mungkin memberi mereka jawaban cepat dan mungkin membiarkan mereka menghindari menjalankan cabang lain. Jadi jika perencana berpikir bahwa n <4 jauh lebih cepat dari n % 2 =1 itu akan mengevaluasi itu terlebih dahulu. Sepertinya tidak berbahaya, kan? Coba:

regress=> CREATE OR REPLACE FUNCTION f_leak(text) RETURNS boolean AS $$
BEGIN
  RAISE NOTICE 'Secret is: %',$1;
  RETURN true;
END;
$$ COST 1 LANGUAGE plpgsql;

regress=> SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4;
NOTICE:  Secret is: secret1
NOTICE:  Secret is: secret2
NOTICE:  Secret is: secret3
NOTICE:  Secret is: secret4
NOTICE:  Secret is: secret5
NOTICE:  Secret is: secret6
NOTICE:  Secret is: secret7
NOTICE:  Secret is: secret8
NOTICE:  Secret is: secret9
NOTICE:  Secret is: secret10
NOTICE:  Secret is: secret11
NOTICE:  Secret is: secret12
NOTICE:  Secret is: secret13
NOTICE:  Secret is: secret14
NOTICE:  Secret is: secret15
NOTICE:  Secret is: secret16
NOTICE:  Secret is: secret17
NOTICE:  Secret is: secret18
NOTICE:  Secret is: secret19
NOTICE:  Secret is: secret20
 n | secret  
---+---------
 1 | secret1
 3 | secret3
(2 rows)

regress=> EXPLAIN SELECT * FROM t_odd WHERE f_leak(secret) AND n < 4;
                        QUERY PLAN                        
----------------------------------------------------------
 Seq Scan on t  (cost=0.00..34.60 rows=1 width=36)
   Filter: (f_leak(secret) AND (n < 4) AND ((n % 2) = 1))
(2 rows)

Ups! Seperti yang Anda lihat, fungsi predikat yang disediakan pengguna dianggap lebih murah untuk dijalankan daripada pengujian lainnya, jadi fungsi ini dilewati setiap baris sebelum predikat tampilan mengecualikannya. Fungsi jahat dapat menggunakan trik yang sama untuk menyalin baris.

penghalang_keamanan dilihat

penghalang_keamanan tampilan memperbaikinya dengan memaksa qualifier pada tampilan untuk dieksekusi terlebih dahulu, sebelum qualifier yang disediakan pengguna dijalankan. Alih-alih memperluas tampilan dan menambahkan qualifier tampilan apa pun ke kueri luar, mereka mengganti referensi ke tampilan dengan subkueri. Subquery ini memiliki security_barrier flag disetel pada entri tabel rentangnya, yang memberi tahu pengoptimal bahwa ia tidak boleh meratakan subkueri atau mendorong kondisi kueri luar ke dalamnya seperti yang akan dilakukan untuk subkueri normal.

Jadi dengan tampilan penghalang keamanan:

CREATE VIEW t_odd_sb WITH (security_barrier) AS SELECT n, secret FROM t WHERE n % 2 = 1;

kita mendapatkan:

regress=> SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4;
NOTICE:  Secret is: secret1
NOTICE:  Secret is: secret3
 n | secret  
---+---------
 1 | secret1
 3 | secret3
(2 rows)

regress=> EXPLAIN SELECT * FROM t_odd_sb WHERE f_leak(secret) AND n < 4;
                          QUERY PLAN                           
---------------------------------------------------------------
 Subquery Scan on t_odd_sb  (cost=0.00..31.55 rows=1 width=36)
   Filter: f_leak(t_odd_sb.secret)
   ->  Seq Scan on t  (cost=0.00..31.53 rows=2 width=36)
         Filter: ((n < 4) AND ((n % 2) = 1))
(4 rows)

Rencana kueri harus memberi tahu Anda apa yang terjadi, meskipun tidak menunjukkan atribut penghalang keamanan dalam output penjelasan. Subquery bersarang memaksa pemindaian pada t dengan view qualifier, maka fungsi yang disediakan pengguna berjalan pada hasil subquery.

Tetapi. Tunggu sebentar. Mengapa predikat yang disediakan pengguna n <4 juga di dalam subquery? Bukankah itu lubang keamanan potensial? Jika n <4 didorong ke bawah, mengapa tidak f_leak(rahasia) ?

TAHAN BOCOK operator dan fungsi

Penjelasannya adalah bahwa operator ditandai TAHAN BOCOK . Atribut ini menunjukkan bahwa operator atau fungsi dipercaya untuk tidak membocorkan informasi, sehingga dapat dengan aman didorong ke bawah melalui security_barrier pandangan. Untuk alasan yang jelas, Anda tidak dapat menyetel LEAKPROOF sebagai pengguna biasa:

regress=> ALTER FUNCTION f_leak(text)  LEAKPROOF;
ERROR:  only superuser can define a leakproof function

dan pengguna super sudah dapat melakukan apa pun yang mereka inginkan, sehingga mereka tidak perlu menggunakan trik dengan fungsi yang membocorkan informasi untuk melewati tampilan penghalang keamanan.

Mengapa Anda tidak dapat memperbarui security_barrier dilihat

Tampilan sederhana di PostgreSQL 9.3 dapat diperbarui secara otomatis, tetapi security_barrier tampilan tidak dianggap "sederhana". Itu karena memperbarui tampilan bergantung pada kemampuan untuk meratakan subkueri tampilan, mengubah pembaruan menjadi pembaruan sederhana di atas meja. Inti dari security_barrier dilihat adalah untuk mencegah meratakan itu. PERBARUI saat ini tidak dapat beroperasi pada subquery secara langsung, jadi PostgreSQL akan menolak segala upaya untuk memperbarui security_barrier lihat:

regress=> UPDATE t_odd SET secret = 'secret_haha'||n;
UPDATE 10
regress=> UPDATE t_odd_sb SET secret = 'secret_haha'||n;
ERROR:  cannot update view "t_odd_sb"
DETAIL:  Security-barrier views are not automatically updatable.
HINT:  To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.

Batasan inilah yang ingin saya angkat sebagai bagian dari pekerjaan untuk meningkatkan keamanan tingkat baris untuk proyek AXLE. Kohei KaiGai telah melakukan beberapa pekerjaan hebat dengan keamanan tingkat baris, dan fitur-fitur seperti security_barrier dan TAHAN BOCOK sebagian besar muncul dari pekerjaannya untuk menambahkan keamanan tingkat baris ke PostgreSQL. Tantangan berikutnya adalah bagaimana menangani pembaruan pada penghalang keamanan dengan aman dan dengan cara yang dapat dipertahankan di masa mendatang.

Mengapa subkueri?

Anda mungkin bertanya-tanya mengapa kita harus menggunakan subqueries untuk ini. Ya. Versi singkatnya adalah kita tidak harus melakukannya, tetapi jika kita tidak menggunakan subkueri, kita harus membuat varian baru yang peka terhadap pesanan dari DAN dan ATAU operator dan mengajari pengoptimal bahwa ia tidak dapat memindahkan kondisi di antara mereka. Karena tampilan sudah diperluas sebagai subkueri, jauh lebih mudah untuk hanya menandai subkueri sebagai pagar yang memblokir pull-up / push-down.

Namun, sudah ada operasi pesanan korsleting di PostgreSQL – CASE . Masalah dengan menggunakan CASE itu tidak operasi dapat dipindahkan melintasi batas CASE , bahkan TAHAN BOCOK yang. Pengoptimal juga tidak dapat membuat keputusan menggunakan indeks berdasarkan ekspresi di dalam CASE ketentuan. Jadi jika kita menggunakan CASE seperti yang saya tanyakan tentang -hacker, kami tidak akan pernah bisa menggunakan indeks untuk memenuhi kualifikasi yang disediakan pengguna.

Dalam kode

penghalang_keamanan dukungan telah ditambahkan di 0e4611c0234d89e288a53351f775c59522baed7c . Itu ditingkatkan dengan dukungan anti bocor di cd30728fb2ed7c367d545fc14ab850b5fa2a4850 . Kredit muncul di catatan komit. Terima kasih kepada semua orang yang terlibat.

Gambar fitur halaman depan adalah Security Barrier oleh Craig A. Rodway, di Flikr

Penelitian yang mengarah pada hasil ini telah menerima dana dari Program Kerangka Ketujuh Uni Eropa (FP7/2007-2013) di bawah perjanjian hibah n° 318633


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ikhtisar Berbagai Node Paket Tambahan di PostgreSQL

  2. Daftar Python ke Array PostgreSQL

  3. Cara Mengonversi Kasus String di PostgreSQL

  4. Cara membersihkan SQL mentah di Rails 4

  5. isumeric() dengan PostgreSQL