首頁 >後端開發 >Python教學 >使用Python如何防止sql注入的方法

使用Python如何防止sql注入的方法

高洛峰
高洛峰原創
2017-03-16 16:14:261751瀏覽

前言

web漏洞之首莫過於sql了,不管使用哪種語言進行web後端開發,只要使用了關係型資料庫,可能都會遇到sql注入攻擊問題。那麼在Python web開發的過程中sql注入是怎麼出現的呢,又是怎麼去解決這個問題的呢?


當然,我這裡並不想討論其他語言是如何避免sql注入的,網路上關於PHP防注入的各種方法都有,Python的方法其實類似,這裡我就舉例來說說。

起因

漏洞產生的原因最常見的就是字串拼接了,當然,sql注入不只是拼接一種情況,還有像寬字節注入,特殊字元轉義等等很多種,這裡就說說最常見的字串拼接,這也是初級程式設計師最容易犯的錯誤。


首先咱們定義一個類別來處理mysql的運算

class Database:
    hostname = '127.0.0.1'
    user = 'root'
    password = 'root'
    db = 'pythontab'
    charset = 'utf8'
    def init(self):
        self.connection = MySQLdb.connect(self.hostname, self.user, self.password, self.db, charset=self.charset)
        self.cursor = self.connection.cursor()
    def insert(self, query):
        try:
            self.cursor.execute(query)
            self.connection.commit()
        except Exception, e:
            print e
            self.connection.rollback()
    def query(self, query):
        cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute(query)
        return cursor.fetchall()
    def del(self):
        self.connection.close()

這個類別有問題嗎?

答案是:有!


這個類別是有缺陷的,很容易造成sql注入,以下就說說為何會產生sql注入。


為了驗證問題的真實性,這裡就寫一個方法來呼叫上面的那個類別裡面的方法,如果出現錯誤會直接拋出例外

def test_query(testUrl):
    mysql = Database()
    try:
        querySql = "SELECT * FROM `article` WHERE url='" + testUrl + "'"
        chanels = mysql.query(querySql)
        return chanels
    except Exception, e:
        print e

這個方法非常簡單,一個最常見的select查詢語句,也使用了最簡單的字串拼接組成sql語句,很明顯傳入的參數testUrl 可控,要想進行注入測試,只需要在testUrl的值後面加上單引號即可進行sql注入測試,這個不多說,肯定是存在註入漏洞的,腳本跑一遍,看啥結果


(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right synt to use near ' 't.tips''' at line 1")

回顯報錯,很眼熟的錯誤,這裡我傳入的測試參數是

t.tips'

下面再說一個導致注入的情況,對上面的方法進行稍微修改後

def test_query(testUrl):
    mysql = Database()
    try:
        querySql = ("SELECT * FROM `article` WHERE url='%s'" % testUrl)
        chanels = mysql.query(querySql)
        return chanels
    except Exception, e:
        print e

這個方法裡面沒有直接使用字串拼接,而是使用了%s 來代替要傳入的參數,看起來是不是非常像預編譯的sql?那這種寫法能不能防止sql注入呢?測試便知道,回顯如下

(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''t.tips' MariaDB server version for the right syntax to use near ''t.tips' '' at line 1")

和上面的測試結果一樣,所以這種方法也是不行的,而且這種方法並不是預先編譯sql語句,那麼怎麼做才能防止sql注入呢?

解決

兩個方案


1> 對傳入的參數進行編碼轉義


2> 使用Python的MySQLdb模組自帶的方法


#第一種方案其實在很多PHP的防注入方法裡面都有,對特殊字元進行轉義或過濾。


第二種方案就是使用內部方法,類似PHP裡面的PDO,這裡對上面的資料庫類別進行簡單的修改即可。


修改後的程式碼

class Database:
    hostname = '127.0.0.1'
    user = 'root'
    password = 'root'
    db = 'pythontab'
    charset = 'utf8'
    def init(self):
        self.connection = MySQLdb.connect(self.hostname, self.user, self.password, self.db, charset=self.charset)
        self.cursor = self.connection.cursor()
    def insert(self, query, params):
        try:
            self.cursor.execute(query, params)
            self.connection.commit()
        except Exception, e:
            print e
            self.connection.rollback()
    def query(self, query, params):
        cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)
        cursor.execute(query, params)
        return cursor.fetchall()
    def del(self):
        self.connection.close()

這裡execute 執行的時候傳入兩個參數,第一個是參數化的sql語句,第二個是對應的實際的參數值,函數內部會對傳入的參數值進行對應的處理防止sql注入,實際使用的方法如下


preUpdateSql = "UPDATE `article` SET title=%s,date=%s,mainbody=%s WHERE id=%s"

# mysql.insert(preUpdateSql, [title, date, content, aid])

這樣就可以防止sql注入,傳入一個列表之後,MySQLdb模組內部會將列表序列化成一個元組,然後進行escape操作。


以上是使用Python如何防止sql注入的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn