Microsoft.Data.SqlClient의 연결 문자열 빌더

SqlConnectionStringBuilder를 사용하여 타입이 있는 속성을 통해 연결 문자열을 생성하고 편집하세요. 이것은 DbConnectionStringBuilder에서 상속됩니다. 문자열을 연결하는 대신 코드가 값을 추가하거나 변경할 때마다 사용하세요.

건축업자:

  • 각 키워드는 와 같이 Encrypt로, SqlConnectionEncryptOption와 같이 ConnectTimeout로 형식이 지정된 속성으로 노출됩니다.
  • 설정할 때 알 수 없는 키워드와 범위를 벗어난 많은 값은 ArgumentException와 함께 거부됩니다.
  • uid 및 Server 같은 동의어를 Data Source 및 User ID 같은 정규 키워드에 매핑합니다.
  • 세미콜론, 따옴표 또는 앞뒤 공백이 있는 값은 따옴표로 묶습니다.

키워드 목록은 연결 옵션을 참조하세요. 텍스트 형식에 대해서는 파싱 규칙을 참조하세요.

각 샘플은 완전한 프로그램입니다. .NET 프레임워크 탭은 C# 7.3을 사용합니다. 샘플을 실행하는 프로젝트를 만들려면 ' 프로젝트 만들기'를 참조하세요.

연결 문자열 주입을 방지합니다

코드가 신뢰할 수 없는 텍스트를 연결 문자열에 삽입하면, 그 텍스트의 세미콜론이 키워드를 추가하거나 이전 키워드를 대체할 수 있습니다. 왜냐하면 마지막 값이 이기기 때문입니다. 빌더는 전체 값을 하나의 속성에 저장하고 따옴표로 묶기 때문에, 텍스트가 또 다른 키워드가 될 수 없습니다.

이 프로그램은 InitialCatalog를 풀링을 끄려고 하는 값으로 설정합니다:

using Microsoft.Data.SqlClient;

// Simulates a value that came from a user or another untrusted source.
string databaseFromUser = "Orders;Pooling=false";

var builder = new SqlConnectionStringBuilder
{
    DataSource = "tcp:sql.example.com,1433",
    InitialCatalog = databaseFromUser,
    IntegratedSecurity = true,
    Encrypt = SqlConnectionEncryptOption.Strict,
};

Console.WriteLine(builder.ConnectionString);
Console.WriteLine($"Pooling: {builder.Pooling}");

결과는 다음과 같습니다.

Data Source=tcp:sql.example.com,1433;Initial Catalog="Orders;Pooling=false";Integrated Security=True;Encrypt=Strict
Pooling: True

빌더는 값을 Pooling 따옴표로 묶고, True 그대로 유지됩니다. 드라이버는 Orders;Pooling=false를 데이터베이스 이름으로 서버에 전송합니다.

따옴표로 묶으면 키워드 주입이 중지됩니다. 어떤 값이 허용되는지 여부를 결정하는 것은 아닙니다. 사용 전에 신뢰할 수 없는 입력을 허용된 서버나 데이터베이스 목록과 비교해 확인하세요.

기본 연결 문자열을 완성하세요

서버나 데이터베이스처럼 변경되지 않는 연결 문자열 부분을 구성에 저장하세요. 실행 시 비밀 값과 워크로드별 값을 추가하세요. 기본 문자열을 빌더 생성자에 전달한 후 속성을 설정합니다.

이 프로그램은 별도의 환경 변수에서 기본 문자열과 비밀번호를 읽고, SQL 인증을 사용해 연결하고, 연결이 열린 후 비밀번호가 보이지 않는지 확인합니다.

using Microsoft.Data.SqlClient;

// The base string holds no secret. The password comes from a separate secret source.
string baseConnectionString =
    Environment.GetEnvironmentVariable("SQL_BASE_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_BASE_CONNECTION_STRING environment variable.");
string password =
    Environment.GetEnvironmentVariable("SQL_PASSWORD")
    ?? throw new InvalidOperationException(
        "Set the SQL_PASSWORD environment variable.");

var builder = new SqlConnectionStringBuilder(baseConnectionString)
{
    Password = password,
    ApplicationName = "Orders.Worker",
};

await using var connection = new SqlConnection(builder.ConnectionString);
await connection.OpenAsync();

await using var command = new SqlCommand("SELECT APP_NAME();", connection);
Console.WriteLine($"APP_NAME(): {await command.ExecuteScalarAsync()}");

var opened = new SqlConnectionStringBuilder(connection.ConnectionString);
Console.WriteLine($"Password visible after open: {opened.ContainsKey("Password") && opened.Password.Length > 0}");

Server=tcp:<server>,1433;Database=<database>;User ID=<user>;Encrypt=true를 SQL_BASE_CONNECTION_STRING와 같은 값으로 설정합니다. 결과는 다음과 같습니다.

APP_NAME(): Orders.Worker
Password visible after open: False

Persist Security Info의 기본값은 false이며, 드라이버가 연결이 열린 후 SqlConnection.ConnectionString에서 비밀번호를 제거합니다. 기본값을 유지하세요.

비밀번호가 필요 없는 인증 방법, 예를 들어 Windows 통합 인증이나 Microsoft Entra ID 관리형 ID를 선호하세요. 자세한 내용은 Microsoft Entra ID 인증 및 연결 정보 보호를 참조하세요.

값 읽기 및 변경

빌더는 사전 역할도 합니다. 인덱서, ContainsKey, Remove, TryGetValue 는 키워드 또는 그 동의어 중 하나를 받아들입니다.

using Microsoft.Data.SqlClient;

var b = new SqlConnectionStringBuilder("Server=sql.example.com;Database=Orders;uid=app_user;pwd=x");
b["server"] = "tcp:sql2.example.com,1433";
Console.WriteLine($"DataSource: {b.DataSource}");
Console.WriteLine($"ContainsKey(\"uid\"): {b.ContainsKey("uid")}");
Console.WriteLine($"Remove(\"pwd\"): {b.Remove("pwd")}");
Console.WriteLine($"TryGetValue: {b.TryGetValue("Database", out object? db)} {db}");
Console.WriteLine($"ShouldSerialize(\"Pooling\"): {b.ShouldSerialize("Pooling")}");
Console.WriteLine(b.ConnectionString);
try { b["Bogus"] = 1; } catch (Exception e) { Console.WriteLine($"{e.GetType().Name}: {e.Message}"); }
try { b.ConnectTimeout = -1; } catch (Exception e) { Console.WriteLine($"{e.GetType().Name}: {e.Message}"); }

결과는 다음과 같습니다.

DataSource: tcp:sql2.example.com,1433
ContainsKey("uid"): True
Remove("pwd"): True
TryGetValue: True Orders
ShouldSerialize("Pooling"): False
Data Source=tcp:sql2.example.com,1433;Initial Catalog=Orders;User ID=app_user
ArgumentException: Keyword not supported: 'Bogus'.
ArgumentException: Invalid value for key 'Connect Timeout'.

ShouldSerialize 문자열에 없는 키워드에 대해 false을(를) 반환합니다. ContainsKey는 문자열이 해당 키워드를 설정하든 아니든 지원되는 모든 키워드에 대해 true를 반환합니다. 속성에는 ConnectionString 설정한 키워드만 포함되어 있습니다.

빌더가 검사하지 않는 부분을 이해하세요

빌더는 알 수 없는 키워드와 많은 부적절한 값은 거부하지만, 모든 값을 검사하지는 않습니다. 예를 들어, Transaction Binding 어떤 텍스트든 받아들이고 Type System Version, 문자열 길이는 검사하지 않습니다. 또한 키워드 조합을 검사하지 않으므로 Integrated Security=true와 Authentication=Active Directory Default를 함께 허용합니다. SqlConnection 문자열을 전달할 때 ArgumentException와의 그 조합을 거부합니다. 전체 규칙 목록은 검증 규칙을 참조하세요.

빌더는 Authentication와 같이 공백 없이 값을 기록합니다. 예: Authentication=ActiveDirectoryDefault 드라이버는 두 가지 형식을 모두 지원합니다.

빌더는 프로세스 메모리에 들어간 후의 비밀번호를 보호하지 않으며, 서버, 신원, 인증서 설정이 안전한지 여부를 판단하지 않습니다.

하나의 표준 연결 문자열만 유지하세요

각 독립된 연결 문자열은 고유한 연결 풀을 가집니다. 키워드 순서나 동의어만 다른 두 개의 손글씨 문자열이 두 개의 풀을 만듭니다. 빌더는 정규 키워드를 일관된 순서로 작성하므로, Server=a;Database=d와 Database=d;Server=a는 모두 Data Source=a;Initial Catalog=d가 됩니다. 시작 시 연결 문자열을 한 번 만들고 다시 사용builder.ConnectionString하세요. 사용자 이름이나 요청 ID 같은 요청당 값을 추가하지 마세요. 더 많은 풀링 가이드는 ' 연결 풀링 사용'을 참조하세요.