PostgreSQL數(shù)據(jù)庫加密方法

作為應(yīng)用最廣泛的信息存儲和處理系統(tǒng),數(shù)據(jù)庫中存在大量敏感數(shù)據(jù),如何防止數(shù)據(jù)被竊取和篡改是重中之重。加密技術(shù)是提高數(shù)據(jù)庫安全的一個(gè)重要手段,在對數(shù)據(jù)庫中存儲數(shù)據(jù)進(jìn)行加密時(shí),需要結(jié)合它們的特點(diǎn),對加密算法、加密粒度以及加密方式進(jìn)行合理選擇。比如,在選擇加密算法時(shí),對加密尤其是解密速度要求比較快,不能因?yàn)榧?解密過程而導(dǎo)致系統(tǒng)性能大幅度下降。其次,應(yīng)當(dāng)支持靈活的加密粒度。根據(jù)用戶的需要,能夠選擇對數(shù)據(jù)庫、表、記錄、字段、數(shù)據(jù)項(xiàng)進(jìn)行加密。同時(shí),還應(yīng)結(jié)合目前DBMS選擇適當(dāng)加密方式。

但不是什么加密方法和方式都是適合數(shù)據(jù)庫的,在加密數(shù)據(jù)的同時(shí),也會帶來一些相關(guān)問題,如果處理不好,不僅會降低數(shù)據(jù)庫的安全性,而且還會帶來其它如性能的影響。

本文簡要介紹和分析了在一個(gè)基于開源數(shù)據(jù)庫PostgreSQL的安全數(shù)據(jù)庫系統(tǒng)研究的基礎(chǔ)上,如何針對PostgreSQL數(shù)據(jù)庫的存儲數(shù)據(jù)的不同層次,對數(shù)據(jù)庫的文件(或表、頁)、記錄、字段等多個(gè)層次實(shí)現(xiàn)了加密。

數(shù)據(jù)庫不同加密粒度實(shí)現(xiàn)

按照PostgreSQL數(shù)據(jù)庫的結(jié)構(gòu)層次,數(shù)據(jù)庫的加密粒度可以分為相應(yīng)的數(shù)據(jù)庫級、表級、記錄級、字段級和數(shù)據(jù)項(xiàng)級。根據(jù)不同的應(yīng)用需要,選擇合適的加密粒度。

一、對整個(gè)數(shù)據(jù)庫加密

整個(gè)數(shù)據(jù)庫加密,就是對數(shù)據(jù)庫中所有的系統(tǒng)表、數(shù)據(jù)表、索引、視圖和存儲過程等進(jìn)行加密處理。這種加密方法簡單快捷,只需對相應(yīng)數(shù)據(jù)庫文件進(jìn)行加密處理即可,對于企業(yè)或者用戶簡單的備份整個(gè)數(shù)據(jù)庫,可以采取這種操作方便加密粒度。但是,使用中的數(shù)據(jù)庫如果采用這種方法則會非常不合適,因?yàn)閿?shù)據(jù)庫中的數(shù)據(jù)共享性高,會同時(shí)被多個(gè)用戶和應(yīng)用訪問使用,即使只需要查詢一條記錄,也需要對整個(gè)數(shù)據(jù)庫進(jìn)行解密,對系統(tǒng)性能會產(chǎn)生極大的影響。因此對此整個(gè)數(shù)據(jù)庫加密的方式我們不作過多的研究和討論。

二、 數(shù)據(jù)庫的表級(頁面)加密

安全數(shù)據(jù)庫將表分成兩大類,含加密字段的表和不含加密字段的表。對于加密的數(shù)據(jù)表則整個(gè)塊加密,否則不加密,且每塊使用一個(gè)密鑰。安全數(shù)據(jù)庫中,如果一個(gè)表中存放敏感數(shù)據(jù),那么這個(gè)表的每一個(gè)頁面的數(shù)據(jù)域在內(nèi)存中是明文,在外存中是密文。

表級加密的對象是數(shù)據(jù)庫中的表。與數(shù)據(jù)庫級加密比較,采用表級加密粒度,系統(tǒng)的查詢性能會有所改善,因?yàn)閷τ谖醇用鼙淼牟樵?與傳統(tǒng)查詢方法一樣,系統(tǒng)性能不會受到影響,對于加密表的查詢,只需要解密對應(yīng)的加密表,而不要解密整個(gè)數(shù)據(jù)庫。在實(shí)行表級加密時(shí),可以采用對存儲數(shù)據(jù)的磁盤塊(頁面)進(jìn)行加密。這種方法與DBMS集成時(shí),需要對DBMS內(nèi)部一些核心模塊進(jìn)行修改,包括對詞法分析器、解釋器和查詢執(zhí)行器的修改,這里我們對開放源代碼的數(shù)據(jù)庫管理系統(tǒng)PostgreSQL的源碼進(jìn)行修改。

1) 加解密數(shù)據(jù)結(jié)構(gòu)

加密函數(shù):

Char cipherpage(char *buffer,int size,BlockNumber blocknum, Relation reln);

解密函數(shù):

Char decipherpage(char *buffer,int size,BlockNumber blocknum, Relation reln);

輸入:

Buffer:加解密數(shù)據(jù)塊所在內(nèi)存緩沖區(qū);

Size:數(shù)據(jù)塊大小;

Blocknum:數(shù)據(jù)塊定位信息,需要寫入或讀出的數(shù)據(jù)塊;

Reln:記錄數(shù)據(jù)庫名,數(shù)據(jù)庫關(guān)系表,數(shù)據(jù)空間的信息;

返回值:

加解密成功后,返回?cái)?shù)據(jù)塊所在的內(nèi)存緩沖區(qū)。

2) 加解密流程的實(shí)現(xiàn)

當(dāng)DBMS將內(nèi)存中的頁面寫出到外存中時(shí),首先查詢安全字典,如果需要加密存儲,則用安全字典中當(dāng)前密鑰加密。在將數(shù)據(jù)從外存讀入內(nèi)存后,如果頁頭標(biāo)識頁面是加密過的(由函數(shù)iscryptic()進(jìn)行判斷),則取出相應(yīng)的密鑰解密。加密時(shí),只加密頁面中的數(shù)據(jù)域。這里也是由iscryptic()對進(jìn)行讀寫的關(guān)系表進(jìn)行判斷是否需要加解密,來對當(dāng)前的數(shù)據(jù)塊進(jìn)行加密或解密。解密時(shí),當(dāng)數(shù)據(jù)塊從外存讀入內(nèi)存時(shí),如果頁頭標(biāo)識頁面是加密過的,則從安全字典中取出相應(yīng)的密鑰解密數(shù)據(jù)域。加解密的密鑰通過主密鑰和頁塊的ID計(jì)算得出。

我們跟蹤數(shù)據(jù)的寫入和讀入流程,找到寫入和讀出數(shù)據(jù)塊(block)的位置,對該數(shù)據(jù)塊進(jìn)行加解密。

和對整個(gè)數(shù)據(jù)庫加密一樣,對數(shù)據(jù)庫進(jìn)行表級加密實(shí)現(xiàn)方法簡單,但是加密的粒度較粗,一張表中會含有大量不需要加密的數(shù)據(jù),這樣也會導(dǎo)致效率低下。

三、使用TOAST技術(shù)實(shí)現(xiàn)字段加密

使用TOAST技術(shù),將加密的字段從原始的數(shù)據(jù)中分離出來,單獨(dú)存放。按照此種處理方案,同一個(gè)表中的明文密文存放在不同的文件或頁面中。

首先對TOAST (The Oversized-Attribute Storage Technique,超大尺寸字段存儲技術(shù))進(jìn)行一個(gè)簡單的概述。

PostgreSQL 數(shù)據(jù)庫的頁面大小是固定的(通常是 8Kb),并且不允許行跨越多個(gè)頁面,因此不可能直接存儲非常大的字段值。為了突破這個(gè)限制,大的字段值被壓縮和/或打碎成多個(gè)物理行。(這些事情對用戶都是透明的)。這個(gè)技術(shù)的昵稱是 TOAST("切片面包之后最好的東西")。TOAST 代碼只有在準(zhǔn)備向某表中存儲超過 BLCKSZ/4 字節(jié)(通常是 2KB)的行的時(shí)候才會觸發(fā)。TOAST 代碼將在對應(yīng)于主表的TOAST表中壓縮和/或另存字段值,直到數(shù)值比 BLCKSZ/4 字節(jié)短,或者無法得到更好的結(jié)果的時(shí)候才停止。

1)字段加密的實(shí)現(xiàn)

借助于TOAST技術(shù)我們可以將TOAST 代碼的觸發(fā)時(shí)機(jī)移植到當(dāng)需要對某一數(shù)據(jù)庫表的字段進(jìn)行加密時(shí)。這樣主表中需要加密的字段中只是存儲實(shí)際已經(jīng)加密的字段數(shù)據(jù)所在位置的索引值,實(shí)現(xiàn)的關(guān)鍵技術(shù)就是學(xué)習(xí)TOAST技術(shù),這是實(shí)現(xiàn)的難點(diǎn)。

需要加密字段的數(shù)據(jù)在這里不是被分裂成(壓縮后)固定字節(jié)的塊后被存儲到相應(yīng)的TOAST表中,而是每個(gè)被加密后的字段數(shù)據(jù)塊都作為獨(dú)立的行在TOAST表里為所屬主表存儲。每個(gè)TOAST 表都有chunk_id字段(一個(gè)表示特定TOAST值的OID)、chunk_seq(一個(gè)序列號,存儲該塊在數(shù)值中的位置)、chunk_data(該塊實(shí)際的數(shù)據(jù))。在chunk_id 和chunk_seq上有一個(gè)唯一索引,提供對數(shù)值的快速檢索。因此,一個(gè)表示線外 TOAST 值的指針數(shù)據(jù)需要存儲要查閱的 TOAST 的OID和特定數(shù)值的OID(它的chunk_id)。這樣在主表中依據(jù)這些標(biāo)識就可以順利找到被加密的字段,解密后即可使用。

TOAST 過的字段的大體積數(shù)值只是在把結(jié)果集發(fā)送給客戶端的時(shí)候才抽出來。因此,主表要小得多,并且它的大部分行都存儲在共享緩沖區(qū)里,因此就可以不需要任何線外存儲。排序集也縮小了,并且排序?qū)⒏嗟卦趦?nèi)存里完成。TOAST 表存儲將近一半大小的裸數(shù)據(jù),而主表只包含全部數(shù)據(jù)的 10%,這與在一個(gè)非 TOAST 的表對比起來,沒有任何運(yùn)行時(shí)的區(qū)別。因此我們不必?fù)?dān)心TOAST帶來性能上的影響。

與前面幾種數(shù)據(jù)庫加密方法不同,對數(shù)據(jù)庫進(jìn)行字段加密不需要對所有的數(shù)據(jù)進(jìn)行加密,加密的粒度更小,對于不需要訪問到的記錄,完全不需要進(jìn)行任何操作,所以使用起來效率會高一些。但是由于每一個(gè)字段都必須有一個(gè)密鑰與之匹配,因此產(chǎn)生和管理記錄密鑰比較復(fù)雜。以記錄為單位的加密分析與以字段為單位的加密情況相似。這里就不再進(jìn)行敘述。

四、其他方式

在PostgreSQL數(shù)據(jù)庫中,所有的記錄都被構(gòu)造成一個(gè)TUPLE。在構(gòu)造的過程中,將加密列上的屬性進(jìn)行加密,保障內(nèi)存中的數(shù)據(jù)是加密的,當(dāng)一條記錄被引用時(shí)根據(jù)TUPLE中的標(biāo)識對其相應(yīng)的字段進(jìn)行解密。

本文基于PostgreSQL數(shù)據(jù)庫自身特點(diǎn)簡要討論了幾種數(shù)據(jù)庫不同加密粒度式,用戶根據(jù)應(yīng)用場合的不同, 可分別選用以數(shù)據(jù)庫表、記錄、字段作為加密基本單位的方案。由于層次不同, 這三個(gè)方案的適用環(huán)境與實(shí)現(xiàn)難度各不相同。一般而言, 加密單位越小, 適用范圍越廣, 但實(shí)現(xiàn)時(shí)的困難也越大。而數(shù)據(jù)庫的安全性仍然不斷面臨新的安全技術(shù)挑戰(zhàn),這有待我們?nèi)ミM(jìn)一步解決。

知識點(diǎn):

PostgreSQL是一個(gè)包含關(guān)系模型和支持SQL標(biāo)準(zhǔn)查詢語言的DBMS(數(shù)據(jù)庫管理系統(tǒng))。