簡體   English   中英

處理動態表/列名時如何防止SQL注入?

[英]How to prevent SQL injection when dealing with dynamic table/column names?

我正在使用 jdbc PreparedStatement 進行數據插入。

Statement stmt = conn.prepareStatement(
"INESRT INTO" + tablename+ "("+columnString+") VALUES (?,?,?)");

tablenamecolumnString是動態生成的。

我試圖參數化 tablename 和 columnString,但它們只會解析為諸如 'tablename' 之類的東西,這將違反語法。

我在網上找到了建議我查找數據庫以檢查有效的表名/列字符串,並將其緩存在某處(可能是哈希集)以用於另一個查詢,但我正在尋找更好的性能/快速破解來解決問題,也許是一個可以解決問題的字符串驗證器/正則表達式。

有沒有人遇到過這個問題,你是如何解決的?

我不是一個 java 人,所以,只是一個理論。

您可以格式化動態添加的標識符或它們列入白名單

第二種選擇要好得多。 因為

  • 大多數開發人員對標識符不夠熟悉,無法正確格式化它們。 比如說,引用第一個評論中提供的標識符,根本不會使其受到保護。
  • 可能還有另一個攻擊向量,不完全是注入,但類似:假設您的表中有一個列,不允許普通用戶 - 例如,稱為“管理員”。 通過使用來自客戶端的數據動態構建columnString ,打造特權升級是小菜一碟。

因此,事先在您的代碼中列出所有可能(和允許)的變體,然后根據它驗證輸入的值,將是最好的。

columnString - 由單獨的列名組成。 因此,為了保護它,必須對照白名單驗證每個單獨的列名稱,然后從它們組裝一個最終的columnString

創建一個為您生成 sql 字符串的方法:

private static final String template = "insert into %s (%s) values (%s)";

private String buildStmt(String tblName, String ... colNames) {
    StringJoiner colNamesJoiner = new StringJoiner(",");
    StringJoiner paramsJoiner = new StringJoiner(",");
    Arrays.stream(colNames).forEach(colName -> {
        colNamesJoiner.add(colName);
        paramsJoiner.add("?");
    });
    return String.format(template, tblName, colNamesJoiner.toString(), paramsJoiner.toString());
}

然后用它...

Statement stmt = conn.prepareStatement(buildStmt(tablename, [your column names]));

作為對@Anders 回答的詳細說明,不要直接使用輸入參數作為名稱,而是保留一個將一組允許的輸入映射到實際表名的屬性文件(或數據庫表)。

這樣,任何無效的名稱都不會導致有效的 SQL(並且可以在生成任何 SQL 之前被捕獲)並且實際名稱在應用程序之外永遠不會知道,因此更難猜測什么是有效的 SQL 語句。

我認為,最好的方法是從數據庫或其他非用戶輸入中獲取表名和列名,然后在准備好的語句中使用參數。

我們可以應用多種解決方案。

1) 白名單輸入驗證

String tableName;
switch(PARAM):
   case "Value1": tableName = "fooTable";
                  break;
   case "Value2": tableName = "barTable";
              break;
   ...

   default      : throw new InputValidationException("unexpected value provided for table name");
  • 通過對 tableName 執行此輸入驗證,將只允許查詢中的指定表,因此它將防止 sql 注入攻擊。

2) 將您的動態 columnName(s) 或 tableName(s) 與特殊字符綁定,如下所示

例如:

  • 對於 Mysql:使用后退代碼 (`)

    從`tableName`中選擇`columnName`;

  • 對於 MSSQL:使用雙代碼(" 或 [ ] )

    從“tableName”中選擇“columnName”;
    或者
    從 [tableName] 中選擇 [columnName];

注意:在執行此操作之前,您應該使用此特殊字符(`、"、[、])清理您的數據

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM