[c#] 매개 변수없이 SQL 주입 방지

여기서는 코드에서 매개 변수화 된 SQL 쿼리를 사용하는 방법에 대해 또 다른 논의를하고 있습니다. 토론에는 두 가지 측면이 있습니다. 나와 다른 사람들은 항상 매개 변수를 사용하여 SQL 주입으로부터 보호해야한다고 말하는 사람과 필요하다고 생각하지 않는 다른 사람을 말합니다. 대신 SQL 삽입을 피하기 위해 모든 문자열에서 단일 아포스트로피를 두 개의 아포스트로피로 바꾸려고합니다. 우리의 데이터베이스는 모두 Sql Server 2005 또는 2008을 실행하고 있으며 코드베이스는 .NET Framework 2.0에서 실행됩니다.

C #으로 간단한 예를 들어 보겠습니다.

나는 이것을 사용하기를 원한다.

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe

다른 사람들은 이것을하기를 원하지만 :

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?

SafeDBString 함수는 다음과 같이 정의됩니다.

string SafeDBString(string inputValue)
{
    return "'" + inputValue.Replace("'", "''") + "'";
}

이제 쿼리의 모든 문자열 값에 SafeDBString을 사용하는 한 안전해야합니다. 권리?

SafeDBString 함수를 사용하는 데는 두 가지 이유가 있습니다. 첫 번째는 석기 시대부터 수행 된 방식이고 두 번째는 데이터베이스에서 실행되는 정확한 쿼리를 볼 수 있기 때문에 SQL 문을 디버그하는 것이 더 쉽습니다.

그럼. 내 질문은 SQL 주입 공격을 피하기 위해 SafeDBString 함수를 사용하는 것이 실제로 충분한 지 여부입니다. 이 안전 조치를 위반하는 코드의 예를 찾으려고 노력했지만 그 예를 찾을 수 없습니다.

이걸 깰 수있는 사람이 있나요? 어떻게 하시겠습니까?

편집 :
지금까지 회신을 요약하려면 :

  • 아직 Sql Server 2005 또는 2008에서 SafeDBString을 다루는 방법을 찾지 못했습니다. 좋은 것 같아요?
  • 여러 응답에서 매개 변수화 된 쿼리를 사용할 때 성능 향상을 얻을 수 있다고 지적했습니다. 그 이유는 쿼리 계획을 재사용 할 수 있기 때문입니다.
  • 또한 매개 변수화 된 쿼리를 사용하면 유지 관리하기 쉬운 더 읽기 쉬운 코드가 제공된다는 데 동의합니다.
  • 또한 다양한 버전의 SafeDBString, 문자열에서 숫자로의 변환 및 문자열에서 날짜로의 변환을 사용하는 것보다 항상 매개 변수를 사용하는 것이 더 쉽습니다.
  • 매개 변수를 사용하면 날짜 나 십진수로 작업 할 때 특히 유용한 자동 유형 변환을 얻을 수 있습니다.
  • 그리고 마지막 으로 JulianR이 쓴 것처럼 스스로 보안을 시도하지 마십시오 . 데이터베이스 공급 업체는 보안에 많은 시간과 비용을 소비합니다. 우리가 더 잘할 수있는 방법은 없으며 그들의 일을하려고 노력해야 할 이유도 없습니다.

그래서 아무도 SafeDBString 함수의 간단한 보안을 깰 수 없었지만 다른 많은 좋은 주장을했습니다. 감사!



답변

정답은 다음과 같습니다.

스스로 보안을 시도하지 마십시오 . 신뢰할 수있는 어떤 사용은 업계 표준 라이브러리는 당신이해야 할 노력보다는하는지에 사용할 수 있습니다 노력하고 그것을 스스로 할 수 있습니다. 보안에 대해 어떤 가정을하든 틀릴 수 있습니다. 자신의 접근 방식이 안전 해 보이지만 (기껏해야 흔들리는 것처럼 보임) 무언가를 간과하고있는 위험이 있으며 보안과 관련하여 그 기회를 정말로 취하고 싶습니까?

매개 변수를 사용하십시오.


답변

그리고 누군가가 가서 ‘대신 “를 사용합니다. 매개 변수는 IMO, 유일한 안전한 방법입니다.

또한 날짜 / 숫자에 대한 많은 i18n 문제를 방지합니다. 01/02/03은 언제입니까? 123,456은 얼마입니까? 서버 (app-server 및 db-server)가 서로 동의합니까?

위험 요소가 설득력이 없다면 성능은 어떻습니까? RDBMS는 매개 변수를 사용하면 쿼리 계획을 재사용하여 성능을 향상시킬 수 있습니다. 문자열만으로는 이것을 할 수 없습니다.


답변

논쟁은 승리하지 않습니다. 취약점을 찾아 내면 동료는 SafeDBString 함수를 변경하여이를 설명한 다음 다시 안전하지 않다는 것을 증명하도록 요청할 것입니다.

매개 변수화 된 쿼리가 확실한 프로그래밍 모범 사례라는 점을 감안할 때 더 안전하고 성능이 좋은 방법을 사용하지 않는 이유를 설명하는 데에는 증명의 부담이 있어야합니다.

문제가 모든 레거시 코드를 다시 작성하는 것이라면 모든 새 코드에서 매개 변수화 된 쿼리를 사용하고 해당 코드에서 작업 할 때 이전 코드를 사용하도록 리팩토링하는 것이 쉬운 방법입니다.

제 생각에는 실제 문제는 자부심과 고집입니다. 그리고 그것에 대해 당신이 할 수있는 일이별로 없습니다.


답변

우선, “교체”버전의 샘플이 잘못되었습니다. 텍스트 주위에 아포스트로피를 넣어야합니다.

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

이것이 매개 변수가하는 또 다른 일입니다. 값을 따옴표로 묶어야하는지 여부에 대해 걱정할 필요가 없습니다. 물론이를 함수에 빌드 할 수는 있지만 함수에 많은 복잡성을 추가해야합니다. ‘NULL’을 null로, ‘NULL’을 문자열로 또는 숫자와 숫자 사이의 차이를 아는 방법 많은 숫자를 포함하는 문자열입니다. 버그의 또 다른 소스 일뿐입니다.

또 다른 한 가지는 성능입니다. 매개 변수화 된 쿼리 계획은 종종 연결된 계획보다 더 잘 캐시되므로 쿼리를 실행할 때 서버를 한 단계 절약 할 수 있습니다.

또한 작은 따옴표를 이스케이프하는 것만으로는 충분하지 않습니다. 많은 DB 제품은 공격자가 이용할 수있는 문자 이스케이프를위한 대체 방법을 허용합니다. 예를 들어 MySQL에서는 백 슬래시로 작은 따옴표를 이스케이프 할 수도 있습니다. 그래서 다음 “name”값은 SafeDBString()함수 만으로 MySQL을 날려 버릴 것입니다 . 작은 따옴표를 두 배로하면 첫 번째는 여전히 백 슬래시로 이스케이프되어 두 번째는 “활성”상태가되기 때문입니다.

x \ ‘OR 1 = 1;-


또한 JulianR은 아래에 좋은 점을 제시합니다. 절대로 보안 작업을 직접 시도하지 마십시오 . 그것은 미묘한 방법으로 보안 프로그래밍 잘못 너무 쉽게 표시를철저한 테스트를 거쳐도 작동 . 그런 다음 시간이 지나고 1 년이 지난 후 6 개월 전에 시스템에 금이 갔음을 알게되었고 그때까지는 알지 못했습니다.

항상 플랫폼에 제공되는 보안 라이브러리에 최대한 의존하십시오. 그들은 생계를 위해 보안 코드를 수행하는 사람들이 작성하고, 관리 할 수있는 것보다 훨씬 더 잘 테스트되며, 취약점이 발견되면 공급 업체에서 서비스를받습니다.


답변

그래서 다음과 같이 말합니다.

1) 내장 된 항목을 다시 구현하려는 이유는 무엇입니까? 바로 사용할 수 있고 사용하기 쉬우 며 이미 글로벌 규모로 디버깅되었습니다. 향후 버그가 발견되면 아무 작업도하지 않아도 매우 빠르게 수정되어 모든 사람이 사용할 수 있습니다.

2)에 자리에없는 어떤 프로세스를 보장 당신이 결코 SafeDBString에 전화를 그리워? 단 한 곳에서 놓치면 전체 문제가 발생할 수 있습니다. 이러한 것들을 얼마나 눈에 띄게 할 것이며, 받아 들여진 정답을 쉽게 찾을 수있을 때 그 노력이 얼마나 낭비되는지 고려하십시오.

3) Microsoft (DB 및 액세스 라이브러리 작성자)가 SafeDBString 구현에서 알고있는 모든 공격 벡터를 얼마나 확실하게 처리 했습니까?

4) SQL의 구조를 읽는 것이 얼마나 쉬운가? 이 예제에서는 + 연결을 사용하고 매개 변수는 더 읽기 쉬운 string.Format과 매우 유사합니다.

또한 실제로 실행 된 것을 알아내는 방법에는 두 가지가 있습니다. 자체 LogCommand 함수, 보안 문제없는 간단한 함수 , 또는 데이터베이스가 실제로 진행되고 있다고 생각하는 작업을 파악하기 위해 SQL 추적을 살펴보세요.

LogCommand 함수는 간단합니다.

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

옳든 그르 든, 보안 문제없이 필요한 정보를 제공합니다.


답변

매개 변수화 된 쿼리를 사용하면 SQL 주입에 대한 보호 이상의 기능을 얻을 수 있습니다. 또한 더 나은 실행 계획 캐싱 가능성을 얻을 수 있습니다. SQL Server 쿼리 프로파일 러를 사용하는 경우 ‘데이터베이스에서 실행되는 정확한 SQL’을 계속 볼 수 있으므로 SQL 문을 디버깅하는 측면에서도 실제로 손실되는 일이 없습니다.


답변

저는 SQL 주입 공격을 피하기 위해 두 가지 접근 방식을 모두 사용했으며 확실히 매개 변수화 된 쿼리를 선호합니다. 연결된 쿼리를 사용했을 때 라이브러리 함수를 사용하여 변수 (mysql_real_escape_string과 같은)를 이스케이프 처리했으며 독점 ​​구현에서 모든 것을 다루었 음을 확신하지 못할 것입니다 (당신도 그렇듯이).