MathJax

MathJax-2

MathJax-3

Google Code Prettify

置頂入手筆記

EnterproseDB Quickstart — 快速入門筆記

由於考慮採用 EnterpriseDB 或是直接用 PostgreSQL 的人,通常需要一些入手的資料。這邊紀錄便提供相關快速上手的簡單筆記 ~ 這篇筆記以 資料庫安裝完畢後的快速使用 為目標,基本紀錄登入使用的範例:

2017年2月7日 星期二

EDB 功能 - SQL/Protect

有時候,程式開發會使用參數方式組合出 SQL 指令,再往資料庫提請求。但是如果設計有疏忽,沒有對前端使用者的輸入條件進行完整的檢查,或是資料庫維護者不小心忘記限制好帳號的權限,可能會被有心人士設計出恰當的輸入條件,湊出能逾越一般該有權限所能進行的資料庫指令,這一般被稱作 SQL Injction 攻擊。


PostgreSQL 企業版提供了資料庫端的資料防護機制,SQL/Protect,能夠在資料庫端防止異常的 SQL,例如,對於資料需要受保護的資料表進行資料「全部」撈出來的 SQL SELECT 指令,或是怪怪的條件等等。


SQL/Protect 模組,當我們選定一些資料庫帳戶 User 或是帳戶群組 Group Role(以上兩者,在 PostgreSQL 裡面統稱為 Role ~)之後,能提供以下的功能:
  • 被動紀錄、警示列管帳戶執行可疑活動
  • 主動防止列管帳戶執行可疑活動


而「可疑活動」的界定,則是透過學習模式,預先將該帳號會進行的行為全部演練一次。例如,該帳號會對哪幾個表格輸入/查詢,查詢用到的 SQL 有哪一些型態,等等,全部都預先在 SQL/Protect 學習模式下跑一遍。建立起該帳號的行為比對模式(能存取的表格,能進行的作業,等等)。


學習完之後,只要該帳號發生紀錄過的行為模式「以外」,就會觸發 SQL/Protect 功能。因此這個工具的使用場景,便是針對提供 Application Server 使用帳號進行保護:這類帳號的行為固定,都是由已經開發好的程式進行活動;發生了一般模式之外的行徑,八成是被攻擊,就會受 SQL/Protect 所保護。

以下便紀錄基本的使用筆記:



使用 SQL/Protect 的流程如下:
  1. 設定預先載入 SQL/Protect 模組
  2. 先啟動 SQL/Protect 學習模式
  3. 設置好需要受管理的資料庫帳號/群組
  4. 進行該帳號需要的進行的「正常」作業,使 SQL/Protect 紀錄到該帳號「所有可能行為」
  5. 完成學習,調整成被動模式/主動模式,開始保護


啟用 SQL/Protect 模組,並設定保護模式:這些要在 $PGDATA/postgresql.conf 裡面設定:
bash-$ source /opt/PostgresPlus/9.5AS/pgplus_env.sh
bash-$ sed -e "s@shared_preload_libraries = '\([^']*\)'@shared_preload_libraries = '\1,\$libdir\/sqlprotect'@g" \
    -i $PGDATA/postgresql.conf

bash-$ cat << CFGEOF  >> $PGDATA/postgresql.conf

#---------------------------------------------------------------------------
# EDB SQL/Protect Module
#---------------------------------------------------------------------------

edb_sql_protect.enabled = on                # Reload to change
edb_sql_protect.level = learn                # Reload to change
                                             # 'learn','passive', or 'active'
edb_sql_protect.max_protected_roles = 64     # Default : 64
edb_sql_protect.max_protected_relations = 1024   # Default : 1024
edb_sql_protect.max_queries_to_save = 5000   # Default : 5000
CFGEOF
bash-$ pg_ctl restart


接著在要啟動 SQL/Protect 的 Database 裡面建立相關物件,在此登入 edb 進行試驗
bash-$ psql -p 5444 -d edb -f /opt/PostgresPlus/9.5AS/share/contrib/sqlprotect.sql
Password: 
CREATE SCHEMA
GRANT
SET
CREATE TABLE
GRANT
CREATE TABLE
GRANT
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
DO
CREATE FUNCTION
CREATE FUNCTION
DO
CREATE VIEW
GRANT
DO
CREATE VIEW
GRANT
CREATE VIEW
GRANT
CREATE FUNCTION
CREATE FUNCTION
SET


指定要被 SQL/Protect 監管的帳號,然後列舉一下狀態
edb=# CREATE USER someuser PASSWORD 'password';
CREATE ROLE
edb=# SET search_path TO sqlprotect;
SET
edb=# SELECT protect_role('someuser');
 protect_role
--------------
 
(1 row)

edb=# SELECT * FROM edb_sql_protect;
 dbid  | roleid  | protect_relations | allow_utility_cmds | allow_tautology | allow_empty_dml 
-------+---------+-------------------+--------------------+-----------------+-----------------
 14845 | 2877024 | t                 | f                  | f               | f
(1 row)

上面可以看到,目前這個帳號受到控制的有
  1. 限制表格存取(僅允許學習模式時紀錄到的表格)(protect_relations
  2. 執行 DDL(allow_utility_cmds
  3. 防止全真條件語句(allow_tautology
  4. 沒有 WHERE 條件限制的 DML(allow_empty_dml


在這裡提醒一點,Super user 帳號無法被 SQL/Protect 模組列管
edb=# SELECT protect_role('enterprisedb');    
ERROR:  super user can not be a protected user



接著建立資訊以便供 SQL/Protect 學習。

  • 測試表和資料
edb=# CREATE TABLE public.protect_tbl(i int,str text);
CREATE TABLE
edb=# DO $$
edb$# BEGIN
edb$#   FOR a IN 1..1000 LOOP
edb$#   INSERT INTO protect_tbl VALUES (a, 'ooooooooooooooo');
edb$#   END LOOP;
edb$# END $$;
DO
edb=# GRANT ALL ON protect_tbl TO someuser;
GRANT

  • 用該受監控帳號,進行他的正常活動
edb=# \c edb someuser
Password for user someuser:
You are now connected to database "edb" as user "someuser".
edb=> INSERT INTO protect_tbl(i, str) VALUES(2000,'hello');
NOTICE:  SQLPROTECT: Learned relation: 2880349
INSERT 0 1
edb=> UPDATE protect_tbl SET str = 'happy' WHERE i = 2;
UPDATE 1
edb=> SELECT * FROM protect_tbl WHERE i = 2;
 i |  str
---+-------
 2 | happy
(1 row)

edb=> \q


然後把學習模式關掉,設置成主動防護模式
bash-$ sed -e "s@edb_sql_protect.level = learn@edb_sql_protect.level = active@g" \
    -i $PGDATA/postgresql.conf
bash-$ pg_ctl reload
server signaled


接著開始展示功能。
    1. 首先,是在 SQL 指令裡面,指定全真條件(Tautology)
    諸如 1=1 這種恆為真的條件,被惡意放在 SQL 的 WHERE 條件裡面,可能就會讓整個表格的資料都被影響到。
    edb=# \c edb someuser
    Password for user someuser:
    You are now connected to database "edb" as user "someuser".
    edb=> select * from public.protect_tbl where 1=1;
    ERROR:  SQLPROTECT: Illegal Query: tautology
    

    採集一下 SQL/Protect 紀錄
    edb=> \c edb enterprisedb
    Password for user enterprisedb: 
    You are now connected to database "edb" as user "enterprisedb".
    edb=# SELECT * FROM sqlprotect.edb_sql_protect_queries;
     username | ip_address | port | machine_name |         date_time         |                          query
    ----------+------------+------+--------------+---------------------------+---------------------------------------------------------
     someuser |            |      |              | 06-FEB-17 13:10:00 +00:00 | select * from public.protect_tbl where 1=1;
    (1 row)
    
    edb=#
      2. 另外 SQL/Protect 也避免受管控帳號執行 DDL
      edb=# \c edb someuser
      Password for user someuser:
      You are now connected to database "edb" as user "someuser".
      edb=> drop table public.protect_tbl;
      ERROR:  SQLPROTECT: This command type is illegal for this user
      edb=> truncate protect_tbl ;
      ERROR:  SQLPROTECT: This command type is illegal for this user
      edb=> create table public.test_tbl(i int);
      ERROR:  SQLPROTECT: This command type is illegal for this user
      

      採集 SQL/Protect 紀錄
      edb=> \c edb enterprisedb
      Password for user enterprisedb: 
      You are now connected to database "edb" as user "enterprisedb".
      edb=# SELECT * FROM sqlprotect.edb_sql_protect_queries;
       username | ip_address | port | machine_name |         date_time         |                          query
      ----------+------------+------+--------------+---------------------------+---------------------------------------------------------
       someuser |            |      |              | 06-FEB-17 13:10:00 +00:00 | select * from public.protect_tbl where 1=1;
       someuser |            |      |              | 06-FEB-17 13:12:00 +00:00 | drop table public.protect_tbl;
       someuser |            |      |              | 06-FEB-17 13:41:00 +00:00 | truncate protect_tbl ;
       someuser |            |      |              | 06-FEB-17 13:26:00 +00:00 | create table public.test_tbl(i int);
      (4 rows)
      
      edb=#
      

      3. 最後,針對 UPDATE 和 DELETE 這兩種 DML,能夠避免沒有附上限制條件的狀況下被執行。以下把 DELETE 相關的狀況都執行一次:
      edb=> DELETE FROM protect_tbl WHERE i=1;
      DELETE 1
      edb=> DELETE FROM protect_tbl WHERE 1=1;
      ERROR:  SQLPROTECT: Illegal Query: tautology
      edb=> DELETE FROM protect_tbl;
      ERROR:  SQLPROTECT: Illegal Query: empty DML
      edb=> DELETE FROM protect_tbl WHERE str IS NOT NULL;
      DELETE 1004
      

      進資料庫確認一下,並採集 SQL/Protect 紀錄
      edb=> \c edb enterprisedb
      Password for user enterprisedb: 
      You are now connected to database "edb" as user "enterprisedb".
      edb=# SELECT * FROM sqlprotect.edb_sql_protect_queries;
       username | ip_address | port | machine_name |         date_time         |                          query
      ----------+------------+------+--------------+---------------------------+---------------------------------------------------------
       someuser |            |      |              | 06-FEB-17 13:10:00 +00:00 | select * from public.protect_tbl where 1=1;
       someuser |            |      |              | 06-FEB-17 13:12:00 +00:00 | drop table public.protect_tbl;
       someuser |            |      |              | 06-FEB-17 13:41:00 +00:00 | truncate protect_tbl ;
       someuser |            |      |              | 06-FEB-17 13:26:00 +00:00 | create table public.test_tbl(i int);
       someuser |            |      |              | 06-FEB-17 13:29:00 +00:00 | DELETE FROM protect_tbl WHERE 1=1;
       someuser |            |      |              | 06-FEB-17 13:30:00 +00:00 | DELETE FROM protect_tbl;
      (6 rows)
      
      edb=#
      

      SQL/Protect 功能目前到這裡展示完畢。


      此外,當資料庫要作 pg_dump 備份或還原,要保有既有的 SQL/Protect 樣式,請參考使用手冊


      截至 9.5 版為止,發現 SQL/Protect 的問題:
      • SQL/Protect 沒辦法阻擋 SELECT * FROM 這個全表格的查詢。
      不過,這個功能可以透過企業版的 Virtual Private Database 功能,或是 PostgreSQL 9.5 版之後引入的 Row-Level Security 功能來達成相似功能。


      • SQL/Protect 沒辦法避免稍微奇怪一點的限制條件。例如,WHERE str IS NOT NULL; 這種條件。
      要阻擋到這種層級,就必須要限制「允許」執行的 SQL 的模式的樣子。這可能就需要類似 sql_firewall 的社群專案達成了。

      • SQL/Protect 的 Utility Command 基本上把受管控帳號的行為限制在 SELECT,INSERT,UPDATE,和 DELETE:像是 COPY 指令也不算數。
      edb=> copy protect_tbl TO STDOUT (DELIMITER '|'); 
      ERROR:  SQLPROTECT: This command type is illegal for this user
      

      後記
      聽說現在的開發架構都比較能夠在開發上避免 SQL Injection 的樣子,我想這個「資料庫防火牆」功能就當成買保險的樣子來用就好了~


      參考資料:




      幾個對於 PostgreSQL 檢查 SQL Injection 的 Cheet Sheet


      有關 SQL Injection 的 xkcd 笑話~ xkcd: Exploits of a Mom(附贈解說篇 Little Bobby Tables - explain xkcd

      sql_firewall 專案:相關介紹 A Hacker's Diary: sql_firewall: a SQL Firewall Extension for PostgreSQL。設計上也是分成三種模式,功能與 SQL/Protect 不同,以學習 Query Statement 達成防堵 SQL Injection 功能。
      不過截至 0.8.1 版,這個模組會全域都有作用,不太適合正式使用。


      另外,有一個「好像」相似的工具,叫做 GreenSQL,只是看來沒在維護了

      沒有留言:

      張貼留言