파이썬 MySQLDB IN 절에서 사용하기 위해 목록을 파열


84

목록을 문자열에 매핑하는 방법을 알고 있습니다.

foostring = ",".join( map(str, list_of_ids) )

그리고 다음을 사용하여 해당 문자열을 IN 절로 가져올 수 있음을 알고 있습니다.

cursor.execute("DELETE FROM foo.bar WHERE baz IN ('%s')" % (foostring))

내가 필요한 것은 MySQLDB를 사용하여 동일한 작업을 안전하게 수행하는 것입니다 (SQL 주입 방지). 위의 예에서는 foostring이 실행을위한 인자로 전달되지 않기 때문에 취약합니다. 또한 mysql 라이브러리 외부에서 인용하고 탈출해야합니다.

( 관련 SO 질문 이 있지만 여기에 나열된 답변은 MySQLDB에서 작동하지 않거나 SQL 주입에 취약합니다.)




@mluebke 쿼리에서 여러 목록을 전달하는 것에 대한 아이디어가 있습니까?
Dipen Dedania

답변:


157

list_of_ids직접 사용 :

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
                tuple(list_of_ids))

그렇게하면 자신을 인용 할 필요가없고 모든 종류의 SQL 주입을 피할 수 있습니다.

데이터 ( list_of_ids)는 매개 변수 (쿼리 텍스트가 아님)로 mysql의 드라이버로 직접 이동하므로 주입이 없습니다. 문자열에 원하는 문자를 남겨 둘 수 있으며 문자를 제거하거나 인용 할 필요가 없습니다.


2
@heikogerlach : % s를 인용하지 않습니다 ... 첫 번째 줄은 "% s, % s, % s"문자열을 생성합니다 ... list_of_ids 길이와 동일한 크기입니다.
nosklo

아아, 당신 말이 맞아요. 더 세게 볼 필요가 있습니다. 어쩐지 나는 그것을 섞었다. 그래도 좋은 솔루션입니다.

sqlite에서도 작동합니까? 방금 시도했는데 구문 오류를 지적하는 것 같습니다.
Sohaib 2014 년

sqlite의 @Sohaib 대체 문자가 ?아니므로 %s첫 번째 줄을 format_strings = ','.join('?' * len(list_of_ids)).
nosklo

1
@kdas 귀하의 경우 쿼리 % format_strings의 다른 %s자리 표시자를 변경 하지 않고 자리 표시 자만 IN (%s)-이를 달성하는 방법은 %교체하려는 문자를 제외한 모든 문자 를 두 배로 늘리는 것입니다.query = ("select distinct cln from vcf_commits where branch like %%s and repository like %%s and filename in (%s) and author not like %%s" % format_strings,); cursor.execute(query, (branch, repository) + tuple(fname_list) + (invalid_author,))
nosklo

5

이 질문은 꽤 오래되었지만 다른 사람이 내가 원하는 것을 찾고 있다면 답변을 남기는 것이 더 나을 것이라고 생각했습니다.

매개 변수가 많거나 명명 된 매개 변수를 사용하려는 경우 허용되는 답변이 지저분 해집니다.

몇 번의 시련 후

ids = [5, 3, ...]  # list of ids
cursor.execute('''
SELECT 
...
WHERE
  id IN %(ids)s
  AND created_at > %(start_dt)s
''', {
  'ids': tuple(ids), 'start_dt': '2019-10-31 00:00:00'
})

테스트 python2.7,pymysql==0.7.11


3
이것은 python 3 및 mysql-connector-python 8.0.21에서는 작동하지 않습니다. "Python 튜플을 MySQL 유형으로 변환 할 수 없음"오류가 반환됩니다.
Rubms

-1

당신이 사용하는 경우 Django 2.0 or 2.1Python 3.6, 이것은 올바른 방법입니다 :

from django.db import connection
RESULT_COLS = ['col1', 'col2', 'col3']
RESULT_COLS_STR = ', '.join(['a.'+'`'+i+'`' for i in RESULT_COLS])
QUERY_INDEX = RESULT_COLS[0]

TABLE_NAME = 'test'
search_value = ['ab', 'cd', 'ef']  # <-- a list
query = (
    f'SELECT DISTINCT {RESULT_COLS_STR} FROM {TABLE_NAME} a '
    f'WHERE a.`{RESULT_COLS[0]}` IN %s '
    f'ORDER BY a.`{RESULT_COLS[0]}`;'
)  # <- 'SELECT DISTINCT a.`col1`, a.`col2`, a.`col3` FROM test a WHERE a.`col1` IN %s ORDER BY a.`col1`;'
with connection.cursor() as cursor:
    cursor.execute(query, params=[search_value])  # params is a list with a list as its element

참조 : https://stackoverflow.com/a/23891759/2803344 https://docs.djangoproject.com/en/2.1/topics/db/sql/#passing-parameters-into-raw


-1

이 질문은 꽤 오래되었지만. 누군가를 도울 수 있다면 내 솔루션을 공유하고 있습니다.

list_to_check = ['A', 'B'] cursor.execute("DELETE FROM foo.bar WHERE baz IN ({})".format(str(list_to_check)[1:-1])

테스트 Python=3.6


이 솔루션은 제공된 list_to_checkSQL이 이스케이프되지 않기 때문에 SQL 주입 공격에 취약합니다 . 이것이 값을 매개 변수로 전달하는 execute것이 더 적절한 이유 입니다. 누군가이 솔루션을 사용하여 시스템을 공격하고 데이터베이스에 액세스 할 수 있으므로이 솔루션을 매우 신중하게 사용하십시오 (즉, 입력 ID는 애플리케이션 외부에서 매개 변수로 수신되지 않음).
Rubms

-2

목록 이해를 사용하는 또 다른 간단한 솔루션 :

# creating a new list of strings and convert to tuple
sql_list = tuple([ key.encode("UTF-8") for key in list_of_ids ])

# replace "{}" with "('id1','id2',...'idlast')"
cursor.execute("DELETE FROM foo.bar WHERE baz IN {}".format(sql_list))

-4
list_of_ids = [ 1, 2, 3]
query = "select * from table where x in %s" % str(tuple(list_of_ids))
print query

이것은 쿼리 문자열을 완성하기 위해 인수를 전달해야하는 메서드에 관심이없고 그냥 호출하려는 경우 일부 사용 사례에서 작동 할 수 있습니다. cursror.execute(query) .

다른 방법은 다음과 같습니다.

"select * from table where x in (%s)" % ', '.join(str(id) for id in list_of_ids)

-7

매우 간단합니다. 아래 구성을 사용하십시오.

rules_id = [ "9", "10"]

sql1 = "SELECT * FROM attendance_rules_staff WHERE id in ("+ ",".join (map (str, rules_id)) + ")"

",".join (map (str, rules_id))


어디에서 SQL 인용을 수행하며 바인드 변수 대신 리터럴을 사용하지 않습니까?
eckes

필요하지 않으며 단순히 잘 작동합니다. 튜플 형성은 첫 번째 중괄호 ( "9", "10")가있는 문자열로 직접 변환 되었기 때문에 테스트 할 수 있습니다. SQL 형성을 조정합니다. 당신이 만드는 다른 형성을 필요가 없습니다 SQL adjastable입니다
미 잔어 라흐만

1
및에 rules_id포함 된 경우 "); DROP TABLES Bobby --?
eckes

이미 수 없습니다 ")"목록을 내파 "말했다 ... 그래서 쿼리하기 전에 유효성을 검사 할 필요가
미 잔어 라흐만

또는 다음을 사용하십시오. sql1 = "SELECT * FROM attendance_rules_staff WHERE id in ("+ ",".join (map (str, rules_id)) + ")"
Mizanur Rahman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.