安全中国首页 > 文章中心 > 安全防护
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:download.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

LINQ-對付SQL Injection的“免費補洞策略”(图)

更新时间:2008-7-11 0:11:27
责任编辑:果果龙
热 点:

作者:黃忠成

一連串的 Mass SQL Injection 攻擊,讓我們回憶起數年前的 SQL Injection 攻擊,多年後的今天,我們仍深陷於同樣的危機中,本文詳述 SQL Injection 的歷史、肇因、解決及偵測方法,更為讀者們引介全新、更加安全的防堵 SQL Injection 策略。

什麼是 SQL Injection?

SQL Injection,中譯為 SQL 注入,更為人知的名稱是【資料隱碼攻擊】,意指開發人員於撰寫網頁應用程式之際,貪圖一時方便或是依循前人的慣性寫法而開啟的一道門。在數年前,一次大型 的隱碼攻擊行動,喚起了所有網站擁有者及設計人員的防駭之心,讓我們認知到,網站是一個曝露在所有人面前的公共園地,其安全性不容忽視!在那次的攻擊行動 中,有數千個網站遭到同一種手法入侵,洩露的資料及因入侵所損失的金額難以估計,而起源竟只是程式設計師的慣性及疏於防範,而我們都曾經是其中一份子。

那具體上,什麼是 SQL Injection 呢?其實說穿了很簡單,就是透過網頁上的輸入區域 (INPUT 如文字輸入框,或是 URL 中的查詢字串),將特定的 SQL 語句透過網頁送往資料庫執行。以一個登入網頁為例,在設計登入網頁時,我們會放兩個 TextBox 控件,分別讓使用者填入使用者 ID 及密碼,類似畫面如下:

圖 1:

在使用者按下登入按鈕後,我們將其輸入的資訊送往資料庫,驗證使用者輸入的登入資訊是否正確:

using System;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Data.SqlClient;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        if (ValidateUser(TextBox1.Text, TextBox2.Text))
            Label1.Text = "歡迎你";
        else
            Label1.Text = "登入失敗";
    }

    private bool ValidateUser(string userName, string password)
    {
        SqlConnection conn = new SqlConnection(
             "Data Source=JEFFRAY;Initial Catalog=Northwind;Integrated Security=True");
        using (conn)
        {
            SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM USERS WHERE USER_ID = '"
                + userName + "' AND PASSWORD = '" + password + "'", conn);
            conn.Open();
            return ((int)cmd.ExecuteScalar() > 0);
        }
    }
}

當你寫下這些程式碼時,已經開啟了 SQL Injection 的大門了,只要使用者於登入時,填入下圖的資訊,那麼不管 ID 密碼是什麼,一律可以登入系統。

圖 2:

這是為什麼呢?很簡單,起因於下面這行程式碼:

SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM USERS WHERE USER_ID = '"
                + userName + "' AND PASSWORD = '" + password + "'", conn);

我們使用傳統 ASP 常見的手法,以組裝 SQL 指令的方式,將使用者的輸入融入既定的 SQL 語句中,但卻忽略了一件重要的事:使用者可以輸入任意的字串,包括了部份的 SQL 指令!透過輸入部份的 SQL 指令及微調,使用者可以輕易的改變這段 SQL 指令,甚至是疊加另一串 SQL 指令,而我們的網頁則照單全收,以上的輸入,會將整句 SQL 語句調整成下面這樣:

圖 3:

透過必然成真的條件式,再加上 SQL 的註解,我們的網站就這樣曝露在網路上,今天我加的是 OR,若是狠一點的加上 DROP TABLE 等破壞性指令,網站就此拜拜。

這種攻擊不僅僅出現在上例這種 POST 狀況,另一種 GET 狀態也常常受到同樣的攻擊,例如下面的程式碼即開啟了 SQL Injection 的大門。

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Data.SqlClient;

public partial class QueryStringInjection : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            SqlConnection conn = new SqlConnection("Data Source=JEFFRAY;Initial Catalog=Northwind;Integrated Security=True");
            using (conn)
            {
                SqlCommand cmd = new SqlCommand(
        "SELECT * FROM Customers WHERE CustomerID = '"+Request.QueryString["ID"]+”‘”, conn);
                conn.Open();
                DetailsView1.DataSource =
                cmd.ExecuteReader(CommandBehavior.CloseConnection);
                DetailsView1.DataBind();
            }
        }
    }
}

試著在 URL 上鍵入:

http://localhost:43236/FirstInjection/QueryStringInjection.aspx?ID=VINET’ OR 1=1 –
註:http://localhost:43236 是你的 Web Development Server 自動產生的 Port,你必須視情況修改。

結果你會看到 CustomerID=”VINET” 以外的 ALFKI 資料列,如下圖:

圖 4:

如果有心人士在 URL 上鍵入 DROP TABLE 或是 INSERT 的 QueryString,將資料任意的刪除或插入惡意的連結 Script (詳見後述的 Mass SQL Injection 一節),那後果不堪設想。

未啟用 Custom Error Page 的漏洞

你應該已經知道,寫 ASP.NET 應用程式的第一道安全手續就是啟用 Custom Error Page 功能,讓駭客們無法透過預設的錯誤網頁來取得不該取得的資訊,若未啟用 Custom Error Page,那麼下圖是可能發生在你的網站中的:

圖 5:

有了這些資訊,具有耐心的駭客,要透過輸入不同的字元來探測整段 SQL 語句就不困難了,防堵的最佳辦法就是啟用 Custom Error Page 設定:

Web.config
...............略
<customErrors mode="On" defaultRedirect="DefaultError.htm">
</customErrors>
............略

一旦啟用後,錯誤發生時會導向 DefaultError.html,結果變成下面這樣:

圖 6:

檢測你的網頁有無 SQL Injection 的可能性

OK,那有沒有辦法可以檢測現在的網頁是否受 SQL Injection 威脅呢?如果你是網站管理者,而非設計師,那麼你只有依賴現在常見的網頁漏洞檢測工具,對網頁進行黑箱測試,不過提醒你,目前的網頁漏洞測試工具大多是針對 PHP、ASP 所設計的,能測出來的漏洞相當有限,有時即使是安全的網頁,也會因為未實作過濾法(後述),而導致誤判。

如果你是程式設計師,事情就簡單的多了,只要檢視一下程式碼,看看動態組裝 SQL 語句的部份是否有 SQL Injection 即可,圖 007 是一個確認 SQL Injection 是否存在於你的程式中的公式。

圖 7:

只要你的程式中,有 SQL 字串加上使用者輸入值的情況,那麼該網頁存在 SQL Injection 危機的可能性就高達 99.9%。

前輩的叮嚀:防止 SQL Injection 的方法

在數千個網站的入侵事件發生後,許多資安專家提出了各種防範 SQL Injection 的方法,其中不外乎圖 008 的四種。

圖 8:

過濾法可以阻止特定字如【–】、【 OR 】、【’】的輸入,能有效防堵必然成真條件式及錯誤訊息顯示時的漏洞,不過魔高一丈,此法最後仍然遭受破解,透過 SQL 的轉碼函式,駭客可以將部份 SQL 語句做出編碼來逃避偵測,最後突破這道防線。但由於轉碼後的字串相當長,所以只要設計師細心些,搭配 MaxLength 的設定,還是可以讓過濾法奏效,但過濾法其實很脆弱,所以一定要搭配其它的手法方能行之。

下面是一個使用過濾法的例子,利用引入外部 JavaScript 檔案及 Form 的 onSubmit 事件,在送出資料前先檢測擁有 ci Attribute 標示的 text tag,此法可運行於 IE 及 FireFox 上:

Injectiondetect.js

function validateInjection()
{
          var i = 0;
          for(i = 0; i < document.forms[0].elements.length;i++)
          {
            if(document.forms[0].elements[i].type == ‘text’ &&
               document.forms[0].elements[i].getAttribute(”ci”) != null)
            {
              var elem = document.forms[0].elements[i];
              if(elem.value != null &&
                 (elem.value.indexOf(’\”) != -1 ||
                  elem.value.indexOf(’–’) != -1 ||
                  elem.value.indexOf(’ OR ‘) != -1))
              {
                alert(’possible injection detected.’)
                return false;
              }
            }
          }
          return true;
}

.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DefaultWithFilter.aspx.cs" Inherits="DefaultWithFilter" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script language='javascript' type="text/javascript" src='injectiondetect.js'>
    </script>
</head>
<body>
    <form id="form1" onsubmit="return validateInjection()" runat="server">
    <div>
        <table border="1">
        <tr>
        <td>使用者編號</td>
        <td><asp:TextBox ID="TextBox1" ci="true" MaxLength="12"
                     runat="server"></asp:TextBox></td>
        </tr>
        <tr>
        <td>密碼</td>
        <td><asp:TextBox ID="TextBox2" ci="true" MaxLength="12"
                 runat="server"></asp:TextBox></td>
        </tr>
        <tr>
        <td colspan=2>
            <asp:Button ID="Button1" runat="server" Text="登入" onclick="Button1_Click" />
        </td>
        </tr>
        </table>
        <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>

1 2 3 4 5 下一页

 
相关文章
一日一文章
 
一日一软件
一日一动画