SQL 주입 방지 메커니즘이 매개 변수화 된 쿼리를 사용하는 방향으로 발전한 이유는 무엇입니까?


59

내가 보는 방식으로 SQL 인젝션 공격은 다음과 같이 방지 할 수 있습니다.

  1. 신중하게 선별, 필터링, 인코딩 입력 (SQL에 삽입하기 전에)
  2. 준비된 명령문 사용 / 매개 변수화 된 쿼리

나는 각각에 장단점이 있다고 가정하지만 왜 # 2가 사출 공격을 막는 사실상의 방법으로 이륙하여 고려 되었습니까? 더 안전하고 오류가 적은 경향이 있습니까? 아니면 다른 요인이 있습니까?

내가 이해하는 것처럼 # 1이 올바르게 사용되고 모든 경고가 처리되면 # 2만큼 효과적 일 수 있습니다.

살균, 필터링 및 인코딩

살균 , 필터링인코딩의 의미가 혼동되었습니다 . 필자는 내 목적을 위해 위의 모든 옵션 1을 고려할 수 있다고 말합니다.이 경우 살균 및 필터링은 입력 데이터 를 수정하거나 버릴 가능성 있지만 인코딩 은 그대로 데이터를 보존 하지만 인코딩합니다. 주입 공격을 피할 수 있습니다. 나는 탈출하는 데이터를 인코딩하는 방법으로 간주 할 수 있다고 생각합니다.

매개 변수화 된 쿼리 및 인코딩 라이브러리

개념 parameterized queries과 개념이 encoding libraries상호 교환 적으로 취급 되는 답변이 있습니다 . 내가 틀렸다면 정정하되, 그들이 다르다는 인상을 받고 있습니다.

내가 이해 하는 것은 SQL이 RDBMS로 전송되기 전에 SQL 자체를 변경하기 때문에 SQL "프로그램"을 수정할 수 encoding libraries있는 잠재력 이 아무리 우수하더라도 항상 이해 하는 것 입니다.

Parameterized queries 반면, RDBMS에 SQL 프로그램을 전송하면 RDBMS에 쿼리를 최적화하고, 쿼리를 최적화하고, 쿼리 실행 계획을 정의하고, 사용할 인덱스를 선택하는 등의 작업을 수행 한 다음 RDBMS 내부의 마지막 단계로 데이터를 연결합니다. 그 자체.

인코딩 라이브러리

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

매개 변수화 된 쿼리

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

역사적 중요성

일부 답변에 따르면 성능상의 이유로 그리고 인젝션 공격 이전에 대상 지정 인코딩 문제가 대중화되기 전에 역사적으로 매개 변수화 된 쿼리 (PQ)가 생성되었다고합니다. 어느 시점에서 PQ가 주입 공격에 대해서도 매우 효과적이라는 것이 분명해졌습니다. 내 질문의 정신을 유지하기 위해 왜 PQ가 선택한 방법으로 남아 있었으며 SQL 주입 공격을 방지 할 때 다른 방법보다 왜 번성 했습니까?


1
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
maple_shaft

23
준비된 명령문은 SQL 주입 공격에서 진화 한 결과 가 아닙니다 . 그들은 처음부터 거기에있었습니다. 귀하의 질문은 허위 선고에 근거합니다.
user207421

4
당신이 나쁜 사람보다 똑똑하다고 생각하면 # 1로 이동
파파 라조

1
"PQ는 왜 가장 좋은 방법으로 남아 있었습니까?"가장 쉽고 강력하기 때문입니다. 앞서 언급 한 PQ에 대한 성능 이점. 실제로 단점은 없습니다.
Paul Draper

1
보안 컨텍스트에서 SQL 삽입 문제가 아니더라도 쿼리 수행 방법 문제에 대한 올바른 솔루션이기 때문 입니다. 이스케이프 처리가 필요하고 명령과 함께 대역 내 데이터를 사용해야하는 양식 은 오류가 발생하기 쉽고 반 직관적이며 잘못 사용하면 나 빠지기 때문에 항상 디자인 버그 입니다. 쉘 스크립팅도 참조하십시오.
R ..

답변:


147

문제는 # 1은 작업중인 SQL 변형 전체를 효과적으로 구문 분석하고 해석해야하므로 원하지 않는 일을하고 있는지 알 수 있다는 것입니다. 데이터베이스를 업데이트 할 때 해당 코드를 최신 상태로 유지하십시오. 어디서나 당신은 당신의 쿼리에 대한 입력을 받아들입니다. 그리고 망치지 마십시오.

그렇습니다. 그런 종류의 일은 SQL 인젝션 공격을 막을 것이지만, 구현하는데 비용이 더 많이 듭니다.


60
@dennis-글쎄, 당신의 SQL 변형에서 인용은 무엇입니까? "? '?"? U + 2018? \ u2018? 식을 구분하는 요령이 있습니까? 하위 쿼리를 업데이트 할 수 있습니까? 고려해야 할 사항 이 많이 있습니다 .
Telastyn

7
@Dennis의 모든 DB 엔진에는 문자열에서 문자를 이스케이프 처리하는 등 자체 작업 방식이 있습니다. 특히 애플리케이션이 여러 DB 엔진에서 작동해야하거나 악용 될 수있는 사소한 쿼리 구문을 변경할 수있는 동일한 엔진의 향후 버전과 호환되어야하는 경우에는 많은 구멍이 있습니다.

12
준비된 명령문의 또 다른 장점은 동일한 쿼리를 다른 값으로 다시 실행해야 할 때 얻는 성능 향상입니다. 또한 준비된 명령문은 값이 null문자열 또는 숫자와 같은 의미인지 알 수 있으며 그에 따라 작동합니다. 이것은 보안에 매우 좋습니다. 그리고 쿼리를 한 번 실행하더라도 DB 엔진은 이미 최적화되어 있습니다. 캐시 된 경우 더 좋습니다!
Ismael Miguel

8
@Dennis Mr. Henry Null이 올바른 방법으로 감사합니다.
Mathieu Guindon

14
@Dennis의 이름은 관련이 없습니다. 문제는성에 있습니다. 참조 스택 오버플로가 , Programmers.SE은 , 폭스 스포츠 , 유선 , BBC , 그리고 어떤 다른 당신이 ;-) 빠른 Google 검색에서 설정할 수 있습니다
마티유 Guindon

80

옵션 1은 해결책이 아니기 때문입니다. 스크리닝 및 필터링은 유효하지 않은 입력을 거부하거나 제거하는 것을 의미합니다. 그러나 모든 입력이 유효 할 수 있습니다. 예를 들어 아포스트로피는 "O'Malley"라는 이름의 유효한 문자입니다. SQL에서 사용되기 전에 올바르게 인코딩되어야합니다. 이는 준비된 명령문이 수행하는 것입니다.


메모를 추가 한 후에는 기본적으로 기능적으로 유사한 코드를 처음부터 작성하지 않고 표준 라이브러리 함수를 사용하는 이유를 묻는 것 같습니다. 자신의 코드를 작성하는 데 항상 표준 라이브러리 솔루션을 선호 해야 합니다. 작업이 적고 유지 관리가 용이합니다. 이것은 모든 기능 의 경우에 해당 하지만, 특히 보안에 민감한 항목의 경우에는 휠을 독자적으로 재발 명하는 것은 절대 의미가 없습니다.


2
그게 다야 (그리고 그것은 두 개의 다른 답변에서 빠진 부분이므로 +1). 질문의 공식화 방법을 고려할 때 사용자 입력을 삭제하는 것이 아니라“입력 필터링 (삽입 전)”이라는 질문을 인용합니다. 질문이 이제 입력을 삭제하는 것에 관한 것이라면, 왜 라이브러리를 사용하지 않고 직접 수행하겠습니까?
Arseni Mourzenko

8
@Dennis : 살균 또는 필터링이란 정보를 제거하는 것을 의미 합니다. 인코딩은 정보 손실 없이 데이터 표현을 변환하는 것을 의미 합니다.
JacquesB

9
@Dennis : 필터링은 사용자 입력을 수락하거나 거부 함을 의미합니다. 예를 들어, "Jeff"는 값이 명백히 유효하지 않기 때문에 "User 's age"필드의 입력 으로 필터링 됩니다. 입력을 필터링하는 대신 작은 따옴표 문자를 바꾸는 등의 방법으로 입력을 변환하면 매개 변수화 된 쿼리를 사용하는 데이터베이스 라이브러리와 정확히 동일한 작업을 수행 하게됩니다. 이 경우, 귀하의 질문은 단순히 "모든 프로젝트에서 휠을 재발 명 할 수있을 때 현장에서 전문가가 작성하고 작성한 것을 사용하는 이유는 무엇입니까?"
Arseni Mourzenko

3
@Dennis : O\'Malley슬래시를 사용하여 올바른 삽입을 위해 인용 부호 를 하십시오 (적어도 일부 데이터베이스에서는). MS SQL 또는 Access에서는 추가 인용 부호를 사용하여 이스케이프 처리 할 수 ​​있습니다 O''Malley. 직접해야한다면 이식성이 떨어집니다.
AbraCadaver

5
시스템이 내 이름을 몇 번이나 완전히 거부했는지 알 수 없습니다. 때로는 내 이름을 사용하여 SQL 삽입으로 인한 오류를 보았습니다. 나는 백엔드에서 실제로 무언가를 깨뜨 렸기 때문에 한 번 내 사용자 이름을 변경하라는 요청을 받았습니다.
Alexander O'Mara

60

문자열 처리를 시도하는 경우 실제로 SQL 쿼리를 생성하지 않습니다. SQL 쿼리를 생성 할 수있는 문자열을 생성 중입니다. 오류 및 버그에 대한 많은 공간을 여는 간접 수준이 있습니다 . 대부분의 상황에서 프로그래밍 방식으로 무언가와 상호 작용하는 것이 행복하다는 점을 감안하면 실제로는 다소 놀랍습니다. 예를 들어 목록 구조가 있고 항목을 추가하려는 경우 일반적으로 다음을 수행하지 않습니다.

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

누군가가 그렇게 제안한다면, 당신은 그것이 우스운 일이며 올바르게 행동해야한다고 올바르게 대답 할 것입니다.

List<Integer> list = /* ... */;
list.add(5, position=2);

이는 개념적 수준에서 데이터 구조와 상호 작용합니다. 해당 구조를 인쇄하거나 구문 분석하는 방법에 대한 의존성은 없습니다. 그것들은 완전히 직교 결정입니다.

첫 번째 접근 방식은 첫 번째 샘플과 비슷합니다 (약간 더 나쁩니다) : 원하는 쿼리로 올바르게 구문 분석 될 문자열을 프로그래밍 방식으로 구성 할 수 있다고 가정합니다. 이것은 파서와 문자열 처리 로직에 달려 있습니다.

준비된 쿼리를 사용하는 두 번째 방법은 두 번째 샘플과 훨씬 비슷합니다. 준비된 쿼리를 사용할 때는 본질적으로 합법적이지만 자리 표시자가 포함 된 의사 쿼리를 구문 분석 한 다음 API를 사용하여 일부 값을 올바르게 대체합니다. 더 이상 구문 분석 프로세스가 필요하지 않으며 문자열 처리에 대해 걱정할 필요가 없습니다.

일반적으로 개념 수준에서 사물과 상호 작용하는 것이 훨씬 쉽고 오류가 적습니다. 쿼리는 문자열이 아니며 쿼리는 문자열을 구문 분석하거나 프로그래밍 방식으로 문자열을 생성하거나 다른 방법으로 만들 수 있습니다.

간단한 텍스트 대체를 수행하는 C 스타일 매크로와 임의 코드 생성을 수행하는 Lisp 스타일 매크로 사이에는 좋은 비유가 있습니다. C 스타일 매크로를 사용하면 소스 코드에서 텍스트를 바꿀 수 있으며 이는 구문 오류나 오도를 일으키는 행동을 유발할 수 있음을 의미합니다. Lisp 매크로를 사용하면 컴파일러가 처리하는 형식으로 코드를 생성합니다 (즉, 컴파일러가 처리하기 전에 독자가 처리해야하는 텍스트가 아니라 컴파일러가 처리하는 실제 데이터 구조를 반환 함) . Lisp 매크로를 사용하면 구문 분석 오류 일 수있는 것을 생성 할 수 없습니다. 예를 들어 (((ab) a)를 생성 할 수 없습니다 .

Lisp 매크로를 사용해도 여전히 잘못된 코드를 생성 할 수 있습니다. 왜냐하면 반드시 존재해야하는 구조를 알 필요가 없기 때문입니다. 예를 들어, Lisp에서 (let ((ab)) a) 는 "변수 a의 변수 a를 변수 b의 값에 대한 새로운 어휘 바인딩을 설정 한 다음 a의 값을 반환합니다"를 의미하며, (let (ab) a) 는 "변수 a와 b의 새로운 어휘 바인딩을 설정하고 둘 다 nil로 초기화 한 다음 a의 값을 리턴합니다." 그것들은 구문 적으로 정확하지만 다른 것을 의미합니다. 이 문제를 피하기 위해보다 의미 인식 기능을 사용하고 다음과 같은 작업을 수행 할 수 있습니다.

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

그런 식으로 구문 상 유효하지 않은 것을 반환하는 것은 불가능 하며 실수로 원하지 않는 것을 반환하는 것이 훨씬 어렵 습니다.


좋은 설명입니다!
Mike Partridge

2
당신은 "좋은 유추"에서 나를 잃었지만 이전의 설명에 근거하여 찬성했습니다. :)
와일드 카드

1
훌륭한 예입니다! -그리고 당신은 추가 할 수 있습니다 : 데이터 유형에 따라 구문 분석 가능한 문자열을 만드는 것이 불가능하거나 때로는 불가능합니다. -매개 변수 중 하나가 스토리 초안 (~ 10.000 자)을 포함하는 자유 텍스트 필드 인 경우 어떻게됩니까? 또는 하나의 매개 변수가 JPG-Image 인 경우 어떻게해야 합니까? -유일한 방법은 매개 변수화 된 쿼리입니다.
Falco

실제로 아니요-준비된 명령문이 SQL 주입에 대한 방어로 진화 한 이유에 대한 아주 나쁜 설명입니다. 특히 코드 예제는 java로 주어졌으며 C / C ++이 최첨단으로 간주되는 시간대에서 매개 변수가있는 쿼리가 개발되었을 때 주변에 없었습니다. SQL 데이터베이스는 1970-1980 년 초에 사용되기 시작했습니다. 더 높은 수준의 언어를 사용하기 전에 대중화하십시오. 도대체 많은 사람들이 데이터베이스 작업을 더 쉽게하기 위해 왔다고 말할 것입니다 (PowerBuilder 누구?)
TomTom

@TomTom은 사실 대부분의 콘텐츠에 동의합니다. 여기서는 보안 측면 만 묵시적으로 다루었습니다. 그래서 많은 SPARQL (SQL과 비슷한 RDF 쿼리 언어) 질문에 대답하고 많은 사람들이 매개 변수가있는 쿼리를 사용하지 않고 문자열을 연결하기 때문에 문제가 발생합니다. 주입 공격이 없어도 매개 변수화 된 쿼리는 버그 / 충돌을 피하는 데 도움이되며, 버그 / 충돌은 주입 공격이 아닌 경우에도 보안 문제가 될 수 있습니다. 그래서 저는 점점 더 많이 말하고 싶습니다. SQL 주입이 문제가되지 않았더라도 매개 변수화 된 쿼리는 훌륭합니다.
Joshua Taylor

21

데이터베이스가 매개 변수화되지 않은 버전의 쿼리를 캐시 할 수 있기 때문에 옵션 # 2가 일반적으로 모범 사례로 간주됩니다. 매개 변수가있는 쿼리는 몇 년 전에 SQL 주입 문제보다 오래되었으므로 (한 번에) 돌 하나로 두 마리의 새를 죽일 수 있습니다.


10
SQL이 처음 발명 된 이후로 SQL 주입이 문제였습니다. 나중에 문제가되지 않았습니다.
Servy

9
@Servy 이론적으로 그렇습니다. 실제로 입력 메커니즘이 온라인 상태가되었을 때만 실제 문제가되어 누군가가 망치는 거대한 공격 표면을 제시했습니다.
Jan Doggen

8
Little Bobby Tables 는 SQL 주입을 활용하기 위해 인터넷이나 대규모 사용자 기반이 필요하다는 데 동의하지 않습니다. 물론 네트워크는 SQL 이전의 날짜 이므로 SQL이 나오면 네트워크를 기다릴 필요가 없습니다. 예, 보안 취약점은 적은 응용 프로그램이 작은 사용자 기반이있는 경우 취약하지만, 그들은 여전히 보안 취약점을있어, 사람들은 않습니다 데이터베이스 자체가 중요한 데이터를 가지고 (많은 경우이를 악용 아주 초기 데이터베이스가 매우 중요한 데이터를 가지고 만 명으로, 귀중한 데이터베이스가 기술을 감당할 수있다.)
Servy

5
@ 내가 아는 한, 동적 SQL은 비교적 늦은 기능이었습니다. SQL의 초기 사용은 대부분 값에 대한 매개 변수 (입 / 출력)를 사용하여 사전 컴파일 / 사전 처리되었으므로 쿼리의 매개 변수는 소프트웨어에서 SQL 삽입보다 먼저 수행 될 수 있습니다 (임시 / CLI 쿼리에는 없을 수 있음).
Mark Rotteveel

6
이들은 SQL 삽입에 대한 인식을 앞설 수 있습니다 .
user253751

20

간단히 말해서 : 그들은하지 않았습니다. 귀하의 진술 :

SQL 주입 방지 메커니즘이 매개 변수화 된 쿼리 사용 방향으로 발전한 이유는 무엇입니까?

근본적으로 결함이 있습니다. 매개 변수가있는 쿼리는 SQL 주입이 적어도 널리 알려진 것보다 오래 존재했습니다. LOB (Line of Business) 응용 프로그램의 일반적인 "검색 양식"기능에서 문자열 집중을 피하는 방법으로 개발되었습니다. 몇 년 후, 누군가가 문자열 조작과 관련된 보안 문제를 발견했습니다.

25 년 전 (인터넷이 널리 사용되지 않았을 때 – 방금 시작했을 때) SQL을 수행 한 것을 기억하고 SQL과 IBM DB5 IIRC 버전 5를 비교 한 것을 기억합니다.


감사. 문자열 연결을 피해야하는 이유는 무엇입니까? 유용한 기능이 될 것 같습니다. 누군가 문제가 있었습니까?
Dennis

3
실제로 두. 첫째, 항상 완전히 사소한 것은 아닙니다. 왜 메모리 할당 등이 필요하지 않을 때 처리해야합니까? 그러나 두 번째로, 고대에는 성능 캐싱 SQL 데이터베이스 측면이 그다지 훌륭하지는 않았습니다. 컴파일 SQL이 비쌌습니다. 하나의 SQL 준비 명령문 (매개 변수가 제공되는 위치)을 사용하면 부작용으로 실행 계획을 재사용 할 수 있습니다. SQL Server는 자동 매개 변수화를 도입했습니다 (매개 변수가 없어도 쿼리 계획을 재사용하기 위해-공제 및 암시)-2000 또는 2007-IIRC 사이에 있다고 생각합니다.
TomTom

2
매개 변수화 된 쿼리가 있어도 문자열 연결 기능을 제거 할 수는 없습니다. 문자열 연결을 수행하여 매개 변수화 된 쿼리를 생성 할 수 있습니다. 기능이 유용하다고해서 특정 문제에 항상 좋은 선택이되는 것은 아닙니다.
JimmyJames

예, 그러나 내가 말했듯이 동적 SQL은 성능이 크게 떨어졌습니다.) 오늘날 사람들은 SQL Server의 동적 SQL 쿼리 계획이 재사용되지 않는다고 말합니다. 나는 2000과 2007 사이의 어떤 점을 말했다-그래서 QUITE long). 예전에는 SQL을 여러 번 실행하면 PREPARED 문을 원했습니다.;)
TomTom

동적 SQL 계획 캐싱에, SQL 서버 7.0에 추가 된 사실이었다 1998 - sqlmag.com/database-performance-tuning/...
마이크 Dimmick

13

다른 좋은 답변들 외에도 :

# 2가 더 좋은 이유는 코드와 데이터를 분리하기 때문입니다. # 1에서 데이터는 코드의 일부이며 모든 나쁜 것들이 나오는 곳입니다. # 1을 사용하면 쿼리를 받고 추가 단계를 수행하여 쿼리가 데이터를 데이터로 이해하도록하는 반면 # 2에서는 코드를 가져오고 코드를 가져오고 데이터는 데이터입니다.


3
코드와 데이터를 분리한다는 것은 데이터베이스 공급 업체가 적대적 코드 삽입에 대한 방어를 작성하고 테스트 함을 의미합니다. 따라서 무해한 쿼리와 함께 매개 변수로 전달 된 항목이 데이터베이스를 손상 시키거나 파괴하는 경우 데이터베이스 회사의 평판이 나빠져 조직에서 고소하여 이길 수도 있습니다. 또한 해당 코드에 악용 가능한 버그가 포함되어 있으면 다른 사이트에서 사용자가 아닌 모든 사이트가 느슨해 질 가능성이 매우 높습니다. (보안 버그 수정을 무시하지 마십시오!)
nigel222

11

SQL 주입 방어를 제공하는 것 외에도 매개 변수가있는 쿼리는 종종 한 번만 컴파일 된 다음 다른 매개 변수로 여러 번 실행되는 추가 이점이 있습니다.

보기의 SQL 데이터베이스 관점에서 select * from employees where last_name = 'Smith'select * from employees where last_name = 'Fisher'분명히 다르다 따라서 별도의 구문 분석, 컴파일 및 최적화를 필요로한다. 또한 컴파일 된 명령문 저장 전용 메모리 영역에서 별도의 슬롯을 차지합니다. 매개 변수 계산과 메모리 오버 헤드가 다른 많은 유사한 쿼리가있는로드가 많은 시스템에서는 상당한 양이 될 수 있습니다.

결과적으로 매개 변수화 된 쿼리를 사용하면 성능이 크게 향상되는 경우가 많습니다.


나는 이것이 이론이라고 생각합니다 (매개 변수화 된 쿼리에 사용 된 준비된 문장에 기초) 실제로 대부분의 구현이 한 번의 호출로 바인드-바인드 실행을 수행하므로 실제로는 종종 그런 경우가 아니라고 생각하므로 실제로 명령문 (및 라이브러리를 준비하기위한 명시적인 단계를 수행하지 않는 한 각 매개 변수화 된 쿼리에 대해 다른 준비된 명령문을 사용하십시오) 레벨 prepare은 종종 실제 SQL 레벨과 상당히 다릅니다 prepare.
jcaron

다음 쿼리도 SQL 파서 SELECT * FROM employees WHERE last_name IN (?, ?)와 다릅니다 SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick

네 그들이 갖고 있어요. WHich가 MS가 1998 년에 쿼리 계획 캐싱을 SQL Server 7에 추가 한 이유는 다음과 같습니다.
TomTom

1
@TomTom-쿼리 계획 캐싱은 힌트를주는 것처럼 보이는 자동 매개 변수화와 다릅니다. 에서와 같이 게시하기 전에 읽으십시오.
mustaccio

@mustaccio 사실 적어도 MS는 동시에 둘 다를 소개했습니다.
TomTom

5

잠깐만 왜?

옵션 1은 모든 유형의 입력에 대해 위생 처리 루틴을 작성해야하지만 옵션 2는 오류가 발생하기 쉽고 코드를 작성 / 테스트 / 유지할 수있는 코드가 적다는 것을 의미합니다.

거의 확실하게 "모든 경고를 처리" 하는 것은 생각보다 복잡 할 수 있으며, 언어 (예 : Java PreparedStatement)는 생각보다 더 중요합니다.

준비된 명령문 또는 매개 변수화 된 쿼리는 데이터베이스 서버에서 사전 컴파일되므로 매개 변수가 설정되면 쿼리가 더 이상 SQL 문자열이 아니므로 SQL 연결이 수행되지 않습니다. 추가 장점은 RDBMS가 쿼리를 캐시하고 매개 변수 값이 다양하더라도 후속 호출은 동일한 SQL로 간주되는 반면 쿼리가 다른 값으로 실행될 때마다 연결된 SQL을 사용하면 쿼리가 다르고 RDBMS가 구문 분석해야한다는 점입니다 실행 계획을 다시 작성하십시오.


1
JDBC는 혐오감을 없애지 않습니다. 프로토콜은 매개 변수에 대한 특정 부분을 가지고 있으며 DB는 단순히 해당 매개 변수를 해석하지 않으므로 매개 변수에서 테이블 이름을 설정할 수 있습니다.
talex

1
왜? 매개 변수가 구문 분석되거나 해석되지 않으면 무언가를 벗어날 이유가 없습니다.
talex

11
매개 변수가있는 쿼리의 작동 방식에 대한 잘못된 이미지가 있다고 생각합니다. 나중에 대체되는 매개 변수의 경우가 아니라에서 대체 되지 않습니다 . DBMS는 모든 쿼리를 "계획", 결과를 얻기 위해 실행하는 일련의 단계로 바꿉니다. 매개 변수화 된 쿼리에서 해당 계획은 함수와 같습니다. 실행시 제공해야하는 많은 변수가 있습니다. 변수가 제공 될 때까지 SQL 문자열은 완전히 잊혀지고 계획은 제공된 값으로 실행됩니다.
IMSoP

2
@IMSoP 그것은 나의 오해였습니다. SO stackoverflow.com/questions/3271249/… 에서이 질문에 가장 많이 투표 된 두 가지 답변에서 볼 수 있듯이 일반적인 것으로 생각합니다 . 나는 그것에 대해 읽었고 당신은 옳습니다. 나는 대답을 편집했다.
Tulains Córdova

3
@TomTom 성능 에는 좋지만 보안 에는 영향을 미치지 않습니다 . 손상된 동적 SQL 조각이 컴파일되고 캐시 될 때 까지 프로그램이 이미 변경되었습니다 . 비 동적 매개 변수화 된 SQL에서 계획을 작성하고 데이터 요소를 전달하는 것은 완전한 SQL 문자열로 제시된 두 쿼리 간의 유사성을 추상화하는 DBMS와 여전히 근본적으로 다릅니다.
IMSoP

1

이상적인 "위생, 필터링 및 인코딩"방식이 어떤 것인지 상상해 봅시다.

살균 및 필터링은 특정 응용 프로그램의 맥락에서 의미가있을 수 있지만 궁극적으로 "데이터를 데이터베이스에 넣을 수 없습니다"라는 말로 귀결됩니다. 응용 프로그램의 경우 좋은 아이디어 일 수 있지만 데이터베이스에 임의의 문자를 저장할 수 있어야하는 응용 프로그램이 있기 때문에 일반적인 솔루션으로 권장 할 수있는 것은 아닙니다.

따라서 인코딩을 남깁니다. 이스케이프 문자를 추가하여 문자열을 인코딩하는 함수를 사용하여 시작할 수 있으므로 직접 대체 할 수 있습니다. 다른 데이터베이스가 다른 문자 탈출이 필요하기 때문에 (일부 데이터베이스에 모두 \'''유효 이스케이프 시퀀스는 '있지만 다른 사람),이 기능은 데이터베이스 공급 업체에 의해 제공 될 필요가있다.

그러나 모든 변수가 문자열 인 것은 아닙니다. 때로는 정수 또는 날짜로 대체해야합니다. 이들은 문자열과 다르게 표시되므로 다른 인코딩 방법이 필요하며 (다시 말해서 데이터베이스 공급 업체에 따라 달라야 함) 다른 방법으로 쿼리로 대체해야합니다.

따라서 데이터베이스가 대체를 처리하는 경우 상황이 더 쉬울 수 있습니다. 쿼리에서 예상하는 유형, 데이터를 안전하게 인코딩하는 방법 및 쿼리로 안전하게 대체하는 방법을 이미 알고 있으므로 걱정할 필요가 없습니다. 당신의 코드에서.

이 시점에서 매개 변수화 된 쿼리를 다시 발명했습니다.

쿼리가 매개 변수화되면 성능 최적화 및 단순화 된 모니터링과 같은 새로운 기회가 열립니다.

인코딩은 올바르게 수행하기 어렵고 인코딩 완료는 매개 변수화와 구별 할 수 없습니다.

당신이 정말로 건물 쿼리의 방법으로 문자열 보간과 같은 플러그 문자열 보간이 언어 (스칼라와 ES2015은 마음에 와서)의 부부가, 그렇다면 있는 라이브러리 , 당신은 문자열 보간과 같이 매개 변수화 쿼리를 작성할 수 있지만, ES2015 구문에서 SQL 주입으로부터 안전합니다.

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"인코딩은 제대로하기 어렵다"-하하하. 그렇지 않습니다. 하루나 이틀, 모두 문서화되어 있습니다. 몇 년 전 ORM에 대해 인코더를 작성했습니다 (sql 서버에는 매개 변수에 제한이 있으므로 15 년 전의 한 문에 5000-10000 개의 행을 삽입하는 데 문제가 있기 때문에 큰 문제라는 것을 기억하지 못합니다.
TomTom

1
아마도 SQL Server는 문제가되지 않을 정도로 규칙적이지만 다른 DB에서 문제가 발생했습니다. 문자 인코딩이 일치하지 않거나 구성 옵션이 모호해지며 로케일 특정 날짜 및 숫자 문제가 있습니다. 모든 해결할 수 있지만 DB의 단점에 대해 최소한의 이해가 필요합니다 (MySQL 및 Oracle을보고 있습니다).
James_pic

3
@TomTom Encoding은 시간을 고려하면 실제로 얻기가 매우 어렵습니다. DB 공급 업체가 다음 릴리스에서 새 주석 스타일을 만들거나 업그레이드시 베어 키워드가 새 키워드가 될 때 수행하는 작업은 무엇입니까? 이론적으로 RDBMS의 한 릴리스에 맞는 인코딩을 실제로 얻을 수 있으며 다음 개정에서는 잘못 될 수 있습니다. 비표준 구문을 사용하여
Eric

@ 에릭, 솔직히 끔찍합니다. (저는 Postgres를 사용합니다. 만약 그런 기괴한 사마귀가 있다면 아직 만나지 못했습니다.)
Wildcard

0

옵션 1에서는 size = infinity의 입력 세트를 사용하여 매우 큰 출력 크기로 맵핑하려고합니다. 옵션 2에서는 입력 내용을 원하는대로 제한했습니다. 다시 말해:

  1. [ 모든 안전한 SQL 쿼리 ]에 대해 신중하게 선별 및 필터링 [ 무한대 ]
  2. [ 귀하의 범위로 제한되는 사전 고려 시나리오 ] 사용

다른 답변에 따르면 범위를 무한대에서 멀리하고 관리 가능한 것으로 제한하면 성능상의 이점이있는 것으로 보입니다.


0

SQL (특히 현대 방언)의 유용한 정신 모델 중 하나는 각 SQL 문이나 쿼리가 프로그램이라는 것입니다. 기본 이진 실행 프로그램에서 가장 위험한 종류의 보안 취약점은 공격자가 다른 명령으로 프로그램 코드를 덮어 쓰거나 수정할 수있는 오버플로입니다.

SQL 주입 취약점은 C와 같은 언어의 버퍼 오버 플로우에 대해 동형입니다. 히스토리에 따르면 버퍼 오버 플로우는 예방하기가 매우 어렵습니다. 공개 검토 대상인 매우 중요한 코드조차도 종종 이러한 취약점을 포함하고 있습니다.

오버플로 취약점을 해결하기위한 현대적인 접근 방식의 한 가지 중요한 측면은 특정 메모리 부분을 실행 불가능으로 표시하고 다른 메모리 부분을 읽기 전용으로 표시하기 위해 하드웨어 및 OS 메커니즘을 사용하는 것입니다. ( 예를 들어, 실행 가능한 공간 보호 에 관한 Wikipedia 기사를 참조하십시오 .) 이렇게하면 공격자가 데이터를 수정할 수 있다고해도 침입자는 주입 된 데이터를 코드로 취급 할 수 없습니다.

따라서 SQL 주입 취약점이 버퍼 오버플로와 동일하다면 SQL은 NX 비트 또는 읽기 전용 메모리 페이지에 해당합니까? 대답은 : 준비된 문 이 아닌 쿼리 요청에 대한 매개 변수화 된 쿼리 플러스 유사한 메커니즘을 포함한다. 준비된 명령문은 읽기 전용으로 표시된 특정 부분으로 컴파일되므로 공격자는 프로그램의 해당 부분과 실행 불가능한 데이터 (준비된 명령문의 매개 변수)로 표시된 다른 부분을 변경할 수 없습니다. 공격자는 데이터를 삽입 할 수는 있지만 프로그램 코드로 취급되지 않으므로 남용 가능성이 거의 없습니다.

확실히, 사용자 입력을 삭제하는 것은 좋지만 실제로 보안을 유지하려면 편집증 (또는 공격자처럼 생각해야 함)이 필요합니다. 프로그램 텍스트 외부의 제어 영역 이이를 수행하는 방법이며 준비된 명령문은 SQL에 해당 제어 영역을 제공합니다. 따라서 준비된 명령문과 매개 변수화 된 쿼리가 대다수의 보안 전문가가 권장하는 접근 방식이라는 것은 놀라운 일이 아닙니다.


이것은 훌륭하고 멋지지만 제목별로 질문을 다루지는 않습니다.
TomTom

1
@TomTom : 무슨 뜻인가요? 문제는 매개 변수화 된 쿼리가 SQL 삽입을 방지하기 위해 선호되는 메커니즘 인 이유에 관한 것입니다. 내 대답은 매개 변수가있는 쿼리가 사용자 입력을 삭제하는 것보다 더 안전하고 강력한 이유를 설명합니다.
Daniel Pryden

죄송합니다. "SQL 주입 방지 메커니즘이 매개 변수화 된 쿼리를 사용하는 방향으로 진화 한 이유는 무엇입니까?"라는 질문이 있습니다. 그들은하지 않았다. 그것은 지금에 관한 것이 아니라 역사에 관한 것입니다.
TomTom

0

나는 이것에 대해 이미 여기에 썼습니다 : https : //.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

그러나 간단하게 유지하려면 다음을 수행하십시오.

매개 변수가있는 쿼리가 작동하는 방식은 sqlQuery가 쿼리로 전송되고 데이터베이스는이 쿼리가 수행 할 작업을 정확히 인식 한 다음 사용자 이름과 암호를 단순히 값으로 삽입하는 것입니다. 이는 데이터베이스가 쿼리의 기능을 이미 알고 있기 때문에 쿼리에 영향을 줄 수 없음을 의미합니다. 따라서이 경우 "Nobody OR 1 = 1 '-"라는 사용자 이름과 빈 암호를 찾아야합니다.

이것은 완전한 해결책은 아니며, 여전히 자바 스크립트를 데이터베이스에 넣을 수 있기 때문에 XSS 공격과 같은 다른 문제에는 영향을 미치지 않기 때문에 입력 검증이 여전히 수행되어야합니다. 그런 다음 이것이 페이지에서 읽 히면 출력 유효성 검사에 따라 일반 자바 스크립트로 표시됩니다. 따라서 실제로 가장 좋은 방법은 여전히 ​​입력 유효성 검사를 사용하지만 매개 변수화 된 쿼리 또는 저장 프로 시저를 사용하여 SQL 공격을 중지하는 것입니다.


0

나는 SQL을 사용한 적이 없다. 그러나 분명히 사람들이 가진 문제에 대해 듣고 SQL 개발자는이 "SQL 주입"에 문제가 있습니다. 오랫동안 나는 그것을 알아낼 수 없었다. 그런 다음 문자열을 연결하여 SQL 문, 실제 텍스트 SQL 소스 문을 만드는 사람들이 사용자가 입력 한 곳을 알았습니다. 그리고 그 실현에 대한 나의 첫 생각은 충격이었습니다. 총 충격. 나는 생각했다 : 어떻게 그렇게 바보 같은 바보 같은 프로그래밍 언어로 진술을 만들 수 있습니까? C, C ++, Java 또는 Swift 개발자에게는 이것이 완전히 광기입니다.

즉, C 문자열을 인수로 사용하는 C 함수를 작성하는 것은 그리 어렵지 않으며 동일한 문자열을 나타내는 C 소스 코드의 문자열 리터럴과 정확히 같은 다른 문자열을 생성합니다. 예를 들어,이 함수는 abc를 "abc"로, "abc"를 "\"abc \ ""로, "\"abc \ ""를 "\"\\ "abc \\"\ ""로 변환합니다. (글쎄, 이것이 잘못 보이면 html입니다. 입력 한 것이 맞았지만 표시되지는 않았습니다) C 함수가 작성된 후에는 C 소스 코드를 생성하는 것이 어렵지 않습니다. 사용자가 제공 한 입력 필드의 텍스트는 C 문자열 리터럴로 바뀝니다. 안전하기는 어렵지 않습니다. SQL 개발자가 SQL 주입을 피할 수있는 방법으로이 방법을 사용하지 않는 이유는 저에게 있습니다.

"살균"은 완전히 결함이있는 접근법입니다. 치명적인 결함은 특정 사용자 입력이 불법이라는 것입니다. 일반 텍스트 필드는 다음과 같은 텍스트를 포함 할 수없는 데이터베이스로 끝납니다. Drop 테이블 또는 SQL 인젝션에 사용하는 모든 것이 손상을 유발합니다. 나는 그것을 받아 들일 수없는 것을 안다. 데이터베이스가 텍스트를 저장 하면 모든 텍스트 를 저장할 수 있어야 합니다. 그리고 실용적인 결함은 소독제가 제대로 얻을 수없는 것입니다 :-(

물론 매개 변수화 된 쿼리는 컴파일 된 언어를 사용하는 모든 프로그래머가 기대하는 것입니다. 삶을 훨씬 쉽게 만듭니다. 문자열 입력이 있고 SQL 문자열로 변환하는 것을 귀찮게하지 않지만 해당 문자열의 문자가 손상되지 않도록 매개 변수로 전달하면됩니다.

따라서 컴파일 된 언어를 사용하는 개발자의 관점에서 보면 살균은 결코 일어나지 않을 것입니다. 살균의 필요성은 미쳤다. 매개 변수화 된 쿼리는 문제에 대한 확실한 솔루션입니다.

(Josip의 답변이 흥미 롭다는 것을 알았습니다. 그는 기본적으로 매개 변수가있는 쿼리를 사용하면 SQL에 대한 공격을 막을 수 있지만 데이터베이스에 JavaScript 삽입을 만드는 데 사용되는 텍스트를 가질 수 있다고 말합니다. Javascript에 해결책이 있는지 모르겠습니다.


-2

주요 문제는 해커가 위생을 둘러 쌀 수있는 방법을 찾았고 매개 변수화 된 쿼리는 성능과 메모리의 추가 이점과 완벽하게 작동하는 기존 절차라는 점입니다.

어떤 사람들은 "단일 인용 부호와 큰 인용 부호"로 문제를 단순화하지만 해커들은 다른 인코딩을 사용하거나 데이터베이스 기능을 사용하는 것과 같은 탐지를 피할 수있는 현명한 방법을 발견했습니다.

어쨌든 치명적인 데이터 유출을 발생시키기 위해 하나의 단일 문자열 만 잊어 버리면됩니다. 스크립트를 자동화하여 일련의 데이터베이스 나 쿼리로 전체 데이터베이스를 다운로드 할 수있는 해커. 소프트웨어가 오픈 소스 제품군 또는 유명한 비즈니스 제품군과 같이 잘 알려진 경우 사용자 및 비밀번호 테이블을 간단히 사용할 수 있습니다.

반면에 연결된 쿼리를 사용하는 것은 사용법을 익히고 익숙해지는 문제였습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.