Най-добрата практика на Python и най-сигурният за свързване с MySQL и изпълнение на заявки

Кой е най-безопасният начин за изпълнение на заявки в mysql, наясно съм с опасностите, свързани с MySQL и SQL инжектирането.

Въпреки това не знам как трябва да изпълнявам заявките си, за да предотвратя инжектиране на променливите, които други потребители (уеб клиенти) могат да манипулират. Преди писах своя собствена escape функция, но очевидно това не е направено.

Какво трябва да използвам и как да го използвам за заявки и безопасно вмъкване в MySQL база данни чрез python, без да рискувам инжектиране на mysql?


person Lucas Kauffman    schedule 28.10.2011    source източник


Отговори (3)


За да избегнете инжекции, използвайте execute с %s на мястото на всяка променлива, след което предайте стойността чрез списък или кортеж като втори параметър на execute. Ето един пример от документацията:

c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
          WHERE price < %s""", (max_price,))

Имайте предвид, че тук се използва запетая, а не % (което би било директно заместване на низ, а не екранирано). Не правете това:

c.execute("""SELECT spam, eggs, sausage FROM breakfast
          WHERE price < %s""" % (max_price,))

Освен това не са ви необходими кавичките около титуляра на позиция ('%s'), ако параметърът е низ.

person Bruno    schedule 28.10.2011
comment
Използвайте и %s (вижте примера max_price по-горе). - person Bruno; 28.10.2011
comment
защо има запетая зад max_price? Какво означава това ? Съжалявам, ако въпросите ми изглеждат глупави, но съм съвсем нов в Python :) - person Lucas Kauffman; 30.10.2011
comment
Запетаята след max_price е нотацията за кортеж от 1 елемент: docs. python.org/tutorial/ - person Bruno; 30.10.2011
comment
Казахте Note that this is using a comma, not % (which would be a direct string substitution, not escaped). Don't do this Сигурен ли сте? Тъй като използвах запетая и тя избягва низ - person Hussain; 23.05.2014
comment
@HussainTamboli, да, точно това казах: запетаята е правилният начин за използване на заместителите на параметрите (извършва всички необходими екранирания), % не екранира параметрите. - person Bruno; 23.05.2014
comment
@Бруно О! разбирам го Така че го правя по правилния начин. Извинете ме между другото. Не съм го разбрал. - person Hussain; 23.05.2014
comment
Python преобразува стойности в кортеж в тип данни, който MySQL разбира, и добавя необходимите кавички. - person shadow0359; 03.04.2017
comment
А когато е име на таблица? струва ми се, че %s въвежда единични кавички като 'breakfast', а не като `breakfast` - person lucidbrot; 25.08.2019
comment
@lucidbrot Имената на таблиците не са параметри. Ако трябва да създадете заявка динамично с имена на таблици, идващи от променливи, трябва да дезинфекцирате тези променливи ръчно, преди да ги поставите в низа на заявката (не чрез контейнери за параметри). Например, позволете само имена на таблици, които съответстват на [a-z0-9_]+. - person Bruno; 25.08.2019
comment
Да, работи. Но защо защитава от SQL инжектиране? - person RicarHincapie; 08.08.2020
comment
@RicarHincapie Той предпазва от SQL инжекции, защото третира %s като параметър, който няма нищо общо със заместването на низ в самата заявка: или драйверът ще избяга правилно от стойностите, или самият DB протокол ще вземе това под внимание. - person Bruno; 09.08.2020

Като разширение на отговора на Bruno, вашата MySQL клиентска библиотека може да поддържа всеки от няколко различни формата за указване на наименувани параметри. От PEP 249 (DB-API) можете да напишете вашите заявки като:

"qmark"

>>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = ?", (lumberjack,))

"цифров"

>>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :1", (lumberjack,))

'на име'

>>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :jack", {'jack': lumberjack})

"формат"

>>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = %s", (lumberjack,))

'pyformat'

>>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = %(jack)s", {'jack': lumberjack})

Можете да видите коя клиентска библиотека поддържа, като погледнете променливата на ниво модул paramstyle:

>>> clientlibrary.paramstyle
'pyformat'

Всяка от горните опции трябва да направи правилното нещо по отношение на обработката на вашите евентуално несигурни данни. Както посочи Бруно, моля, никога не се опитвайте сами да вмъквате параметри. Често използваните клиентски библиотеки са много по-добри в правилната обработка на данни, отколкото ние, обикновените смъртни, някога ще бъдем.

person Kirk Strauser    schedule 28.10.2011
comment
Кои клиентски библиотеки поддържат „named“? PyMySQL и MySQLdb поддържат „format“, а oursql поддържа „qmark“. - person Martin Burch; 10.04.2015
comment
sqlite3 поне поддържа 'named'. Нямам инсталирани MySQL адаптери, за да проверя за поддръжка на „named“ в тях. - person Kirk Strauser; 10.04.2015
comment
Знам, че това е стар въпрос, но се опитвам да кодирам уеб страницата си правилно и нямам много опит със защитен SQL. Използването на този метод, описан по-горе, адекватно ли е за предотвратяване на SQL инжектиране или има други неща, които трябва да направя в допълнение към това? Благодаря. - person jonesy19; 22.05.2018
comment
Това само по себе си е достатъчно добро. Горещо, силно препоръчвам да проверите ORM като SQLAlchemy и да го оставите да се справи с подробностите вместо вас. - person Kirk Strauser; 23.05.2018

Ако използвате mysqldb, можете да използвате вградената функция escape_string. Като този.

sql = "SELECT spam FROM eggs WHERE lumberjack = '" + MySQLdb.escape_string(str(lumberjack)) + "';"
cursor.execute(sql)

Винаги предпочитам да използвам функцията за изход на конектора на базата данни - тя работи по предназначение и ръчното кодиране на функции за изход сами представлява риск за сигурността.

person Wayne Workman    schedule 03.07.2018
comment
Тъй като MySQLdb в Python е почти директно картографиране на C API и тъй като MySQL документ казва Не използвайте тази функция. вероятно не е добра идея да я използвате. Дори и с real_escape_string, може да има уязвимости (предимно PHP примери, но базирани на един и същ C API и в двата случая). - person Bruno; 13.11.2018