使用 Golang 打造 Web 應用程式
  • Introduction
  • Go 環境配置
    • Go 安裝
    • GOPATH 與工作空間
    • Go 命令
    • Go 開發工具
    • 小結
  • Go 語言基礎
    • 你好,Go
    • Go 基礎
    • 流程和函式
    • struct
    • 物件導向
    • interface
    • 併發
    • 小結
  • Web 基礎
    • web 工作方式
    • Go 建立一個簡單的 web 服務
    • Go 如何使得 web 工作
    • Go 的 http 套件詳解
    • 小結
  • 表單
    • 處理表單的輸入
    • 驗證表單的輸入
    • 預防跨站指令碼
    • 防止多次提交表單
    • 處理檔案上傳
    • 小結
  • 存取資料庫
    • database/sql 介面
    • 使用 MySQL 資料庫
    • 使用 SQLite 資料庫
    • 使用 PostgreSQL 資料庫
    • 使用 beedb 函式庫進行 ORM 開發
    • NoSQL 資料庫操作
    • 小結
  • session 和資料儲存
    • session 和 cookie
    • Go 如何使用 session
    • session 儲存
    • 預防 session 劫持
    • 小結
  • 文字檔案處理
    • XML 處理
    • JSON 處理
    • 正則處理
    • 範本處理
    • 檔案操作
    • 字串處理
    • 小結
  • Web 服務
    • Socket 程式設計
    • WebSocket
    • REST
    • RPC
    • 小結
  • 安全與加密
    • 預防 CSRF 攻擊
    • 確保輸入過濾
    • 避免 XSS 攻擊
    • 避免 SQL 注入
    • 儲存密碼
    • 加密和解密資料
    • 小結
  • 國際化和本地化
    • 設定預設地區
    • 本地化資源
    • 國際化站點
    • 小結
  • 錯誤處理,除錯和測試
    • 錯誤處理
    • 使用 GDB 除錯
    • Go 怎麼寫測試案例
    • 小結
  • 部署與維護
    • 應用日誌
    • 網站錯誤處理
    • 應用部署
    • 備份和還原
    • 小結
  • 如何設計一個 Web 框架
    • 專案規劃
    • 自訂路由器設計
    • controller 設計
    • 日誌和配置設計
    • 實現部落格的增刪改
    • 小結
  • 擴充套件 Web 框架
    • 靜態檔案支援
    • Session 支援
    • 表單支援
    • 使用者認證
    • 多語言支援
    • pprof 支援
    • 小結
  • 參考資料
Powered by GitBook
On this page

Was this helpful?

  1. 表單

防止多次提交表單

不知道你是否曾經看到過一個論壇或者部落格,在一個帖子或者文章後面出現多條重複的記錄,這些大多數是因為使用者重複提交了留言的表單引起的。由於種種原因,使用者經常會重複提交表單。通常這只是滑鼠的誤操作,如雙擊了提交按鈕,也可能是為了編輯或者再次核對填寫過的資訊,點選了瀏覽器的後退按鈕,然後又再次點選了提交按鈕而不是瀏覽器的前進按鈕。當然,也可能是故意的——比如,在某項線上調查或者博彩活動中重複投票。那我們如何有效的防止使用者多次提交相同的表單呢?

解決方案是在表單中新增一個帶有唯一值的隱藏欄位。在驗證表單時,先檢查帶有該唯一值的表單是否已經提交過了。如果是,拒絕再次提交;如果不是,則處理表單進行邏輯處理。另外,如果是採用了 Ajax 模式提交表單的話,當表單提交後,透過 javascript 來禁用表單的提交按鈕。

我繼續拿 4.2 小節的例子優化:

<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">籃球
<input type="checkbox" name="interest" value="tennis">網球
使用者名稱:<input type="text" name="username">
密碼:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="登陸">

我們在模版裡面增加了一個隱藏欄位token,這個值我們透過 MD5(時戳) 來取得唯一值,然後我們把這個值儲存到伺服器端(session 來控制,我們將在第六章講解如何儲存),以方便表單提交時比對判定。

func login(w http.ResponseWriter, r *http.Request) {
    fmt.Println("method:", r.Method) //取得請求的方法
    if r.Method == "GET" {
        crutime := time.Now().Unix()
        h := md5.New()
        io.WriteString(h, strconv.FormatInt(crutime, 10))
        token := fmt.Sprintf("%x", h.Sum(nil))

        t, _ := template.ParseFiles("login.gtpl")
        t.Execute(w, token)
    } else {
        //請求的是登陸資料,那麼執行登陸的邏輯判斷
        r.ParseForm()
        token := r.Form.Get("token")
        if token != "" {
            //驗證 token 的合法性
        } else {
            //不存在 token 報錯
        }
        fmt.Println("username length:", len(r.Form["username"][0]))
        fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //輸出到伺服器端
        fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
        template.HTMLEscape(w, []byte(r.Form.Get("username"))) //輸出到客戶端
    }
}

上面的程式碼輸出到頁面的原始碼如下:

圖 4.4 增加 token 之後在客戶端輸出的原始碼資訊

我們看到 token 已經有輸出值,你可以不斷的重新整理,可以看到這個值在不斷的變化。這樣就保證了每次顯示 form 表單的時候都是唯一的,使用者提交的表單保持了唯一性。

我們的解決方案可以防止非惡意的攻擊,並能使惡意使用者暫時不知所措,然後,它卻不能排除所有的欺騙性的動機,對此類別情況還需要更復雜的工作。

links

Previous預防跨站指令碼Next處理檔案上傳

Last updated 4 years ago

Was this helpful?

上一節:

下一節:

目錄
預防跨站指令碼
處理檔案上傳