2017-06-15 56 views
0

我有一個小的Python/Flask web應用程序,它以CSV格式從SQLite表中返回數據。我發現使用sqlite3命令行程序將查詢結果直接導出到CSV文件然後返回CSV文件比將查詢結果讀入Python,將它們寫入到內存文件然後返回內存中的文件。但是,使用sqlite3程序意味着我必須自己構建查詢,使我的應用程序受到SQL注入攻擊。在Python中構建SQLite查詢,但使用sqlite3命令行程序執行它?

快,但容易受到SQL注入式攻擊

queries = """ 
.mode csv 
.headers on 
.output /tmp/results.csv 
SELECT * FROM mytable WHERE foo = '{0}'; 
""".format(user_input) 

subprocess.check_output(
    ["sqlite3", "/path/to/mydb.sqlite"], input=bytes(queries.encode("utf-8"))) 

return send_file("/tmp/results.csv", mimetype="text/csv") 

從SQL注入安全,但速度較慢

conn = sqlalchemy.create_engine("sqlite:////path/to/mydb.sqlite") 
result = conn.execute("SELECT * FROM mytable WHERE foo = ?", (user_input,)) 

csvfile = io.StringIO() 
csvwriter = csv.writer(csvfile) 
csvwriter.writerow(result.keys()) 

for row in result.fetchall(): 
    csvwriter.writerow(row) 

return Response(csvfile.getvalue(), mimetype="text/csv") 

有沒有一種方法,我可以使用Python庫(SQLAlchemy的, sqlite3模塊或其他任何東西)來構建一個從SQL注入安全但不真正執行查詢的查詢,這樣我就可以通過子進程使用0來執行查詢命令行程序?

回答

0

直接的SQLite到CSV導出路徑(使用sqlite3程序的.mode csv)將比主要選擇大量數據的更復雜的SQLite到Python到CSV導出路徑具有速度優勢;當選擇較小的數據量時,速度不會相差太多。

因此,根據您的模式,一種選擇是使用Python查詢數據庫中的一組ID,然後使用sqlite3使用ID選擇其餘數據。這樣Python庫會清理你的用戶輸入(防止SQL注入等),你傳遞給sqlite3程序的內容將不是用戶輸入,而是你自己查詢的ID。

conn = sqlalchemy.create_engine("sqlite:////path/to/mydb.sqlite") 
result = conn.execute("SELECT ID FROM mytable WHERE foo = ?", (user_input,)) 
ids = [row[0] for row in result.fetchall()] 

queries = """ 
.mode csv 
.headers on 
.output /tmp/results.csv 
SELECT * FROM mytable WHERE ID IN ({0}); 
""".format(",".join([str(id) for id in ids])) 

subprocess.check_output(
    ["sqlite3", "/path/to/mydb.sqlite"], input=bytes(queries.encode("utf-8"))) 

return send_file("/tmp/results.csv", mimetype="text/csv") 

這種方法涉及2次的查詢,而不是1,但如果瓶頸選擇所有數據的差異可能是微不足道的。對您的應用進行配置,以便確定。

0

在一個SQL字符串中,唯一的特殊字符是引用'本身。 (以及碼0的字符,這將結束整個查詢,並導致一個語法錯誤。)

爲了逃避單引號,雙它們:

user_input.replace("'", "''")