SQL 문에서 항상 매개 변수를 사용하는 것을 선호하는 이유는 무엇입니까?


114

저는 데이터베이스 작업에 매우 익숙합니다. 지금은 쓸 수 SELECT, UPDATE, DELETE, 및 INSERT명령. 그러나 나는 우리가 쓰는 것을 선호하는 많은 포럼을 보았습니다.

SELECT empSalary from employee where salary = @salary

...대신에:

SELECT empSalary from employee where salary = txtSalary.Text

왜 우리는 항상 매개 변수를 선호하며 어떻게 사용합니까?

첫 번째 방법의 용도와 이점을 알고 싶었습니다. SQL 주입에 대해 들어 봤지만 완전히 이해하지 못했습니다. SQL 주입이 내 질문과 관련이 있는지조차 모릅니다.


2
맞습니다. 이것은 SQL 주입과 관련이 있습니다. 매개 변수를 다루는 방법은 일반적으로 프로그램이 실행중인 언어 / 프레임 워크의 책임이며 언어에 따라 달라질 수 있습니다. RDBMS (도움)와 ORM 프레임 워크 (필수)를 모두 게시하십시오.
Clockwork-Muse

1
프로그래밍 언어로 C #을 사용하고 데이터베이스로 Sql Server 2008을 사용하고 있습니다. Microsoft dotNet Framework 4.0을 사용하고 있습니다. 정말 죄송합니다. 무엇을 요청하고 있는지 (RDBMS 또는 ORM) 확실하지 않습니다. 아마도 지금 제 RDBMS 및 ORM 프레임 워크 버전을 제공 할 수 있습니다. :-). 감사합니다
Sandy

1
RDBMS는 데이터베이스 (귀하의 경우 SQL Server 2008)입니다. ORM은 데이터베이스 (이 경우 ADO.NET)에 액세스하는 방법입니다. 기타에는 LINQ to SQLEntity Framework가 포함 됩니다. 실제로 ADO.NET 및 SQL의 기본 사항을 배우면 LINQ 또는 EF와 같은 ORM이 SQL을 수동으로 작성하여 발생하는 많은 문제를 처리하므로 ORM을 사용하는 것이 좋습니다.
Chad Levy

답변:


129

매개 변수를 사용 하면 데이터베이스가 데스크톱 프로그램 또는 웹 사이트와 같은 프로그램 인터페이스와 함께 사용될 때 SQL 주입 공격 을 방지하는 데 도움 이됩니다.

귀하의 예에서 사용자는 .NET에서 문을 작성하여 데이터베이스에서 SQL 코드를 직접 실행할 수 있습니다 txtSalary.

예를 들어, 작성하는 경우 0 OR 1=1실행 된 SQL은 다음과 같습니다.

 SELECT empSalary from employee where salary = 0 or 1=1

모든 empSalaries가 반환됩니다.

또한 사용자는 데이터베이스에 대해 훨씬 더 나쁜 명령을 수행 할 수 있습니다 0; Drop Table employee.

SELECT empSalary from employee where salary = 0; Drop Table employee

그러면 테이블 employee이 삭제됩니다.


귀하의 경우에는 .NET을 사용하고있는 것 같습니다. 매개 변수 사용은 다음과 같이 쉽습니다.

씨#

string sql = "SELECT empSalary from employee where salary = @salary";

using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
    var salaryParam = new SqlParameter("salary", SqlDbType.Money);
    salaryParam.Value = txtMoney.Text;

    command.Parameters.Add(salaryParam);
    var results = command.ExecuteReader();
}

VB.NET

Dim sql As String = "SELECT empSalary from employee where salary = @salary"
Using connection As New SqlConnection("connectionString")
    Using command As New SqlCommand(sql, connection)
        Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
        salaryParam.Value = txtMoney.Text

        command.Parameters.Add(salaryParam)

        Dim results = command.ExecuteReader()
    End Using
End Using

2016-4-25 수정 :

George Stocker의 의견에 따라 샘플 코드를 AddWithValue. 또한 일반적으로 IDisposables를 using문으로 래핑하는 것이 좋습니다 .


훌륭한 솔루션. 그러나 매개 변수를 사용하는 것이 안전한 이유와 방법에 대해 좀 더 설명해 주시겠습니까? 내 말은 여전히 ​​sql 명령이 동일한 것처럼 보인다는 의미입니다
Sandy

SQL 명령에 여러 매개 변수를 추가 할 수 있습니까? INSERT 명령에 필요할 수있는 것처럼?
Sandy

2
SQL Server는 매개 변수 내부의 텍스트를 입력으로 만 처리하고 실행하지 않습니다.
Chad Levy

3
예, 여러 매개 변수를 추가 할 수 있습니다 Insert Into table (Col1, Col2) Values (@Col1, @Col2).. 코드에서 여러 AddWithValues를 추가합니다 .
Chad Levy

1
AddWithValue를 사용하지 마십시오! 암시 적 변환 문제가 발생할 수 있습니다. 항상 명시 적으로 크기를 설정하고 매개 변수 값을 parameter.Value = someValue.
조지 스토커

75

맞습니다. 이것은 SQL 주입 과 관련 이 있습니다. 이는 malicioius 사용자가 데이터베이스에 대해 임의의 명령문을 실행할 수 있도록하는 취약점입니다. 이 옛날 좋아하는 XKCD 만화 는 개념을 보여줍니다.

그녀의 딸은 운전 면허증 공장에 갇혀 도와 줘요.


귀하의 예에서 다음을 사용하는 경우 :

var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query

SQL 주입에 열려 있습니다. 예를 들어 누군가 txtSalary를 입력한다고 가정 해 보겠습니다.

1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.

이 쿼리를 실행하면 a SELECT및 an UPDATE또는 DROP, 또는 원하는대로 수행됩니다 . --말은 단순히 당신이 후에 어떤을 연결 한 경우 공격에 유용 할 것이다 쿼리의 나머지 부분을 주석으로 txtSalary.Text.


올바른 방법은 매개 변수화 된 쿼리를 사용하는 것입니다 (예 : (C #)).

SqlCommand query =  new SqlCommand("SELECT empSalary FROM employee 
                                    WHERE salary = @sal;");
query.Parameters.AddWithValue("@sal", txtSalary.Text);

이를 통해 쿼리를 안전하게 실행할 수 있습니다.

다른 여러 언어로 SQL 삽입을 방지하는 방법에 대한 참조 는 SO 사용자가 관리하는 웹 사이트 bobby-tables.com을 확인하십시오 .


1
훌륭한 솔루션. 그러나 매개 변수를 사용하는 것이 안전한 이유와 방법에 대해 좀 더 설명해 주시겠습니까? 나는 여전히 sql 명령이 동일하게 보일 것임을 의미합니다.
Sandy

1
@ user815600 : 일반적인 오해-여전히 매개 변수가있는 쿼리가 값을 가져와 실제 값을 매개 변수로 대체 할 것이라고 믿습니다. 맞습니까? 아니 이런 일이 일어나지 않습니다! -대신 매개 변수가있는 SQL 문이 매개 변수 및 해당 값 목록과 함께 SQL Server로 전송됩니다.-SQL 문은 동일 하지 않습니다
marc_s

1
이는 SQL 주입이 SQL Server 내부 메커니즘 또는 보안에 의해 모니터링되고 있음을 의미합니다. 감사.
Sandy

4
내가 만화를 좋아하는 것처럼 테이블을 삭제할 수있는 충분한 권한으로 코드를 실행한다면 더 큰 문제가있을 것입니다.
philw 2013-08-03

9

다른 답변 외에도 SQL 주입을 방지 뿐만 아니라 쿼리 성능을 향상시킬 수 있는 매개 변수를 추가해야 합니다 . SQL Server는 매개 변수가있는 쿼리 계획을 캐싱하고 반복되는 쿼리 실행에 재사용합니다. 쿼리를 매개 변수화하지 않은 경우 SQL Server는 쿼리 텍스트가 다를 경우 각 쿼리 (일부 제외 포함) 실행 에 대해 새 계획을 컴파일 합니다.

쿼리 계획 캐싱에 대한 추가 정보


1
이것은 생각보다 더 적절합니다. "작은"쿼리도 수천 또는 수백만 번 실행되어 전체 쿼리 캐시를 효과적으로 플러시 할 수 있습니다.
James

5

처음 방문한 지 2 년이 지난 후 , 저는 복습 중입니다 ...

매개 변수를 선호하는 이유는 무엇입니까? SQL 인젝션은 분명히 큰 이유이지만 SQL 을 언어로 되돌리고 자 은밀히 갈망 하고 있기 때문일 수 있습니다 . 문자열 리터럴의 SQL은 이미 이상한 문화적 관행이지만 최소한 요청을 복사하여 관리 스튜디오에 붙여 넣을 수 있습니다. SQL에 조건부 및 제어 구조가있을 때 호스트 언어 조건부 및 제어 구조로 동적으로 구성된 SQL은 수준 0 야만적입니다. 앱이 생성하는 SQL을 확인하려면 디버그 또는 추적으로 앱을 실행해야합니다.

매개 변수만으로 멈추지 마십시오. 끝까지 가서 QueryFirst (면책 조항 : 내가 쓴)를 사용하십시오. SQL 은 .sql 파일에 있습니다.. 테이블과 열에 대한 구문 유효성 검사 및 Intellisense를 사용하여 멋진 TSQL 편집기 창에서 편집합니다. 특별 코멘트 섹션에서 테스트 데이터를 할당하고 "재생"을 클릭하여 창에서 바로 쿼리를 실행할 수 있습니다. 매개 변수 작성은 SQL에 "@myParam"을 입력하는 것만 큼 쉽습니다. 그런 다음 저장할 때마다 QueryFirst는 쿼리에 대한 C # 래퍼를 생성합니다. 강력한 형식의 매개 변수가 Execute () 메서드에 대한 인수로 나타납니다. 결과는 IEnumerable 또는 강력한 형식의 POCO 목록으로 반환되며 쿼리에서 반환 된 실제 스키마에서 생성 된 형식입니다. 쿼리가 실행되지 않으면 앱이 컴파일되지 않습니다. db 스키마가 변경되고 쿼리가 실행되지만 일부 열이 사라지면 컴파일 오류가 코드의 줄을 가리 킵니다.누락 된 데이터에 액세스하려고합니다. 그리고 다른 많은 장점이 있습니다. 다른 방법으로 데이터에 액세스하려는 이유는 무엇입니까?


4

Sql에서 @ 기호가 포함 된 단어는 변수임을 의미하며이 변수를 사용하여 값을 설정하고 동일한 SQL 스크립트의 숫자 영역에서 사용합니다. 이는 많은 변수를 선언 할 수 있지만 단일 스크립트에서만 제한되기 때문입니다. 많은 스크립트에서 동일한 유형 및 이름의. 저장 프로시 저는 미리 컴파일 된 쿼리이기 때문에 저장 프로 시저 로트에서이 변수를 사용하고 추가 정보를 위해 스크립트, 데스크톱 및 웹 사이트에서 이러한 변수의 값을 전달할 수 있습니다. 로컬 변수 선언 , SQL 저장 프로 시저SQL 주입을 읽어보십시오 .

또한 SQL 주입으로부터 보호를 읽으면 데이터베이스를 보호 할 수있는 방법을 안내합니다.

질문을 이해하는 데 도움이되기를 바랍니다.


3

다른 답변은 매개 변수가 중요한 이유를 다루지 만 단점이 있습니다! .net에는 매개 변수를 생성하는 몇 가지 방법 (Add, AddWithValue)이 있지만, 모두 매개 변수 이름에 대해 불필요하게 걱정할 필요가 있으며 모두 코드에서 SQL의 가독성을 떨어 뜨립니다. SQL을 묵상하려고 할 때 매개 변수에 어떤 값이 사용되었는지 확인하기 위해 위 또는 아래를 찾아야합니다.

나는 나의 작은 SqlBuilder 클래스가 매개 변수화 된 쿼리를 작성하는 가장 우아한 방법 이라고 겸손하게 주장 한다 . 코드는 다음과 같습니다.

씨#

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();

코드는 더 짧고 훨씬 더 읽기 쉽습니다. 추가 라인도 필요하지 않으며, 다시 읽을 때 매개 변수 값을 찾을 필요가 없습니다. 필요한 수업이 여기 있습니다 ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
    _rq = new StringBuilder();
    _cmd = cmd;
    _seq = 0;
}
public SqlBuilder Append(String str)
{
    _rq.Append(str);
    return this;
}
public SqlBuilder Value(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append(paramName);
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public SqlBuilder FuzzyValue(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append("'%' + " + paramName + " + '%'");
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public override string ToString()
{
    return _rq.ToString();
}
}

매개 변수 이름을 지정하면 서버가 실행중인 쿼리를 프로파일 링 할 때 확실히 도움이됩니다.
Dave R.

내 상사가 같은 말을했다. 의미있는 매개 변수 이름이 중요한 경우 값 메소드에 paramName 인수를 추가하십시오. 나는 당신이 불필요하게 복잡한 일을하고 있다고 생각합니다.
bbsimonbb

나쁜 생각. 이전에 말했듯 AddWithValue이 암시 적 변환 문제를 일으킬 수 있습니다.
Adam Calvet Bohl

@Adam 당신 말이 맞지만 그것이 AddWithValue ()가 매우 널리 사용되는 것을 막지는 못하며, 그것이 아이디어를 무효화한다고 생각하지 않습니다. 그러나 그 사이에, 나는 마련했습니다 훨씬 더 좋은 방법은 매개 변수화 된 쿼리를 작성하고, 그 :-)) AddWithValue를 (사용하지 않는
bbsimonbb

권리! 곧 볼 거라고 약속 해요!
Adam Calvet Bohl

3

이전 게시물이지만 새 사용자가 저장 프로 시저를 알고 있는지 확인하고 싶었습니다 .

여기서 내 10 센트 가치는 SQL 문을 저장 프로 시저 로 작성할 수 있다면 이것이 최적의 접근 방식이라는 것입니다. 나는 항상 저장된 procs를 사용하고 주 코드에서 레코드를 반복하지 않습니다. 예 : SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

저장 프로 시저를 사용하면 사용자를 EXECUTE 권한으로 만 제한하여 보안 위험줄일 수 있습니다 .

저장 프로시 저는 기본적으로 매개 변수화되며 입력 및 출력 매개 변수를 지정할 수 있습니다.

저장 프로 시저 ( SELECT문을 통해 데이터를 반환하는 경우 )는 SELECT코드 의 일반 문과 동일한 방식으로 액세스하고 읽을 수 있습니다 .

또한 SQL Server에서 컴파일되기 때문에 더 빠르게 실행됩니다.

또한 update테이블 과 같은 여러 단계를 수행 하고 다른 DB 서버에서 값을 확인한 다음 마지막으로 완료되면 데이터를 모두 동일한 서버에있는 클라이언트로 반환하고 클라이언트와의 상호 작용이 없다고 언급 했습니까? 따라서 이것은 코드에서이 로직을 코딩하는 것보다 훨씬 빠릅니다.

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