ASP 經典表單處理:從 Request.Form 到 SQL INSERT 的完整流程
完整範例程式碼(列表 A)
<%
' 1. 從表單取值
Dim fName, lName, age
fName = Request.Form("fName")
lName = Request.Form("lName")
age = Request.Form("age")
' 2. 組裝 SQL 字串
Dim iStr
iStr = "INSERT INTO uData (fName, lName, age) "
iStr = iStr & "VALUES ("
iStr = iStr & "'" & fName & "', "
iStr = iStr & "'" & lName & "', "
iStr = iStr & age & ")"
' 3. 簡單驗證與輸出
If Trim(fName) <> "" Then
Response.Write iStr & "<BR>"
Else
%>
<HTML>
<BODY>
<FORM NAME="f" METHOD="POST" ACTION="listing1.ASP">
Gimme your:<BR>
First Name: <INPUT TYPE="TEXT" NAME="fName"><BR>
Last Name: <INPUT TYPE="TEXT" NAME="lName"><BR>
Age: <INPUT TYPE="TEXT" NAME="age"><BR>
<INPUT TYPE="SUBMIT" VALUE="Submit">
</FORM>
</BODY>
</HTML>
<%
End If
%>
進階版:使用 Request.Form 集合遍歷(列表 B)
<%
Dim iStr, vStr, nStr, x
iStr = "INSERT INTO uData "
vStr = "VALUES ("
nStr = "("
For Each x In Request.Form
' 建立欄位名清單
nStr = nStr & x & ", "
' 建立欄位值清單
If UCase(x) = "AGE" Then
vStr = vStr & Request.Form(x) & ", "
Else
vStr = vStr & "'" & Request.Form(x) & "', "
End If
Next
' 清理結尾逗號
vStr = Left(vStr, Len(vStr) - 2) & ")"
nStr = Left(nStr, Len(nStr) - 2) & ") "
' 組裝最終 SQL
iStr = iStr & nStr & vStr
If Trim(Request("fName")) <> "" Then
Response.Write iStr & "<BR>"
End If
%>
核心技術要點
| 技術點 | 說明 | 範例 |
| Request.Form 集合 | 取得所有 POST 表單欄位 | For Each x In Request.Form |
| 欄位值存取 | Request.Form("欄位名") | Request.Form("fName") |
| 字串串接 | & 運算子 | "INSERT INTO " & tableName |
| 大小寫轉換 | UCase()/LCase() | UCase(x) = "AGE" |
| 字串修剪 | Trim()/Left()/Len() | Left(vStr, Len(vStr)-2) |
| 條件判斷 | If...Then...Else | If Trim(fName) <> "" Then |
關鍵安全缺陷(必讀)
🚫 此程式碼存在嚴重 SQL 注入漏洞,絕不可用於生產環境!
- **單引號未逸出**:輸入 `O'Reilly` → SQL 語法錯誤或惡意注入
- **字串拼接本質**:資料與程式碼混合,根本無法防禦注入
- **型別判斷脆弱**:僅針對 `AGE` 硬編碼,擴充性極差
- **無輸入驗證**:長度、格式、業務規則完全缺失
現代安全替代方案
方案一:參數化查詢(ADO 傳統改良)
<%
Dim cmd, conn
Set cmd = Server.CreateObject("ADODB.Command")
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "YourConnectionString"
cmd.ActiveConnection = conn
cmd.CommandText = "INSERT INTO uData (fName, lName, age) VALUES (?, ?, ?)"
cmd.CommandType = 1
cmd.Parameters.Append cmd.CreateParameter("@fName", 200, 1, 50)
cmd.Parameters.Append cmd.CreateParameter("@lName", 200, 1, 50)
cmd.Parameters.Append cmd.CreateParameter("@age", 3, 1)
cmd("@fName") = Request.Form("fName")
cmd("@lName") = Request.Form("lName")
cmd("@age") = CInt(Request.Form("age"))
cmd.Execute
%>
方案二:預存程序(最佳實務)
-- SQL Server 預存程序
CREATE PROCEDURE sp_InsertUser
@fName NVARCHAR(50),
@lName NVARCHAR(50),
@age INT
AS
BEGIN
INSERT INTO uData (fName, lName, age) VALUES (@fName, @lName, @age)
END
<%
Dim cmd, conn
Set cmd = Server.CreateObject("ADODB.Command")
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "YourConnectionString"
cmd.ActiveConnection = conn
cmd.CommandText = "sp_InsertUser"
cmd.CommandType = 4 ' adCmdStoredProc
cmd.Parameters.Append cmd.CreateParameter("@fName", 200, 1, 50)
cmd.Parameters.Append cmd.CreateParameter("@lName", 200, 1, 50)
cmd.Parameters.Append cmd.CreateParameter("@age", 3, 1)
cmd("@fName") = Request.Form("fName")
cmd("@lName") = Request.Form("lName")
cmd("@age") = CInt(Request.Form("age"))
cmd.Execute
%>
方案三:現代架構(ASP.NET Core / Entity Framework)
// ASP.NET Core Controller
[HttpPost]
public async Task<IActionResult> Create(UserDto dto)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var user = new User {
FirstName = dto.FirstName,
LastName = dto.LastName,
Age = dto.Age
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return Ok(user);
}
技術演進時間軸
| 時期 | 技術 | SQL 建構方式 | 安全性 |
| 1996-2002 | Classic ASP | 字串拼接 (Request.Form) | 極低(注入高風險) |
| 2002-2010 | ASP.NET WebForms | 參數化查詢 / SqlDataSource | 中(參數化可防注入) |
| 2009-現在 | ASP.NET MVC / Core | Entity Framework / Dapper / 預存程序 | 高(ORM 參數化 + 驗證) |
| 2016-現在 | .NET Core / 5+ | EF Core / 最小 API / 薄控制器 | 最高(編譯期檢查 + 依賴注入) |
遷移重構檢查清單
- ☐ 識別所有字串拼接 SQL 程式碼
- ☐ 替換為參數化查詢或預存程序
- ☐ 加入伺服器端輸入驗證(長度、型別、格式、業務規則)
- ☐ 實作錯誤處理與日誌記錄
- ☐ 套用最小權限資料庫帳號
- ☐ 移除詳細錯誤訊息對外洩露
- ☐ 執行滲透測試驗證注入修復