[c#] 매개 변수가 문자열이 아닌 경우 SQL 쿼리를 매개 변수화하지 않는 것이 안전합니까?

SQL 주입 측면 에서 매개 변수를 매개 변수화해야하는 필요성을 완전히 이해합니다 string. 그것은 책에서 가장 오래된 속임수 중 하나입니다. 그러나 매개 변수화 하지 않는 것이 언제 정당화 될 수 SqlCommand있습니까? 매개 변수화하지 않는 “안전한”데이터 유형이 있습니까?

예를 들면 : 난 아무데도 자신을 생각하지 않는다 근처 SQL 전문가,하지만 난 받아 SQL 주입에 잠재적으로 취약 할 것입니다 어떤 경우 생각할 수 없다 bool거나를 int하고 바로 질의로를 연결할.

내 가정이 맞습니까, 아니면 잠재적으로 내 프로그램에 큰 보안 취약점을 남길 수 있습니까?

설명을 위해이 질문에는 태그가 있습니다. 강력한 형식의 언어입니다. “매개 변수”라고 말하면 public int Query(int id) .



답변

기술적으로 는 안전하다고 생각 합니다만, 들어가는 것은 끔찍한 습관입니다. 정말로 이와 같은 쿼리를 작성하고 싶습니까?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive +
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

또한 유형이 정수에서 문자열로 변경되는 상황에서도 취약합니다 (이름에도 불구하고 문자를 포함 할 수있는 직원 번호를 생각해보십시오).

따라서 EmployeeNumber 유형을에서 int으로 변경 string했지만 SQL 쿼리를 업데이트하는 것을 잊었습니다. 죄송합니다.


답변

제어하는 컴퓨터 (예 : 웹 서버)에서 강력한 형식의 플랫폼을 사용하는 bool경우 DateTime, 또는 int(및 기타 숫자) 값만있는 쿼리에 대한 코드 삽입을 방지 할 수 있습니다 . 문제는 SQL Server가 모든 쿼리를 다시 컴파일하도록 강제하고 어떤 쿼리가 어떤 빈도로 실행되는지에 대한 좋은 통계를 얻지 못하도록함으로써 발생하는 성능 문제입니다 (캐시 관리에 문제가 있음).

그러나 “컴퓨터에서 제어하는”부분은 중요합니다. 그렇지 않으면 사용자가 임의의 텍스트를 포함하도록 해당 값에서 문자열을 생성하기 위해 시스템에서 사용하는 동작을 변경할 수 있기 때문입니다.

장기적으로 생각하는 것도 좋아합니다. 오늘날의 오래되고 파열 된 강력한 유형의 코드 기반이 자동 번역을 통해 새로운 인기 동적 언어로 이식되고 갑자기 유형 검사를 잃어 버리지 만 동적 코드에 대한 올바른 단위 테스트가 아직 없을 때 어떻게됩니까? ?

실제로 이러한 값에 쿼리 매개 변수를 사용하지 않을 이유가 없습니다. 이 문제를 해결하는 올바른 방법 입니다. 값이 실제로 상수 일 때 SQL 문자열에 하드 코딩하십시오. 그렇지 않으면 매개 변수를 사용하지 않는 이유는 무엇입니까? 어려운 게 아니에요.

궁극적으로 나는 이것을 버그 라고 부르지 않을 것이지만 냄새 라고 부를 것입니다. 그 자체로는 버그에 미치지 못하지만 버그가 근처에 있거나 결국있을 것이라는 강력한 표시입니다. 좋은 코드는 냄새를 남기지 않으며 좋은 정적 분석 도구는이를 표시합니다.

불행히도 이것은 당신이 바로 이길 수있는 종류의 논쟁이 아니라고 덧붙일 것입니다. “올바른”것으로는 더 이상 충분하지 않은 상황처럼 들리며,이 문제를 스스로 해결하기 위해 동료의 발걸음을 밟는 것은 좋은 팀 역학을 촉진하지 못할 것입니다. 궁극적으로 도움이되는 것보다 더 많은 상처를 입을 수 있습니다. 이 경우 더 나은 접근 방식은 정적 분석 도구의 사용을 촉진하는 것입니다. 그것은 목표로하고 되돌아 가고 기존 코드를 수정하는 노력에 정당성과 신뢰성을 줄 것입니다.


답변

어떤 경우에는 문자열 값이 아닌 매개 변수화되지 않은 (연결된) 변수로 SQL 주입 공격을 수행 할 수 있습니다. Jon의이 기사를 참조하십시오. http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables -culture / .

문제는를 ToString호출하면 일부 사용자 지정 문화 공급자가 문자열이 아닌 매개 변수를 문자열 표현으로 변환하여 일부 SQL을 쿼리에 삽입 할 수 있다는 것입니다.


답변

입니다 하지 심지어 문자열이 아닌 유형에 대한 안전합니다. 항상 매개 변수를 사용하십시오. 기간.

다음 코드 예제를 고려하십시오.

var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");

언뜻보기에 코드는 안전 해 보이지만 Windows 국가 별 설정을 일부 변경하고 짧은 날짜 형식으로 삽입을 추가하면 모든 것이 변경됩니다.

날짜 시간 주입

이제 결과 명령 텍스트는 다음과 같습니다.

SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'

intSQL 인젝션으로 쉽게 변경할 수있는 사용자 정의 음의 부호를 사용자 정의 할 수 있으므로 유형에 대해서도 동일하게 수행 할 수 있습니다.

현재 문화 대신 불변 문화를 사용해야한다고 주장 할 수 있지만 이와 같은 문자열 연결을 너무 많이 보았고 +.


답변

“SELECT * FROM Table1 WHERE Id =”+ intVariable.ToString ()


보안
괜찮습니다.
공격자는 입력 한 int 변수에 아무것도 삽입 할 수 없습니다.

성능이
좋지 않습니다.

매개 변수를 사용하는 것이 더 좋으므로 쿼리가 한 번 컴파일되고 다음 사용을 위해 캐시됩니다. 다음에 다른 매개 변수 값을 사용해도 쿼리가 캐시되고 데이터베이스 서버에서 컴파일 할 필요가 없습니다.

코딩 스타일
나쁜 습관.

  • 매개 변수가 더 읽기 쉽습니다.
  • 매개 변수가없는 쿼리에 익숙해 지거나 한 번 실수를해서 이런 식으로 문자열 값을 사용한 다음 데이터에 작별 인사를해야 할 수도 있습니다. 나쁜 습관!

“SELECT * FROM Product WHERE Id =”+ TextBox1.Text


귀하의 질문은 아니지만 향후 독자에게 유용 할 수 있습니다.

보안
재난! 필드가 정수인
경우에도 Id쿼리에 SQL 주입이 적용될 수 있습니다. 애플리케이션에 쿼리가 있다고 가정하면 "SELECT * FROM Table1 WHERE Id=" + TextBox1.Text공격자가 텍스트 상자에 삽입 할 수 1; DELETE Table1있으며 쿼리는 다음과 같습니다.

"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"

여기서 매개 변수화 된 쿼리를 사용하지 않으려면 입력 된 값을 사용해야합니다.

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))

귀하의 질문


내 질문은 동료가 정수 값을 연결하는 많은 쿼리를 작성했기 때문에 발생했으며, 모든 것을 검토하고 수정하는 것이 내 시간 낭비인지 궁금했습니다.

이 코드를 변경하는 것은 시간 낭비가 아니라고 생각합니다. 과연 변화를 추천합니다!

동료가 int 변수를 사용하는 경우 보안 위험은 없지만 해당 코드를 변경하는 것은 시간 낭비가 아니며 실제로 해당 코드를 변경하는 것이 좋습니다. 이는 코드를 더 읽기 쉽고, 유지 관리하기 쉽게 만들고, 실행 속도를 더 빠르게합니다.


답변

실제로 하나에 두 가지 질문이 있습니다. 그리고 제목의 질문은 이후 의견에서 OP가 표현한 우려와 거의 관련이 없습니다.

OP의 경우 중요한 것은 그들의 특별한 경우라는 것을 알고 있지만, Google에서 오는 독자에게는보다 일반적인 질문에 대답하는 것이 중요합니다. 내가 연결하는 모든 문자가 안전하다는 것을? “. 그래서 저는이 후자에 집중하고 싶습니다. 그리고 대답은

확실히 아니오.

대부분의 독자들이 바라는 것처럼 직접적인 설명은 아니지만 최선을 다하겠습니다.

나는 그 문제에 대해 잠시 숙고 해 왔고, 그 결과 모든 것을 요약하려고 노력한 기사 (PHP 환경을 기반으로 함)가 나왔다. SQL 인젝션으로부터의 보호에 대한 질문은 종종 문자열 이스케이프, 타입 캐스팅 등과 같은 관련이 있지만 더 좁은 주제로 빠져 나간다는 생각이 들었습니다. 일부 조치는 스스로 취하면 안전한 것으로 간주 될 수 있지만 시스템이 없으며 따라야 할 간단한 규칙도 없습니다. 이로 인해 개발자의 관심과 경험에 너무 많은 영향을 미치면서 매우 미끄러운 땅이됩니다.

SQL 주입 문제는 특정 구문 문제로 단순화 할 수 없습니다. 일반적인 개발자가 생각하는 것보다 넓습니다. 그것은의 방법 론적 아니라 질문입니다. “적용해야하는 특정 서식”뿐만 아니라 ” 어떻게 수행해야 하는가 “도 중요합니다.

(이 관점에서 다른 답변에 인용 된 Jon Skeet의 기사는 일부 엣지 케이스를 다시 골라 내고 특정 구문 문제에 집중하고 문제를 전체적으로 해결하지 못하기 때문에 좋지 않은 것입니다.)

보호 문제를 전체가 아니라 다양한 구문 문제로 해결하려고 할 때 많은 문제에 직면하게됩니다.

  • 가능한 서식 선택 목록은 정말 엄청납니다. 쉽게 일부를 간과 할 수 있음을 의미합니다. 또는 ( 예를 들어 식별자문자열 이스케이프를 사용하여) 혼동하십시오 .
  • 연결은 모든 보호 조치가 프로그램이 아닌 프로그래머에 의해 수행되어야 함을 의미합니다. 이 문제만으로도 몇 가지 결과가 발생합니다.
    • 이러한 형식은 수동입니다. 수동은 오류가 매우 발생하기 쉽다 는 것을 의미 합니다. 신청하는 것을 잊을 수 있습니다.
    • 더욱이, 포맷팅 절차를 중앙 집중화 된 기능으로 옮기고, 일을 더 엉망으로 만들고, 데이터베이스로 보내지 않는 데이터를 망칠 유혹이 있습니다.
  • 둘 이상의 개발자가 참여하면 문제가 10 배로 증가합니다.
  • 연결을 사용하면 잠재적으로 위험한 쿼리를 한 눈에 알 수 없습니다. 모두 잠재적으로 위험합니다!

그 혼란과는 달리 준비된 진술은 실제로 성배입니다.

  • 따라하기 쉬운 하나의 간단한 규칙의 형태로 표현할 수 있습니다.
  • 이는 본질적으로 신뢰할 수없는 조치이며 개발자가 방해 할 수 없으며 기꺼이 또는 의도하지 않게 프로세스를 망칠 수 없음을 의미합니다.
  • 주입으로부터의 보호는 실제로 준비된 명령문 의 부작용 일 뿐이며 실제 목적은 구문 적으로 올바른 명령문을 생성하는 것입니다. 그리고 구문 상 올바른 진술은 100 % 주입 증명입니다. 그러나 주입 가능성에도 불구하고 올바른 구문이 필요합니다.
  • 끝까지 사용하면 개발자의 경험에 관계없이 응용 프로그램을 보호합니다. 2 차 주입 이라는 것이 있습니다. 그리고 “보호하기 위해 모든 사용자가 제공 한 입력을 이스케이프 “라는 매우 강력한 착각입니다 . 함께 결합하면 개발자가 자유로이 결정하여 보호해야 할 사항과 보호해야 할 사항을 결정하는 경우 주입으로 이어집니다.

(더 생각해 보면, 현재 자리 표시 자 세트는 실제 요구 사항에 충분하지 않으며 배열과 같은 복잡한 데이터 구조와 SQL 키워드 또는 식별자까지 확장해야합니다. 쿼리도 동적으로 수행되지만 개발자는 이러한 경우에 대해 비무장 상태로 남겨져 문자열 연결로 대체해야하지만 이는 다른 질문의 문제입니다).

흥미롭게도이 질문의 논란은 Stack Overflow의 매우 논란이 많은 성격에 의해 유발되었습니다. 이 사이트의 아이디어는 검색을 통해 오는 사용자에게 적합한 범용 답변 데이터베이스를 갖는 목표를 달성하기 위해 직접 요청하는 사용자특정 질문 을 활용하는 것입니다 . 아이디어 자체 는 나쁘지 않지만 다음과 같은 상황에서는 실패합니다. 사용자가 매우 좁은 질문을 할 때 , 특히 동료와의 분쟁에서 논쟁을 벌일 때 (또는 코드를 리팩토링 할 가치가 있는지 결정하기 위해) 실패합니다 . 경험이 많은 참가자 대부분이 답변을 작성하려고하지만 임무 를 염두에두고 전체적으로 스택 오버플로에 대한 답변은 OP뿐 아니라 가능한 한 많은 독자에게 좋은 답변을 제공합니다.


답변

보안 또는 형식 안전 고려 사항에 대해서만 생각하지 마십시오.

매개 변수화 된 쿼리를 사용하는 이유는 데이터베이스 수준에서 성능을 향상시키기 위해서입니다. 데이터베이스 관점에서 매개 변수화 된 쿼리는 SQL 버퍼에있는 하나의 쿼리입니다 (모든 데이터베이스가 내부적으로 유사한 개념을 가지고 있다고 생각하지만 Oracle의 용어를 사용하기 위해). 따라서 데이터베이스는 준비되고 실행할 준비가 된 메모리에 일정량의 쿼리를 저장할 수 있습니다. 이러한 쿼리는 구문 분석 할 필요가 없으며 더 빠릅니다. 자주 실행되는 쿼리는 일반적으로 버퍼에 있으며 사용할 때마다 구문 분석이 필요하지 않습니다.

제외

누군가 매개 변수화 된 쿼리를 사용하지 않습니다. 이 경우 버퍼는 거의 동일한 쿼리의 스트림을 통해 지속적으로 플러시되며, 각 쿼리는 데이터베이스 엔진에 의해 구문 분석되고 실행되어야하며 자주 실행되는 쿼리조차 여러 번 다시 구문 분석되므로 성능이 전반적으로 저하됩니다. 일. 저는 생계를 위해 데이터베이스를 조정했으며 이것이 가장 큰 열매를 맺는 가장 큰 원천 중 하나였습니다.

지금

질문에 답하기 위해 쿼리에 고유 한 숫자 값이 적 으면 문제가 발생하지 않고 실제로 성능이 극히 향상 될 수 있습니다. 그러나 잠재적으로 수백 개의 값이 있고 쿼리가 많이 호출되면 시스템 성능에 영향을 미치므로 그렇게하지 마십시오.

예, SQL 버퍼를 늘릴 수 있지만 궁극적으로 인덱스 또는 데이터 캐싱과 같은 메모리에 대한 다른 더 중요한 사용을 희생해야합니다. 도덕적, 매우 종교적으로 매개 변수화 된 쿼리를 사용하여 데이터베이스를 최적화하고 중요한 작업에 더 많은 서버 메모리를 사용할 수 있습니다.