[sql] 매개 변수화 된 명령문이 모든 SQL 주입을 중지 할 수 있습니까?

그렇다면 왜 여전히 많은 성공적인 SQL 주입이 있습니까? 일부 개발자가 매개 변수화 된 명령문을 사용하기에는 너무 멍청하기 때문에?



답변

질문에 대한 내 의견에 게시 한 링크는 문제를 매우 잘 설명합니다. 문제가 지속되는 이유에 대한 내 감정을 아래에 요약했습니다.

  1. 막 시작하는 사람들은 SQL 주입에 대해 인식하지 못할 수 있습니다.

  2. 일부는 SQL 인젝션을 알고 있지만 이스케이프가 유일한 솔루션이라고 생각합니다. 에 대한 빠른 Google 검색을 수행하는 경우 php mysql query나타나는 첫 번째 페이지는 mysql_query이스케이프 된 사용자 입력을 쿼리에 삽입하는 예가 있는 페이지입니다. 대신 준비된 진술을 사용하는 것에 대한 언급이 없습니다 (적어도 볼 수는 없습니다). 다른 사람들이 말했듯이 매개 변수 보간을 사용하는 튜토리얼이 너무 많아서 여전히 얼마나 자주 사용되는지는 놀라운 일이 아닙니다.

  3. 매개 변수화 된 명령문이 작동하는 방식에 대한 이해 부족. 어떤 사람들은 그것이 가치를 피하는 멋진 수단이라고 생각합니다.

  4. 다른 사람들은 매개 변수화 된 명령문을 알고 있지만 너무 느리다고 들었으므로 사용하지 마십시오. 나는 많은 사람들이 매개 변수화 된 문장이 얼마나 느린 지 들어 봤지만 실제로 그들 자신의 테스트를하지 않았다고 생각한다. Bill Karwin이 그의 강연에서 지적했듯이, 준비된 진술의 사용을 고려할 때 성능의 차이를 요인으로 거의 사용해서는 안됩니다. 한 번 준비하고 여러 번 실행하는 것의 이점은 보안 및 코드 유지 관리의 개선과 마찬가지로 종종 잊혀진 것처럼 보입니다.

  5. 일부는 모든 곳에서 매개 변수화 된 명령문을 사용하지만 테이블 및 열 이름, 키워드 및 조건부 연산자와 같은 확인되지 않은 값을 보간합니다. 사용자가 다양한 검색 필드, 비교 조건 및 정렬 순서를 지정할 수있는 것과 같은 동적 검색이 이에 대한 대표적인 예입니다.

  6. ORM을 사용할 때 잘못된 보안 감각. ORM은 여전히 ​​SQL 문 부분의 보간을 허용합니다.

  7. 프로그래밍은 크고 복잡한 주제이고 데이터베이스 관리는 크고 복잡한 주제이며 보안은 크고 복잡한 주제입니다. 안전한 데이터베이스 응용 프로그램을 개발하는 것은 쉽지 않습니다. 숙련 된 개발자도 잡힐 수 있습니다.

  8. stackoverflow에 대한 많은 답변이 도움이되지 않습니다. 사람들이 동적 SQL 및 매개 변수 보간을 사용하는 질문을 작성할 때 매개 변수화 된 명령문을 대신 사용하도록 제안하는 응답이 부족한 경우가 많습니다. 몇몇 경우에 저는 사람들이 준비된 진술을 사용하라는 제 제안을 반박했습니다. 일반적으로 허용 할 수없는 성능 오버 헤드 때문에 발생합니다. 나는 이러한 질문의 대부분을 묻는 사람들이 매개 변수화 된 명령문을 준비하는 데 걸리는 추가 몇 밀리 초가 애플리케이션에 치명적인 영향을 미치는 위치에 있다고 진지하게 의심합니다.


답변

SQL 공격을 막는 매개 변수화 된 쿼리에 대한 기사에서는 그 이유를 실제로 설명하지 못합니다. 종종 “그렇기 때문에 이유를 묻지 마십시오.”라는 경우가 종종 있습니다. 아마도 그들은 스스로를 모르기 때문일 것입니다. 나쁜 교육자의 확실한 신호는 그들이 무언가를 모른다는 것을 인정할 수 없다는 것입니다. 그러나 나는 빗나 갔다. 내가 혼란스러워하는 것이 완전히 이해할 수 있다고 말하면 간단합니다. 동적 SQL 쿼리를 상상해보십시오.

sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password

따라서 간단한 SQL 삽입은 사용자 이름을 ‘OR 1 = 1로 입력하는 것입니다. 이것은 효과적으로 SQL 쿼리를 만듭니다.

sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password

즉, 사용자 이름이 비어있는 모든 고객 ( ”) 또는 1 = 1 (부울 인 true) 인 모든 고객을 선택합니다. 그런 다음-를 사용하여 나머지 쿼리를 주석 처리합니다. 따라서 이것은 모든 고객 테이블을 인쇄하거나 원하는 모든 작업을 수행합니다. 로그인하면 관리자가 될 수있는 첫 번째 사용자의 권한으로 로그인합니다.

이제 매개 변수화 된 쿼리는 다음과 같은 코드를 사용하여 다르게 수행합니다.

sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'

parameters.add("User", username)
parameters.add("Pass", password)

여기서 사용자 이름 및 암호는 입력 된 관련 사용자 이름 및 암호를 가리키는 변수입니다.

이제이 시점에서 여러분은 이것이 전혀 변경되지 않는다고 생각할 수 있습니다. 확실히 Nobody OR 1 = 1 ‘-과 같은 사용자 이름 필드에 입력하여 효과적으로 쿼리를 작성할 수 있습니다.

sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'

그리고 이것은 유효한 주장처럼 보일 것입니다. 그러나 당신은 틀릴 것입니다.

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

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


답변

좋은 질문입니다. 대답은 결정 론적보다 확률 적이며 작은 예를 사용하여 내 견해를 설명하려고 노력할 것입니다.

SQL 주입 (SQLi)을 피하기 위해 쿼리에서 매개 변수를 사용하거나 매개 변수와 함께 저장 프로 시저를 사용하도록 제안하는 많은 참조가 네트워크에 있습니다. 저장 프로 시저 (예 :)가 SQLi에 대한 마술 지팡이가 아님을 보여 드리겠습니다. 책임은 여전히 ​​프로그래머에게 있습니다.

‘Users’테이블에서 사용자 행을 가져 오는 다음 SQL Server 저장 프로 시저를 고려하십시오.

create procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
           'from Users '+
           'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)

사용자 이름과 비밀번호를 매개 변수로 전달하여 결과를 얻을 수 있습니다. 암호가 자유 텍스트로되어 있다고 가정하면 (이 예제의 단순성을 위해) 일반적인 호출은 다음과 같습니다.

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [dbo].[getUser]
   @name = 'admin'
  ,@pass = '!@Th1siSTheP@ssw0rd!!'
GO

그러나 여기에는 프로그래머가 저장 프로 시저 내부에서 사용하는 잘못된 프로그래밍 기술이 있으므로 공격자가 다음을 실행할 수 있습니다.

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [TestDB].[dbo].[getUser]
   @name = 'admin'
  ,@pass = 'any'' OR 1=1 --'
GO

위의 매개 변수는 저장 프로 시저에 인수로 전달되며 마지막으로 실행될 SQL 명령은 다음과 같습니다.

select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'

.. 사용자로부터 모든 행을 다시 가져옵니다.

여기서 문제는 “저장 프로 시저를 만들고 필드를 매개 변수로 검색하도록 전달”원칙을 따르더라도 SQLi가 여전히 수행된다는 것입니다. 이는 저장 프로 시저 내부에 잘못된 프로그래밍 방식을 복사하기 때문입니다. 문제에 대한 해결책은 다음과 같이 저장 프로 시저를 다시 작성하는 것입니다.

alter procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass

제가 말하고자하는 것은 개발자가 먼저 SQLi 공격이 무엇인지, 어떻게 수행 할 수 있는지 배우고 그에 따라 코드를 보호해야한다는 것입니다. 맹목적으로 ‘모범 사례’를 따르는 것이 항상 더 안전한 방법은 아닙니다 … 그리고 아마도 이것이 우리가 많은 ‘모범 사례’를 가지고있는 이유입니다.


답변

예, 준비된 명령문을 사용하면 적어도 이론적으로 모든 SQL 주입이 중지됩니다. 실제로 매개 변수화 된 명령문은 실제 준비된 명령문이 아닐 수 있습니다. 예를 들어 PDOPHP에서 기본적으로 에뮬레이트하므로 엣지 케이스 공격에 노출됩니다 .

실제 준비된 진술을 사용한다면 모든 것이 안전합니다. 예를 들어 테이블 이름을 준비 할 수 없다는 반응으로 안전하지 않은 SQL을 쿼리에 연결하지 않는 한 적어도.

그렇다면 왜 여전히 많은 성공적인 SQL 주입이 있습니까? 일부 개발자가 매개 변수화 된 명령문을 사용하기에는 너무 멍청하기 때문에?

예, 교육은 여기서 주요 포인트이며 레거시 코드베이스입니다. 많은 튜토리얼은 이스케이프를 사용하며 불행히도 웹에서 쉽게 제거 할 수 없습니다.


답변

나는 프로그래밍에서 절대적인 것을 피합니다. 항상 예외가 있습니다. 저장 프로 시저와 명령 개체를 적극 권장합니다. 내 배경의 대부분은 SQL Server에 있지만 때때로 MySql을 사용합니다. 캐시 된 쿼리 계획을 포함하여 저장 프로 시저에는 많은 이점이 있습니다. 예, 이것은 매개 변수와 인라인 SQL을 사용하여 수행 할 수 있지만 주입 공격에 대한 더 많은 가능성을 열어 우려를 분리하는 데 도움이되지 않습니다. 내 응용 프로그램은 일반적으로 해당 저장 프로 시저에 대한 실행 권한 만 갖기 때문에 데이터베이스를 보호하는 것이 훨씬 쉽습니다. 직접적인 테이블 / 뷰 액세스 없이는 아무것도 주입하기가 훨씬 더 어렵습니다. 응용 프로그램 사용자가 손상되면 사전 정의 된 것을 정확하게 실행할 수있는 권한 만 있습니다.

내 2 센트.


답변

나는 “벙어리”라고 말하지 않을 것이다.

튜토리얼이 문제라고 생각합니다. 대부분의 SQL 자습서, 책, 바인드 매개 변수를 전혀 언급하지 않고 인라인 값으로 SQL을 설명합니다. 이 튜토리얼에서 배우는 사람들은 그것을 올바르게 배울 기회가 없습니다.


답변

대부분의 코드는 보안과 관리를 염두에두고 작성되지 않았기 때문에 기능 추가 (특히 판매 할 수있는 것)와 보안 / 안정성 / 신뢰성 (매출이 훨씬 더 어렵습니다) 사이에서 선택이 주어지면 거의 항상 전자. 보안은 문제가 될 때만 문제가됩니다.