整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Go語言SQL注入和防注入

          Go語言SQL注入和防注入

          、SQL注入是什么

          SQL注入是一種注入攻擊手段,通過執行惡意SQL語句,進而將任意SQL代碼插入數據庫查詢,從而使攻擊者完全控制Web應用程序后臺的數據庫服務器。攻擊者可以使用SQL注入漏洞繞過應用程序驗證,比如繞過登錄驗證登錄Web身份驗證和授權頁面;也可以繞過網頁,直接檢索數據庫的所有內容;還可以惡意修改、刪除和增加數據庫內容。

          二、防止SQl注入的思路和方法

          • 1.永遠不要信任用戶的輸入。對用戶的輸入進行校驗,可以通過正則表達式,或限制長度;對單引號和 雙"-"進行轉換等。
          • 2.永遠不要使用動態拼裝SQL,可以使用參數化的SQL或者直接使用存儲過程進行數據查詢存取。
          • 3.永遠不要使用管理員權限的數據庫連接,為每個應用使用單獨的權限有限的數據庫連接。
          • 4.不要把機密信息直接存放,加密或者hash掉密碼和敏感的信息。
          • 5.應用的異常信息應該給出盡可能少的提示,最好使用自定義的錯誤信息對原始錯誤信息進行包裝
          • 6.sql注入的檢測方法一般采取輔助軟件或網站平臺來檢測,軟件一般采用sql注入檢測工具jsky,網站平臺就有億思網站安全平臺檢測工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻擊等。

          三、Go語言防止SQL注入的方法

          我們采取了第二條思路和方法,即不用動態拼接SQL語句的方法,而是使用參數化查詢,即變量綁定。

          下面給出SQL注入攻擊安全漏洞代碼——拼接SQL語句:

          //數據庫
          /*
           Navicat Premium Data Transfer
          
           Source Server         : localhost_3306
           Source Server Type    : MySQL
           Source Server Version : 50553
           Source Host           : localhost:3306
           Source Schema         : test
          
           Target Server Type    : MySQL
           Target Server Version : 50553
           File Encoding         : 65001
          
           Date: 28/02/2020 10:48:06
          */
          
          SET NAMES utf8mb4;
          SET FOREIGN_KEY_CHECKS=0;
          
          -- ----------------------------
          -- Table structure for userinfo
          -- ----------------------------
          DROP TABLE IF EXISTS `userinfo`;
          CREATE TABLE `userinfo`  (
            `uid` int(10) NOT NULL AUTO_INCREMENT,
            `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
            `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
            PRIMARY KEY (`uid`) USING BTREE
          ) ENGINE=MyISAM AUTO_INCREMENT=14 CHARACTER SET=latin1 COLLATE=latin1_swedish_ci ROW_FORMAT=Dynamic;
          
          -- ----------------------------
          -- Records of userinfo
          -- ----------------------------
          INSERT INTO `userinfo` VALUES (2, 'aaa', 'hh');
          INSERT INTO `userinfo` VALUES (4, 'ast', 'dddd');
          
          SET FOREIGN_KEY_CHECKS=1;
          
          
          //test.go
          package main
          
          import (
          	"database/sql"
          	"fmt"
          	_ "github.com/go-sql-driver/mysql"
          	"html/template"
          	"log"
          	"net/http"
          	"strings"
          )
          func login(w http.ResponseWriter, r *http.Request) {
          	fmt.Println("method:", r.Method) //獲取請求的方法
          	if r.Method=="GET" {
          		t, _ :=template.ParseFiles("D:/Golang/GoItem/go_ex/goSql/test.html")
          		t.Execute(w, nil)
          	} else {
          		//請求的是查詢數據,那么執行查詢的邏輯判斷
          		r.ParseForm()
          		fmt.Println("username:", r.Form["username"])
          		var sename=strings.Join(r.Form["username"], "")
          		var partname=strings.Join(r.Form["password"], "")
          		db, err :=sql.Open("mysql", "root:123456@/test?charset=utf8")
          		infoErr(err)
          		if sename !="" && partname !="" {
          			var uid int
          			var username string
          			var password string
          			//字符串拼接查詢
          			err :=db.QueryRow("SELECT * FROM userinfo WHERE username='"+sename+"'AND password='"+partname+"'").
          				Scan(&uid, &username, &password)
          			infoErr(err)
          			//判斷返回的數據是否為空
          			if err==sql.ErrNoRows {
          				fmt.Fprintf(w, "無該用戶數據")
          			} else {
          				if (sename==username) && (partname==password) {
          					fmt.Println(uid)
          					fmt.Println(username)
          					fmt.Println(password)
          					t, _ :=template.ParseFiles("D:/Golang/GoItem/go_ex/goSql/success.html")
          					t.Execute(w, nil)
          				}
          			}
          		} else if sename=="" || partname=="" {
          			fmt.Fprintf(w, "錯誤,輸入不能為空!")
          		}
          
          	}
          
          }
          
          
          func infoErr(err error) {
          	if err !=nil {
          		panic(err)
          	}
          }
          
          func main() {
          	http.HandleFunc("/login",login)     //設置訪問的路由 	//設置訪問的路由
          	err :=http.ListenAndServe(":9092", nil) //設置監聽的端口
          	if err !=nil {
          		log.Fatal("ListenAndServe: ", err)
          	}
          }
          
          //login.html
          <html>
          <head>
              <meta charset="utf-8" />
              <title>sql防注入</title>
              <style>
                  form{
                      width: 30vw;
                      height: 30vh;
                      min-height: 300px;
                      margin: 10vh auto;
                      border: 1px solid;
                      border-radius: 4px;
                  }
                  form .username,.password{
                     display: block;
                      float: right;
                  }
                  div {
                      width: 300px;
                      height: 80px;
                      margin: 30px auto 0;
                  }
                  input label {
                      float: left;
                      display: inline-block;
                  }
                  input {
                      height: 30px;
                  }
                  .button {
                      width: 100px;
                      margin: auto;
                      clear: both;
                      display: block;
                  }
              </style>
          </head>
          <body>
          <form action="/login" method="post">
              <div>
                  <label>username: </label>
                  <input class="username" type="text" name="username">
              </div>
              <div>
                  <label>password:</label>
                  <input class="password" type="text" name="password">
              </div>
              <input class="button" type="submit" value="查詢">
          </form>
          </body>
          </html>
          

          解決防SQL注入方案——參數化查詢:

          //test.go
          package main
          
          import (
          	"database/sql"
          	"fmt"
          	_ "github.com/go-sql-driver/mysql"
          	"html/template"
          	"log"
          	"net/http"
          	"strings"
          )
          
          func login(w http.ResponseWriter, r *http.Request) {
          	fmt.Println("method:", r.Method) //獲取請求的方法
          	if r.Method=="GET" {
          		t, _ :=template.ParseFiles("D:/Golang/GoItem/go_ex/goSql/login.html")
          		t.Execute(w, nil)
          	} else {
          		//請求的是查詢數據,那么執行查詢的邏輯判斷
          		r.ParseForm()
          		fmt.Println("username:", r.Form["username"])
          		var sename=strings.Join(r.Form["username"], "")
          		var partname=strings.Join(r.Form["password"], "")
          		db, err :=sql.Open("mysql", "root:123456@/test?charset=utf8")
          		checkErr(err)
          		if sename !="" && partname !="" {
          			var uid int
          			var username string
          			var password string
                      //參數查詢在一定程度上防止sql注入,參數化查詢主要做了兩件事:
          			//1.參數過濾;2.執行計劃重用
          			//因為執行計劃被重用,所以可以防止SQL注入。
          			err :=db.QueryRow("SELECT * FROM userinfo WHERE username=? AND password=?", sename, partname).
          				Scan(&uid, &username, &password)
          			//判斷返回的數據是否為空
          			if err==sql.ErrNoRows {
          				fmt.Fprintf(w, "無該用戶數據")
          			} else {
          				if (sename==username) && (partname==password) {
          					fmt.Println(uid)
          					fmt.Println(username)
          					fmt.Println(password)
          					t, _ :=template.ParseFiles("D:/Golang/GoItem/go_ex/goSQL/success.html")
          					t.Execute(w, nil)
          				}
          			}
          		} else if sename=="" || partname=="" {
          			fmt.Fprintf(w, "錯誤,輸入不能為空!")
          		}
          
          	}
          
          }
          
          func checkErr(err error) {
          	if err !=nil {
          		panic(err)
          	}
          }
          
          func main() {
          	http.HandleFunc("/login", login)     //設置訪問的路由
          	err :=http.ListenAndServe(":9090", nil) //設置監聽的端口
          	if err !=nil {
          		log.Fatal("ListenAndServe: ", err)
          	}
          }
          
          

          四、SQL注入判斷

          執行登錄查詢的數據庫語句:"SELECT * FROM userinfo WHERE username='"+sename+"'AND password='"+partname+"'"

          當查詢到數據表中存在同時滿足 username 和 password 字段時,會返回用戶信息。 嘗試在用戶名中輸入 123' or 1=1 #, 密碼同樣輸入 123' or 1=1 # ,實際執行的SQL語句是select * from users where username='123' or '1'='1' and password='123' or '1'='1

          則會出現一個空白頁面,其實此時SQl注入已經繞過驗證進入到需要身份驗證的頁面。

          而如果執行"SELECT * FROM userinfo WHERE username=? AND password=?", sename, partname

          再次輸入123' or 1=1 #,則會被攔截下來,顯示無該用戶數據

          五、為什么參數化查詢會防止SQL注入

          我們需要知道參數化查詢都做了些什么事:

          1.參數過濾

          2.執行計劃重用

          它的原理是采用了預編譯的方法,先將SQL語句中可被客戶端控制的參數集進行編譯,生成對應的臨時變量集,再使用對應的設置方法,為臨時變量集里面的元素進行賦值,而QueryRow()方法會對傳入參數進行強制類性檢查和安全檢查,所以就避免了SQL注入的產生。

          package main
          import (
              "fmt"
              "net/http"
              "sync"
              "github.com/PuerkitoBio/goquery"
          )
          func main() {
              // 定義urls數組,包含要爬取的多個網頁的URL
              urls :=[]string{
                  "https://www.xp.cn/using.html",
                  "https://www.xp.cn/advise.html",
                  "https://www.xp.cn/bug.html",
              }
              // WaitGroup來等待所有的goroutine執行完畢
              var wg sync.WaitGroup
              for _, url :=range urls {
                  wg.Add(1)
                  // 用goroutine來同時爬取這些網頁
                  go func(u string) {
                      defer wg.Done()
                      resp, err :=http.Get(u)
                      if err !=nil {
                          fmt.Println(err)
                          return
                      }
                      defer resp.Body.Close()
                      // 解析網頁內容
                      doc, err :=goquery.NewDocumentFromReader(resp.Body)
                      if err !=nil {
                          fmt.Println(err)
                          return
                      }
                      // 獲取標題
                      doc.Find(".pb-20").Each(func(i int, s *goquery.Selection) {
                          title :=s.Find("li div a").Text()
                          fmt.Printf("%s\n", title)
                      })
                  }(url)
              }
              wg.Wait()
          }

          實際應用中,我們還可以通過設置代理、使用cookie等方式來提高爬取效率和穩定性。

          爬取汽車之家網站的車型數據,您需要先了解該網站的頁面結構和數據源。一般來說,您可以使用Go語言中的HTTP客戶端庫(如net/http)和HTML解析器(如goquery)來實現爬取。

          以下是一個簡單的示例代碼,它可以爬取汽車之家網站上所有品牌的車型數據:

          package main
          
          import (
          	"fmt"
          	"log"
          	"net/http"
          	"strings"
          
          	"github.com/PuerkitoBio/goquery" // 導入 goquery 庫,用于解析 HTML 頁面
          )
          
          func main() {
          	url :="https://www.autohome.com.cn/grade/carhtml/"
          
          	// 發送 HTTP GET 請求獲取頁面內容
          	res, err :=http.Get(url)
          	if err !=nil {
          		log.Fatal(err)
          	}
          	defer res.Body.Close()
          
          	// 檢查狀態碼是否為 200
          	if res.StatusCode !=200 {
          		log.Fatalf("Status code error: %d %s", res.StatusCode, res.Status)
          	}
          
          	// 使用 goquery 解析頁面內容
          	doc, err :=goquery.NewDocumentFromReader(res.Body)
          	if err !=nil {
          		log.Fatal(err)
          	}
          
          	// 遍歷頁面中所有的 <dl> 元素
          	doc.Find("dl").Each(func(i int, s *goquery.Selection) {
          		// 提取品牌名稱
          		brand :=s.Find("dt").Text()
          		brand=strings.TrimSpace(brand)
          		if brand !="" {
          			fmt.Println(brand)
          			// 遍歷該品牌下的所有車型
          			s.Find("dd a").Each(func(j int, t *goquery.Selection) {
          				model :=t.Text()
          				model=strings.TrimSpace(model)
          				if model !="" {
          					fmt.Printf("  %s\n", model)
          				}
          			})
          		}
          	})
          }
          

          注釋已添加在代碼中。該代碼使用 http.Get() 函數發送 HTTP GET 請求,并使用 goquery.NewDocumentFromReader() 函數解析 HTML 頁面內容。然后,代碼遍歷頁面中所有的 <dl> 元素,提取品牌名稱和車型名稱,并將它們打印到控制臺中。

          需要注意的是,這里只是簡單地打印車型數據到控制臺,實際使用時可能需要進行更多的處理,例如將數據保存到數據庫或文件中。此外,還需要遵守網站的使用條款和限制,不要過度訪問或濫用網站資源。


          主站蜘蛛池模板: 国产福利一区二区三区| 日韩一区二区三区精品| 中字幕一区二区三区乱码| 国产成人精品a视频一区| 亚洲av一综合av一区| 尤物精品视频一区二区三区| 国产在线精品一区二区不卡麻豆| 国产乱人伦精品一区二区在线观看 | 色欲AV蜜臀一区二区三区| 精品视频无码一区二区三区| 国产精品男男视频一区二区三区| 久久99精品波多结衣一区| 亚洲av午夜精品一区二区三区| 人妖在线精品一区二区三区| 国精无码欧精品亚洲一区| 久久国产精品免费一区二区三区| 无码一区二区三区亚洲人妻| 精品国产一区二区三区在线 | 中文字幕一区二区免费| 亚洲免费一区二区| 无码乱人伦一区二区亚洲一| 亚洲熟妇av一区| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 无码国产精品一区二区免费模式 | 波多野结衣的AV一区二区三区| 国产成人无码AV一区二区在线观看 | 一区二区三区午夜| 性无码一区二区三区在线观看| 亚洲日韩AV一区二区三区四区 | 无遮挡免费一区二区三区| 精品国产一区二区三区不卡| 日本一区视频在线播放| 中文字幕人妻AV一区二区| 中文字幕日韩一区二区三区不| 呦系列视频一区二区三区| 久久无码精品一区二区三区| 国产在线不卡一区二区三区| 亚洲国产一区二区a毛片| 亚洲天堂一区二区三区四区| 亚洲熟女乱色一区二区三区| 麻豆视传媒一区二区三区|