如何為數據庫中的位圖添加動態水印
譯文譯者 | 李睿
審校 | 重樓
概要
許多數據庫存儲了以blob或文件形式保存的位圖,其中包括照片、文檔掃描、醫學圖像等。當這些位圖被各種數據庫客戶端和應用程序檢索時,為了日后的識別和追蹤,有時需要在檢索時為它們添加唯一的水印。在某些情況下,人們甚至希望這些水印是不可見的。
這種動態的位圖操作可以很容易地通過可編程的數據庫代理完成,無需對持久化存儲的位圖進行任何修改。
這種方法有以下好處:
- 水印可以為每次檢索進行定制,并可以包含有關日期、時間、用戶身份、IP地址等信息。
- 圖像處理由代理完成,這樣不會給數據庫帶來額外的負載。
- 不需要更改數據庫或數據庫客戶端。
最終結果
給定一個存儲在數據庫中的位圖,例如下圖:
可編程的數據庫代理可以在發送到客戶端的途中修改位圖,以包含任何所需信息的水印,例如下圖:
工作原理
這個架構很簡單:它并不依賴于傳統的數據庫客戶端與服務器之間的直接連接,而是采用了一種不同的方式:
客戶端連接到代理,代理連接到服務器:
然后,代理可以在檢索位圖時根據需要處理這些位圖。
例如,它只能為某些位圖添加水印,也可以根據具體情況使用不同樣式的水印。存儲在數據庫中的位圖完全不受影響:它們在轉發給客戶端時被動態修改。
優點
- 客戶和數據庫對此一無所知——這對他們來說是完全透明的。
- 每當圖像被檢索時,都可以為其添加獨特的水印(例如,日期/時間、用戶名、客戶端的IP地址等)。
- 數據庫服務器上不會增加額外的負載。
缺點
- 由于添加了代理,系統變得更加復雜。
- 延遲會增加(通常是適度的),主要取決于圖像的大小,但應該與替代方案進行比較。
實例
使用代理,可以創建一個簡單的過濾器,為某些位圖添加水印。
如果假設數據庫包含一個名為images的表,其中包含一個名為bitmap的列,類型為blob或varbinary(取決于數據庫),可以在代理中使用以下參數創建一個結果集過濾器:
Query pattern: regex:select.*from.*images.*
以下一些JavaScript代碼(也使用底層Java引擎):
JavaScript
// Get the value of the bitmap column as a byte stream
let stream = context.packet.getJavaStream("bitmap");
if (stream === null) {
return;
}
// The text to use as watermark
const now = new Date();
const watermark = "Retrieved by " + context.connectionContext.userName +
" on " + now.getFullYear() + "/" + (now.getMonth()+1) + "/" + now.getDate();
// Read the bitmap
const ImageIO = Java.type("javax.imageio.ImageIO");
let img = ImageIO.read(stream);
// Create the Graphics to draw the text
let g = img.createGraphics();
const Color = Java.type("java.awt.Color");
g.setColor(new Color(255, 255, 0, 150));
const Font = Java.type("java.awt.Font");
g.setFont(new Font("sans-serif", Font.BOLD, 16));
// Draw the text at the bottom of the bitmap
let textRect = textFont.getStringBounds(watermark, g.getFontRenderContext());
g.drawString(watermark, (img.getWidth() / 2) - (textRect.getWidth() / 2),
img.getHeight() - (textRect.getHeight() / 2));
// Write the bitmap to the column value
const ByteArrayOutputStream = Java.type("java.io.ByteArrayOutputStream");
let outStream = new ByteArrayOutputStream();
ImageIO.write(img, "png", outStream);
context.packet.bitmap = outStream.toByteArray();
啟用這個過濾器之后,從這個表中檢索的位圖將包括一個水印,其中包含數據庫用戶的名稱和時間戳。
數據庫永遠不會受到影響:存儲在數據庫中的位圖完全不變,它們是在發送給客戶端時即時修改的。
顯然,可以選擇性地為位圖添加水印,可以根據任何相關因素更改水印的文本,還可以通過字體、顏色、定位、透明度等因素添加水印。有關詳細信息,參見這個示例。
秘密水印
在某些情況下,可能需要以肉眼不可見的方式標記位圖。一種簡單的方法是編輯圖像的元數據,但如果需要更微妙的東西,可以使用隱寫術在位圖中分發秘密消息,使其難以被檢測到。
可以修改以上的示例以使用Adumbra庫:
// Get the value of the bitmap column as a byte stream
let inStream = context.packet.getJavaStream("bitmap");
if (inStream === null) {
return;
}
// The hidden message
const now = new Date();
const message = "Retrieved by " + context.connectionContext.userName +
" on " + now.getFullYear() + "/" + (now.getMonth()+1) + "/" + now.getDate();
const messageBytes = context.utils.getUTF8BytesForString(message);
const keyBytes = context.utils.getUTF8BytesForString("This is my secret key");
// Hide the message in the bitmap
const Encoder = Java.type("com.galliumdata.adumbra.Encoder");
const ByteArrayOutputStream = Java.type("java.io.ByteArrayOutputStream");
let outStream = new ByteArrayOutputStream();
let encoder = new Encoder(1);
encoder.encode(inStream, outStream, "png", messageBytes, keyBytes);
context.packet.bitmap = outStream.toByteArray();
有了這一點,提供給客戶端修改后的位圖將包含一個難以檢測的秘密水印,且在沒有密鑰的情況下幾乎無法提取。
水印技術還有哪些用途?
這種水印技術也可以應用于位圖以外的文檔:
- 像PDF和MS Word這樣的文檔可以被賦予一些額外的元數據,或者它們可以被賦予一個可見或不可見的水印??梢詤⒖糚DF文檔的這個示例。
- 所有的文本文檔可以巧妙地使用水印技術進行標記,例如改變間距、拼寫、布局、字體和顏色、零寬度字符等。
- 所有能夠在不失去任何重要意義的情況下進行微小更改的數字文檔,例如位圖、音頻文件和樣本集,都能夠以類似的方式進行修改。
- 事實上,整個數據集可以通過巧妙地修改數據的一些非關鍵方面來添加水印,從而有可能在以后識別這些數據集并確切地知道它們的來源。這超出了本文的范圍,但是有許多方法可以使數據追溯到其起源。
結論
當需要從數據庫中檢索位圖或文檔時,并且每次檢索都需要一個定制的水印,這里展示的技術是一種可靠的方法,它避免了給數據庫帶來任何額外的負擔,并且不需要對客戶端或服務器進行任何更改。
原文標題:Dynamic Watermarking of Bitmaps in Databases,作者:Max Tardiveau