在 Dreamweaver 8.0.2 和 CS3 的標準資料錄集內使用動態 SQL 參數

上次更新時間 2020年6月3日

涵蓋內容

解決資料庫安全性漏洞

為了提高 Dreamweaver 8.0.2 和 CS3 的安全性,我們已移除在標準資料錄集內使用動態 SQL 的功能。 原始程式碼會使資料庫容易受到 SQL 插入的攻擊。 使用為 ASP_VBS 和 ColdFusion 伺服器模型備妥的陳述式已解決安全性漏洞。 原本必須在套用第一個資料錄集時將函數新增到頁面,然後在 PHP_MySQL 頁面上從所有資料錄集呼叫該函數。 此技巧類似於備妥的 ASP_VBS 和 ColdFusion 陳述式。

為了填補安全性漏洞,我們會捨棄部分彈性來加強安全性。 對於已經在開發或打算開發擴充功能且需要處理動態 SQL 查詢的開發人員,以及想要自訂資料錄集以接受動態參數的一般使用者,我們希望提供從 Dreamweaver 內部編輯這些自訂資料錄集的功能。

SQL 插入和防止方法

SQL 插入是針對安全性漏洞攻擊的一種技巧,發生在應用程式的資料庫層。 使用者輸入針對內嵌於 SQL 陳述式的字串常值逸出字元進行了錯誤的篩選,或是使用者輸入並非強型別且因此無預警執行時,就會出現此漏洞。 事實上,某個程式或指令碼語言內嵌在另一個程式或指令碼語言時,這個漏洞實例就是可能會發生的較常見類別。 如需其他資訊,請造訪 Wikipedia

標準 SQL 查詢範例:

SELECT * FROM company_com WHERE name_com = '$companyName'

在上方的範例中,$companyName 變數是從輸入表格欄位讀取,因此使用者能夠完全控制要提交的值。 在最好的情況下,使用者在對應公司名稱的輸入欄位中輸入「Adobe」(不包含括號) 時,所提供的 SQL 查詢看起來會類似這樣:

SELECT * FROM company_com WHERE name_com = 'Adobe'

但是,有時候使用者可能會想要入侵網站並竊取敏感資訊和/或破壞資料庫。 考量到上方的範例,輸入「blabla' OR '1'='1」字串 (不包含前後括號) 將能夠輕鬆完成 SQL 插入。 透過提供此字元序列,使用者將能夠擁有對所有公司的存取權限,而不是單一公司。 所執行的實際 SQL 為:

SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'

此外,如果使用者想要刪除整個 company_com 表,只要傳遞「x'; DROP TABLE company_com; --」字串 (不包含前後括號) 即可輕鬆完成。 以下是完整的 SQL 查詢:

SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'

使用以下其中一個措施可避免網站遭到 SQL 插入:

  • 自動移除所有其他不在指定範圍內的字元,將輸入限制為最小的允許字元組。
  • 在 SQL 查詢中使用時,逸出所有可能會產生問題的字員 (例如單括號)。
  • 使用已備妥的陳述式。
  • 使用資料庫存取權限。
  • 使用預存程序。

建議使用已備妥的陳述式,詳細說明請參閱使用已備妥的陳述式。 Dreamweaver 8.0.2 和 CS3 對 ASP_VBS、ASP_JavaScript、Cold Fusion 和 JSP 伺服器模型使用已備妥的陳述式方法,並對 PHP_MySQL 伺服器模型使用逸出使用者輸入方法。 如需 SQL 插入式攻擊的其他資訊,請參閱 SQL 插入式攻擊範例

使用已備妥的陳述式

Dreamweaver 8.0.2 和 CS 會使用已備妥的陳述式,因為此解決方案能夠向我們保證,以指定值替換實際 SQL 參數 (可能透過 URL 傳送) 會在伺服器上完成,而不是在頁面上完成。 已備妥的陳述式也能夠保證傳遞到 SQL 本身的值具有適當的資料類型並使用適當的逸出 (如果參數屬於類型 int,則使用者將無法提交字母;如果參數屬於類型 text,則整個傳遞的字串將會適當地逸出,且使用者將沒有機會執行 SQL 插入)。

適用於 ASP_VBS 和 ASP_JS 伺服器模型的已備妥陳述式

對於 ASP_VBS 和 ASP_JS,我們需要 ADODB 資料庫層。 以下範例顯示使用動態 SQL 參數來篩選結果的簡易 ASP_VBS 資料錄集:

... <% Dim Recordset1__MMColParam Recordset1__MMColParam = "1" If (Request.QueryString("id_com") <> "") Then Recordset1__MMColParam = Request.QueryString("id_com") End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com WHERE id_com = ?" Recordset1_cmd.Prepared = true Recordset1_cmd.Parameters.Append Recordset1_cmd.CreateParameter("param1", 5, 1, -1, Recordset1__MMColParam) ' adDouble Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...

適用於 ColdFusion 伺服器模型的已備妥陳述式

ColdFusion 為已備妥的陳述式提供內建支援。 以下範例顯示使用動態 SQL 參數來篩選結果的簡易 ColdFusion 資料錄集:

... <cfparam name="URL.id_com" default="1"> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com WHERE id_com = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric"> </cfquery> ...

適用於 PHP_MySQL 伺服器模型的模擬已備妥陳述式

適用於 PHP 的已備妥陳述式是透過 MySQL 4.1 提供,但因為我們也希望支援先前版本的 MySQL,所以決定實作功能實際上相同的自訂函數。 以下範例顯示使用動態 SQL 參數來篩選結果的簡易 PHP 資料錄集:

... <?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } $colname_Recordset1 = "-1"; if (isset($_GET['id_com'])) { $colname_Recordset1 = $_GET['id_com']; } mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = sprintf("SELECT * FROM company_com WHERE id_com = %s", GetSQLValueString($colname_Recordset1, "int")); $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...

允許在標準資料錄集內使用動態參數

所以,如何能夠在防止 SQL 插入的同時,仍允許在 Dreamweaver 中使用動態參數,並透過「伺服器行為」面板使所產生的程式碼維持可供編輯呢? 解決方案是將動態參數插入 SQL 查詢本身,而不是以傳統的 SQL 參數加以傳遞 (也就是透過已備妥的陳述式來逸出或附加到 SQL)。

重要:使用者無法使用 Dreamweaver 8.0.2 或 CS3 隨附的物件和伺服器行為來產生這類程式碼 (例如:透過「伺服器行為」面板或「插入」列來插入資料錄集)。 此解決方案需要使用者手動編輯程式碼,或是安裝建立此類程式碼的協力廠商擴充功能。 寫入安全程式碼的責任屬於使用者或協力廠商軟體開發人員。

此方法的目的在於讓資深的開發人員建立動態 SQL 查詢。 不當使用此解決方案會讓駭客有機會獲得不必要的權限和/或入侵網頁及資料庫。 請參閱 SQL 插入和防止方法來減少 SQL 插入的風險。

我們發現可使用此解決方案的主要案例為:

  • 手動建立/更新 SQL 查詢以接受動態參數。
  • 建立/升級現有擴充功能以產生動態參數。

我們將在接下來的段落中詳細說明這兩個使用案例。

手動建立/更新 SQL 查詢以接受動態參數,同時將資料錄集維持為可從 Dreamweaver 內部編輯

在接下來的範例中,使用者想要新增排序功能到動態表中。 他決定重新載入頁面,同時在 URL 中傳遞要使用的檔案和排序方向 (遞增或遞減)。 初始的 URL 範例為:

http://www.mydomain.com/index.php?sortCol=name_com&sortDir=asc

需要手動編碼才能透過伺服器行為在 Dreamweaver 中完成此工作。 以下程式碼範例顯示了必須對每個伺服器模型進行的變更。

重要:請注意,以下任一範例均未對 SQL 插入進行驗證。 這些範例所扮演的角色純粹是為了教育用途,不應按照原樣用於生產。 這些範例並不具備萬無一失的安全性,因此無法如此簡單地使用。 您必須對 SQL 插入進行防護。 如需其他資訊,請參閱 SQL 插入和防止方法

ASP_VBS

之前:

... <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...

之後:

... <% Dim orderBy: orderBy = "" If (Request.QueryString("sortCol") <> "") Then orderBy = "ORDER BY " & Request.QueryString("id_com") If (Request.QueryString("sortDir") <> "") Then orderBy = orderBy & " " & Request.QueryString("sortDir") End If End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com " & orderBy & "" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...

ColdFusion

之前:

... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...

之後:

... <cfparam name="URL.sortCol" default=""> <cfparam name="URL.sortDir" default="ASC"> <cfset orderBy=""> <cfif (#URL.sortCol# NEQ "")> <cfset orderBy="ORDER BY #URL.sortCol# #URL.sortDir#"> </cfif> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com #orderBy# </cfquery> ...

PHP_MySQL

之前:

... <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com"; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...

之後:

... <?php $orderBy = ""; if (isset($_GET['sortCol'])) { $orderBy = "ORDER BY " . $_GET['sortCol']; if (isset($_GET['sortDir'])) { $orderBy .= " " . $_GET['sortDir']; } } ?> <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com " . $orderBy . ""; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...

建立/升級現有擴充功能以產生動態參數,同時將資料錄集維持為可從 Dreamweaver 內部編輯

現在,請考慮與上方相同的情境,但這次使用者想要建立的是會產生適當程式碼的擴充功能,而不需要結合內建功能和手動編輯。 新的擴充功能應該會對 ASP_VBS、ColdFusion 和 PHP_MySQL 伺服器模型產生正確的程式碼。

重要:在以下區段中,我們只會專注在產生會使用來自 URL 參數之值的程式碼,以建立動態 SQL 查詢。 主要重點在於產生程式碼,因此 Dreamweaver 仍然會加以識別,也能夠在標準 Dreamweaver 介面中加以編輯。 此教學課程不會處理輸入資料的驗證 (透過 URL 參數) 或保護最終的 SQL 以防止任何 SQL 插入嘗試,因為此類程式碼的複雜度超出本教學課程的範圍。 開發人員僅負責保護最終的 SQL,使其免於受到 SQL 插入的攻擊。

我們提供了一個示範擴充功能,能夠將連線和表做為輸入,並產生顯示所有記錄的動態表。 它也會更新所產生的 SQL,使其含有 orderBy 變數,如上方範例所示。 此擴充功能是專為 Dreamweaver 8.0.2 和 CS3 所設計。 所產生的程式碼不具備萬無一失的安全性,因為目標是使用標準 Dreamweaver 介面來建立含有維持可編輯之動態參數的資料錄集。 擴充功能來源程式碼位於使用者的設定檔案夾中。

在下方所有程式碼範例中,都新增了特別標示的區段來啟用透過 URL 參數 sortColsortDir 排序的功能。

ASP_VBS

為 ASP_VBS 伺服器模型產生適當 SQL 查詢的程式碼位在使用者設定檔案夾的「[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js」檔案中。 顯著的變更在行 130:

... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...

MyDynamicTable ASP_VBS 伺服器行為的參與者檔案會包括一個額外的參與者,因此會對頁面新增「orderBy」變數定義:

<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="connectionref_statement" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="recordset_main" /> <groupParticipant name="repeatedRegion_init2" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="recordset_close" /> </groupParticipants> </group>

ColdFusion

使用者設定檔案夾中的相同「USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js」也含有 ColdFusion 伺服器模型的程式碼,此案例的相關變更在行 138:

... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...

MyDynamicTable ColdFusion 伺服器行為的參與者檔案會包括一個額外的參與者,因此會對頁面新增「orderBy」變數定義:

<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="RepeatedRegion_pageNum" /> <groupParticipant name="RepeatedRegion_maxRows" /> <groupParticipant name="RepeatedRegion_startRow" /> <groupParticipant name="RepeatedRegion_endRow" /> <groupParticipant name="RepeatedRegion_totalPages" /> </groupParticipants> </group>

PHP_MySQL

擴充功能中的最後一個伺服器模型是 PHP_MySQL。 使用者設定檔案夾中「[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js」檔案的相關變更在行 122。

... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...

MyDynamicTable PHP_MySQL 伺服器行為的參與者檔案會包括一個額外的參與者,因此會對頁面新增「orderBy」變數定義:

<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="Connection_include" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="EditOps_SQLValueString" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="Recordset_close" /> </groupParticipants> </group>

優點

上方所述的方法有多個優勢,包括:

  • 開發人員更能夠更進階地運用 SQL 插入的漏洞來建構動態上更為複雜的 SQL。
  • 所建立的資料錄集仍然可以從 Dreamweaver 內部編輯。
  • 整合到透過動態 SQL 參數產生資料錄集的現有 Dreamweaver 擴充功能並不困難。
  • Dreamweaver 產生的預設程式碼會保持不變,因此不需要動態查詢的開發人員 (及其客戶) 可以感到安心,因為 Dreamweaver 寫入的程式碼是安全的。

缺點

因為 Dreamweaver 的設計預設不支援此方法,因此也有一些缺點,包括:

  • 使用此文件所述的方法產生的程式碼很容易受到 SQL 插入攻擊,因此需要透過其他方式加以保護。 此類保護方法的清單包括但不限於:
    • 確保一般使用者無法使用 SQL 插入方法來入侵網頁和/或資料庫。
    • 在引數達到實際 SQL 之前加以逸出。
    • 即便使用者得以入侵您的程式碼,也請確保他們無法竊取敏感資訊和/或無法入侵資料庫。
  • 有兩個已知問題必須直到發行下一個版本的 Dreamweaver 時才能修正:
    • ColdFusion:在編輯並重新套用時,簡易資料錄集使用者介面會將變數名稱前後的雜湊從 SQL 查詢中移除
    • ColdFusion、ASP_VBS、ASP_JS:SQL 查詢含有變數名稱時,進階資料錄集使用者介面的「測試」按鈕無法使用

尋找使用者的設定檔案夾

  • Windows XP

    C:\Documents and Settings\[username]\Application Data\Adobe\Dreamweaver 9\Configuration
  • Windows Vista

    C:\Users\[username]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
  • Mac OS 10.4.x

    /Users/[username]/Library/Application\ Support/Adobe/Dreamweaver\ 9/Configuration