|
오라클 기반 .NET 애플리케이션 개발 마스터하기
오라클 데이터베이스 기반 .NET 애플리케이션의 보안
저자 - John Paul Cook
.NET 애플리케이션에서 오라클의 빌트인 보안 기능을 활용하는 방법을 배워 보십시오.
"오라클 기반 .NET 애플리케이션 개발 마스터하기" 홈페이지
보안에 대한 관심이 계속적으로 증가하면서, 기업들은 기본적인 액세스 컨트롤 이상의 성숙된 보안 체계를 기대하게 되었습니다. 또 새로이 제정되는 국제법, 국내법, 지방 법들을 통해 한층 세분화된 보안 접근법이 요구되고 있습니다. 불법적인 접근으로부터 데이터베이스를 보호하는 것만으로는 더 이상 충분하지 않습니다. 어떤 사용자가 어떤 작업을 데이터베이스에서 수행했는지 확인하고 감사 로그를 관리하는 것 또한 필수적입니다.
그나마 다행스러운 것은, 오라클을 데이터베이스 플랫폼으로 사용하는 .NET 개발자들의 경우 Oracle Data Provider for .NET(ODP.NET)이 제공하는 오라클 보안 기능을 포괄적인 보안 전략을 위한 기반으로 활용할 수 있다는 사실입니다.
이번 연재에서는, 아래와 같은 보안 목표를 구현하기 위한 애플리케이션 설계 방법에 대해 설명하기로 합니다.
- 프록시 인증을 사용하여 실제 엔드 유저를 오라클 서버에 전달하는 동시에 커넥션 풀링의 이점을 활용하는 방법. 프록시 인증을 이용하면 커넥션 풀링을 이용하면서도 사용자 단위의 데이터베이스 인증을 수행하는 것이 가능합니다.
- 클라이언트 ID를 이용하여 커스텀 ID 문자열 또는 실제 엔드 유저 ID를 오라클 서버에 전달하는 방법
- 오라클 사용자 ID와 패스워드 대신 오라클 환경에서 Windows 인증을 이용한 싱글사인온을 통해 인증을 구현하는 방법
- 매개변수화된 쿼리를 통해 (오늘날 데이터베이스에 대한 최대 보안 위협의 하나로 간주되는) SQL 인젝션 공격을 차단하는 방법.
마지막으로 난이도가 각각 다른 다섯 가지 실습 랩에서 배운 내용을 실제로 적용해 볼 수 있는 기회가 제공됩니다.
본 문서는 독자 여러분이 Oracle Data Provider for .NET의 개념과 Visual Studio.NET에서 프로젝트를 생성하는 방법에 익숙하다고 가정합니다. 이에 필요한 정보는 필자가 기고한 오라클 데이터베이스 기반 .NET 애플리케이션의 구축 (한글)문서에서 참고하실 수 있습니다.
프록시 인증
필자가 앞서 기고한 아티클에서 간단한 오라클 연결 문자열을 정의하기 위해 사용한 코드가 아래와 같습니다:
Dim oradb As String = "Data Source=OraDb;User Id=scott;Password=tiger;" ' VB.NET
string oradb = "Data Source=OraDb;User Id=scott;Password=tiger;"; // C#
위와 같은 연결 문자열은 애플리케이션에서 사용자에게 사용자 ID와 패스워드를 요구할 때 사용됩니다. 웹 애플리케이션에서 특히 이러한 방법이 주로 사용됩니다. 이 연결 방식은 커넥션 풀링을 통한 성능 개선을 가능하게 합니다. 모든 애플리케이션 사용자들은 동일한 오라클 인증 정보를 사용하여 연결하며, 이는 커넥션 풀링의 기본 요구 사항이기도 합니다. 감사 관점에서 볼 때, 공통 사용자 ID를 통해 연결하는 방식은 그리 바람직하지 못합니다. 모든 데이터베이스 작업이 실제 사용자가 아닌, 연결 문자열에 정의된 사용자에 의해 수행된 것처럼 기록되기 때문입니다. 커넥션 풀링의 성능적인 이점을 얻는 대신 익명성이라는 보안 상의 문제를 감수해야 하는 셈입니다. 데이터베이스 내에서는 실제 사용자의의 신분을 확인해 내는 것이 불가능합니다.
프록시 인증 방식에서는 연결 문자열에 Proxy User Id, Proxy Password 정보가 추가됩니다. 다시 말해, 실제 사용자와 프록시 사용자, 이렇게 2가지 사용자 ID가 동시에 전달됩니다. 이 기능을 이용하면 하나의 미드티어 커넥션 풀을 유지하면서 동시에 User Id 정보를 이용하여 사용자의 작업 내역을 감사할 수 있습니다. 프록시 인증의 사용 여부에 따라 User Id와 Password의 활용 방법에 미묘한 차이가 있다는 점을 알아둘 필요가 있습니다. 프록시 인증이 사용되는 경우, 프록시 사용자 인증 정보는 Proxy User Id, Proxy Password를 통해 전달됩니다. 다시 말해, User Id와 Password가 사용되지 않습니다. 그 예가 다음과 같습니다:
Dim oradb As String = "Data Source=OraDb;User Id=ActualUser;Password=secret; Proxy User Id=scott;Proxy Password=tiger;" ' VB.NET
string oradb = "Data Source=OraDb;User Id=ActualUser;Password=secret; Proxy User Id=scott;Proxy Password=tiger; "; // C#
실제 애플리케이션에서는 실제 사용자 ID와 패스워드를 하드 코딩 방식으로 입력하지 않습니다. 그 대신, 사용자가 직접 텍스트 상자에 사용자 ID와 패스워드를 입력하고, 이 정보가 연결 문자열로 전달됩니다. 사용자가 Windows 폼 또는 웹 페이지의 대화 상자에서 사용자 ID와 패스워드를 입력하는 방식을 마이크로소프트는 폼 인증(forms authentication)이라 부릅니다.
데이터베이스에서 프록시 인증은 라이트웨이트 세션(lightweight session)이라 불리는 2번째 세션을 생성합니다. 이 세션은 실제 사용자의 정보를 데이터베이스 전달하기 위한 용도로 활용됩니다. 이러한 방식을 사용하기 위해서는, 데이터베이스 관리자가 프록시 사용자의 라이트웨이트 연결 생성 권한을 명시적으로 허용해야 합니다. SCOOT를 프록시 사용자로 지정하는 방법이 아래와 같습니다.
alter user ActualUser grant connect through scott;
연결 문자열에서 실제 사용자 패스워드를 포함시킬 수도, 또는 제외시킬 수도 있습니다. 패스워드를 입력하지 않은 상태에서 실제 사용자 정보를 사용하면 연결은 성공합니다. 실제 사용자를 승인하려면 Password 연결 문자열 속성을 사용해야 합니다. 사용자에 대해 잘못된 패스워드가 제공되면 인증은 실패합니다.
프록시 연결은 사용자에 의해 호출되는 데이터베이스 작업을 위해 사용됩니다. 프록시 연결이 풀(pool)에 반환되면, 라이트웨이트 세션은 소멸됩니다.
Oracle Database 10g Release 2에는 client identifier라는 새로운 기능이 추가되었습니다. Client identifier는 Password 연결 문자열 속성을 사용하지 않고 프록시 인증을 거치는 것과 유사합니다. 한 가지 중요한 차이점은 client identifier가 데이터베이스에 두 번째 세션을 생성하지 않는다는 사실입니다. 또 다른 차이점은, client identifier로 어떤 문자열이든 사용될 수 있다는 점입니다. 다시 말해, 데이터베이스 사용자의 이름과 일치할 필요가 없습니다. Client identifier를 설정하려면 연결이 오픈된 뒤 커넥션 오브젝트의 ClientId 속성에 문자열 값을 할당해 주어야 합니다.
conn.ClientId = "SomeUser" ' VB.NET
conn.ClientId = "SomeUser"; // C#
위 구문은 사용자 세션의 CLIENT_IDENTIFIER 값을 할당합니다. 애플리케이션이 폼 인증을 사용하는 경우, 사용자가 입력한 사용자 ID는 ClientId의 값을 설정하는데 사용됩니다. ClientId는 USERENV의 일부이므로, 사용자가 ALL_USERS에 정의된 실제 사용자가 아니라 하더라도 Oracle Virtual Private Database에서 연결을 제한하는데 사용될 수 있습니다. ClientId는 확장성을 개선하기 위한 용도로도 활용됩니다. 한 예로, 사용자가 작업을 완료하고 ClientId을 다음 사용자를 위해 리셋한 상태에서도 웹 애플리케이션에서 데이터베이스 연결을 오픈된 상태로 유지할 수 있습니다.
사용자가 Windows에 로그인하면, 운영 체제에 의해 인지된 Windows 사용자 정보는 .NET 애플리케이션에서 Windows.Security.Principal 클래스를 통해 확인할 수 있습니다. 이 작업을 수행하고 Windows에 ClientId를 설정하기 위한 코드가 아래와 같습니다:
Dim user As New WindowsPrincipal(WindowsIdentity.GetCurrent())
conn.ClientId = user.Identity.Name ' VB.NET
WindowsPrincipal user = new WindowsPrincipal(WindowsIdentity.GetCurrent());
conn.ClientId = user.Identity.Name; // C#
이러한 테크닉을 이용하여, Windows 사용자 정보를 오라클 데이터베이스에서 확인할 수 있습니다. Password 연결 문자열이 없는 상태에서 프록시 인증을 사용한 경우 Windows 사용자 정보가 User Id를 통해 데이터베이스에 전달될 수도 있습니다. 이 경우, 위에서 설명한 것처럼 User Id 연결 문자열은 실제 사용자를 인증하는 것이 아니라 단지 확인만 하는 목적으로 사용됩니다.
Windows 인증
위에서 오라클 사용자 ID를 이용하여 사용자의 데이터베이스 인증을 수행하는 방법을 설명했습니다. 또 Windows 운영 체제의 인증 정보를 이용하여 데이터베이스 인증을 수행하는 싱글사인온 구성 또한 가능합니다. 필자가 Windows IT Pro 매거진에서 기고한 기사 Implementing Windows Authentication from Oracle에서 상세 정보를 확인하시기 바랍니다. Windows 인증을 활용하기 위해서는 연결 문자열을 수정해 주어야 합니다:
"Data Source=ORCL10g;User Id=/;"
슬래시(/) 기호는 Windows 인증이 사용됨을 오라클 데이터베이스에 알려 주는 역할을 합니다. Password 연결 문자열 속성은 오라클 데이터베이스 연결을 수행하는 경우에만 사용되기 때문에 위에서 사용되지 않았습니다. Windows 인증 과정에서 Password 속성이 연결 문자열에 포함되어 있더라도 무시됩니다.
Windows 인증 환경에서, Windows 사용자는 ORA_DBA와 같은 Windows 관리자 그룹에 포함되어 있거나, 외부 인증이 활성화된 상태이어야 합니다. 외부 인증 방식은 그룹 멤버십을 이용한 방법보다 보안 상 위험하기 때문에 권장되지 않습니다.
SQL 인젝션 공격의 이해
지금까지 데이터베이스에 접근하는 사용자를 추적하는 방법에 대해 설명했습니다. 이러한 방법도 중요하지만, 외부 침입자가 데이터베이스의 컨텐트에 위해를 가할 수 없도록 차단하는 것이 한층 중요하다고 할 수 있습니다. 데이터베이스 애플리케이션에 대한 중대 위협 중 하나로 SQL 인젝션 공격이 있습니다. SQL 인젝션은 악의를 가진 사용자가 코드 상의 취약점을 이용해 애플리케이션에 SQL 커맨드를 심는 테크닉입니다. SQL 인젝션 취약점은 사용자가 입력한 내용을 기준으로 SQL 구문이 구성되는 코드 환경에서 쉽게 발견됩니다. 따라서 프로그래밍을 위한 보안 기준을 준수하지 않는 경우 어떤 데이터베이스 제품이라도 피해를 입을 수 있습니다.
특정 직업 카테고리의 직원 수를 계산하는 간단한 애플리케이션의 예를 들어 보겠습니다:
데이터베이스 커맨드 문자열을 구성하는 코드가 아래와 같습니다:
cmd.CommandText = "select count(ename) from emp where " _
+ "job = '" + TextBox1.Text + "'" ' VB.NET
cmd.CommandText = "select count(ename) from emp where "
+ "job = '" + TextBox1.Text + "'"; // C#
사용자가 CLERK를 입력한 경우 데이터베이스는 아래와 같은 커맨드를 생성합니다:
select count(ename) from emp where job = 'CLERK'
사용자가 로직을 변경하지 않는 이상 모든 것은 정상적으로 동작합니다. 사용자 입력을 기준으로 SQL 문자열이 런타임에 구현되도록 허용한다는 것은, 결국 사용자가 SQL 로직을 변경할 수 있는 권한을 갖게 됨을 의미합니다. 사용자가 CLERK 대신 아래와 같은 내용을 입력했다고 가정해 봅시다:
' or 1=1 --
그 결과로 쿼리의 로직은 아래와 같이 변경됩니다:
select count(ename) from emp where job = '' or 1=1 --'
위에서 작은 따옴표가 연이어 두 개가 사용되고 인라인 코멘트 처리되어, SQL 파서는 코멘트 기호 뒤에 붙는 문자열들을 처리하지 않게 됩니다. 인라인 코멘트를 사용하지 않았다면, 수정된 SQL은 아래와 같은 잘못된 신택스를 갖게 될 것입니다:
select count(ename) from emp where job = '' or 1=1 '
사용자에 의해 수정된 SQL 신택스는 테이블 내의 모든 로우를 카운트할 것을 요구하고 있습니다. "where job = ''"은 카운트를 0으로 만드는 조건이지만, "or 1=1"은 모든 로우를 카운트할 것을 요구합니다.
위의 예에서, SQL 인젝션은 애플리케이션 설계자가 의도한 것과 다른 결과를 얻게 하는 것 이상의 악영향은 끼치지 않고 있습니다. 이번에는 사용자 인증을 위한 애플리케이션 윈도우의 구현 사례를 살펴 보겠습니다:
로그인 정보의 처리를 위해 사용되는 코드가 아래와 같습니다:
cmd.CommandText = "select user_role from app_login where " _
+ "user_id = '" + TextBox1.Text + "' and " _
+ "password = '" + TextBox2.Text + "'" ' VB.NET
cmd.CommandText = "select user_role from app_login where "
+ "user_id = '" + TextBox1.Text + "' and "
+ "password = '" + TextBox2.Text + "'"; // C#
악의를 가진 사용자가 아래와 같이 입력했다고 가정해 봅시다:
admin' or 1=1 --
그 결과로 얻어지는 SQL 구문이 아래와 같습니다.
select user_role from app_login
where user_id = 'admin' or 1=1 --' and password=''
여기서도 인라인 코멘트가 쿼리 로직을 왜곡하는 방편으로 악용되고 있습니다. "--" 인라인 코멘트는 나머지 쿼리 문자열들을 (프로세싱이 불필요한) 코멘트로 처리되게 합니다. 결과로 얻어진 쿼리 문자열에는 어떤 경우에든 TRUE로 평가되는 where 조건절이 포함되어 있습니다. 따라서 사용자는 패스워드를 입력하지 않고도 관리자 권한으로 로그인할 수 있습니다. 이처럼, 기본 쿼리 문자열의 구성 방식에 따라, 사용자 ID나 패스워드를 입력하지 않고도 애플리케이션에 로그인하는 것이 가능할 수도 있습니다.
SQL 인젝션 공격의 방지
사용자 입력을 검증하기 위한 대책을 아무리 철저하게 수립해 놓더라도, 외부 공격자는 매우 영리한 방법으로 공격을 시도하곤 합니다. 입력 값의 검증만으로는 모든 SQL 인젝션 시도를 확인해 낼 수 없습니다. 문제의 근본 원인은 사용자의 입력이 단순한 문자열이 아닌 SQL 신택스의 일부로서 사용된다는 데에 있습니다.
입력을 문자열 매개변수로 이용함으로써, 사용자의 입력을 더 이상 SQL 구문 요소가 아닌 SQL 쿼리의 값으로서 전달되는 단순한 문자열로서 취급할 수 있습니다. OracleParameter 오브젝트를 이용하여, 악의적인 입력 내용을 아래와 같이 무해한 방법으로 해석할 수 있습니다:
select user_role from app_login
where user_id = 'admin' or 1=1 --' and password = ''
여기서 "--"는 인라인 코멘트가 아닌 단순한 텍스트 문자열로 취급되고 있습니다. 따라서 사용자 입력이 실제 실행되는 SQL 구문의 신택스 요소로 사용되지 않습니다.
매개변수화된 쿼리를 생성하려면 쿼리 문자열을 아래와 같이 수정해 줍니다:
cmd.CommandText = "select user_role from app_login where " _
+ "user_id = :user_id and password = :password" ' VB.NET
cmd.CommandText = "select user_role from app_login where "
+ "user_id = :user_id and password = :password"; // C#
다음으로 OracleParameter 오브젝트를 인스턴스화(instantiate)하고 OracleParameters 컬렉션에 추가합니다.
Dim p1 As New OracleParameter("dname", OracleDbType.Varchar2) ' VB.NET
p1.Value = TextBox1.Text
cmd.Parameters.Add(p1)
Dim p2 As New OracleParameter("loc", OracleDbType.Varchar2)
p2.Direction = ParameterDirection.Input ' optional property
p2.Size = 13 ' optional property
p2.Value = TextBox2.Text
cmd.Parameters.Add(p2)
OracleParameter p1 = new OracleParameter("dname", OracleDbType.Varchar2);
p1.Value = textBox1.Text; // C#
cmd.Parameters.Add(p1);
OracleParameter p2 = new OracleParameter("loc", OracleDbType.Varchar2);
p2.Direction = ParameterDirection.Input; ' optional
p2.Size = 13; ' optional
p2.Value = textBox2.Text;
cmd.Parameters.Add(p2);
OracleParameter에는 여러 개의 컨스턱터가 존재하며 Parameters.Add에는 다양한 오버로드(overload)가 사용됩니다. 또, 쿼리의 성격에 따라 여러 가지 속성을 설정할 수 있습니다. 스토어드 프로시저에 매개변수를 전달하는 방식도 위와 동일한 코딩 패턴을 따릅니다.
|
랩 1: 데이터베이스 사용자의 디스플레이
1. 먼저 Oracle.DataAccess에 대한 참조를 추가합니다. 상세 정보가 필요한 경우 오라클 데이터베이스 기반 .NET 애플리케이션 구현하기(한글) 문서를 참고합니다.
2. Windows 폼에 버튼 컨트롤과 레이블 컨트롤을 추가합니다.
3. 오라클 데이터베이스로부터 데이터를 가져오고 폼 결과를 디스플레이할 코드를 추가합니다. 코드를 버튼의 클릭 이벤트 핸들러에 구현합니다. 가장 쉬운 방법은 버튼을 더블 클릭하는 것입니다. 이렇게 하면 이벤트 핸들러를 위한 스텁(stub)이 자동으로 생성됩니다.
4. VB.NET Imports 구문을 Public Class 선언부의 앞 부분에 추가합니다. (참고: 코드 샘플을 다운로드하면 코딩 과정을 전혀 거치지 않고도 랩 내용을 따라 할 수 있습니다.)
Imports System.Data ' VB.NET
Imports Oracle.DataAccess.Client ' ODP.NET Oracle managed provider
using System.Data; // C#
using Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
5. 본 예제는 ORCL10g에 대한 tnsnames.ora 앨리어스가 존재함을 가정하고 있습니다. tnsnames.ora를 사용하고 있지 않다면 연결 문자열을 수정해 주어야 합니다. 필자가 이전에 기고한 아티클(한글)에서 tnsnames.ora에 의존하지 않는 연결 문자열의 구현 방법을 확인할 수 있습니다.
Private Sub, End Sub 구문 사이에 클릭 이벤트 핸들러 코드의 VB.NET 버전을 추가합니다.
Dim oradb As String = "Data Source=ORCL10g;User Id=scott;Password=tiger;"
Dim conn As New OracleConnection(oradb) ' VB.NET
conn.Open()
Dim cmd As New OracleCommand
cmd.Connection = conn
cmd.CommandText = "select user from user_users"
cmd.CommandType = CommandType.Text
Dim dr As OracleDataReader = cmd.ExecuteReader()
dr.Read()
label1.Text = dr.Item("user") ' or dr.Item(0)
conn.Dispose()
버튼의 클릭 이벤트 핸들러에서 "{" 기호와 "}" 기호 사이에 아래의 C# 코드를 추가합니다.
string oradb = "Data Source=ORCL10g;User Id=scott;Password=tiger;";
OracleConnection conn = new OracleConnection(oradb); // C#
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select user from user_users";
cmd.CommandType = CommandType.Text;
OracleDataReader dr = cmd.ExecuteReader();
dr.Read();
label1.Text = dr.GetString(0);
conn.Dispose();
6. 애플리케이션을 실행하고 버튼을 클릭합니다. 아래와 같은 결과를 확인할 수 있습니다:
랩 2: 프록시 인증의 추가
이제 오라클 데이터베이스 서버에서 인지한 사용자가 누구인지 확인할 수 있게 되었습니다. 다음 단계로 프록시 인증을 연결 문자열에 추가합니다.
1. 데이터베이스 서버에 SCOTT로 로그인 했을 때 실제 사용자 역할을 담당하게 될 새로운 사용자를 생성합니다.
create user ActualUser identified by secret;
grant connect to ActualUser;
alter user ActualUser grant connect through scott;
2. 연결 문자열이 ActualUser를 포함하고 프록시 인증을 사용하도록 수정해 줍니다:
Dim oradb As String = "Data Source=ORCL10g;" _
+ "User Id=ActualUser;Password=secret;" _
+ "Proxy User Id=scott;Proxy Password=tiger;" ' VB
string oradb = "Data Source=ORCL10g;"
+ "User Id=ActualUser;Password=secret;"
+ "Proxy User Id=scott;Proxy Password=tiger;"; // C#
3. 애플리케이션을 실행하고 버튼을 클릭합니다. 아래와 같은 결과를 확인할 수 있습니다:
4. 연결 문자열에서 Password=secret를 제거하고 다시 애플리케이션을 실행합니다. 버튼을 누릅니다. 패스워드가 입력되지 않았는데도, 앞에서와 동일한 결과를 확인할 수 있습니다. 이는 실제 사용자로 인증을 거치고 있기 때문입니다. 다시 말해 실제 사용자의 아이디를 데이터베이스에 전달하기만 하고 있습니다.
5. 연결 문자열에 Password=wrongsecret을 추가하고 다시 애플리케이션을 실행합니다. 실제 사용자에 대한 인증이 실패하며, 따라서 애플리케이션은 런타임 에러와 함께 실패합니다.
랩 3: Client Identifier의 사용
1. 랩 2의 연결 문자열을 아래와 같이 수정해 줍니다:
Dim oradb As String = "Data Source=ORCL10g;" _
+ "User Id=scott;Password=tiger;" ' VB
string oradb = "Data Source=ORCL10g;"
+ "User Id=scott;Password=tiger;"; // C#
2. 연결의 Open 메소드 부분을 찾아, 그 아래의 라인에 ClientId 속성을 설정합니다:
conn.Open() ' VB
conn.ClientId = "SomeUser" ' add this
conn.Open(); // C#
conn.ClientId = "SomeUser"; ' add this
3. CommandText 속성을 아래와 같이 변경해 줍니다:
cmd.CommandText =
"SELECT SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER') FROM DUAL" ' VB
cmd.CommandText =
"SELECT SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER') FROM DUAL"; // C#
4. DataReader의 Item 속성을 아래와 같이 수정해 줍니다:
Label1.Text = dr.Item(0) ' VB
label1.Text = dr.GetString(0); // C#
5. 애플리케이션을 실행하고 버튼을 클릭합니다. 아래와 같은 결과를 확인할 수 있습니다:
랩 4: Windows.Identity.Principal의 사용
1. 랩 3의 코드를 수정하여 Imports 또는 using 구문을 추가합니다:
Imports System.Security.Principal ' VB
using System.Security.Principal; // C#
2. WindowsPrincipal 오브젝트를 인스턴스화하고 ClientId 속성을 설정하는데 이용합니다:
Dim user As New WindowsPrincipal(WindowsIdentity.GetCurrent())
conn.ClientId = user.Identity.Name ' VB
WindowsPrincipal user = new WindowsPrincipal(WindowsIdentity.GetCurrent());
conn.ClientId = user.Identity.Name; // C#
3. 애플리케이션을 실행하고 버튼을 클릭합니다. 아래와 같은 결과를 확인할 수 있습니다:
필자의 테스트 머신에서는 ORAWIN도메인에 WinuserWindows 사용자로 로그인하였습니다. 여러분들도 여러분의 도메인 네임(또는 머신 네임)과 Windows 사용자 이름을 확인할 수 있을 것입니다.
랩 5: 매개변수화된 쿼리
1. 앞의 탭에서 두 개의 레이블과 두 개의 텍스트 박스를 추가하여 폼을 수정합니다. 아래와 유사한 결과가 표시될 것입니다:
2. 쿼리 문자열에서 사용자 입력을 받아들이도록 코드를 수정합니다:
cmd.CommandText = "select count(deptno) from dept where " _
+ "dname = '" + TextBox1.Text + "' and " _
+ "loc = '" + TextBox2.Text + "'" ' VB
cmd.CommandText = "select count(deptno) from dept where " _
+ "dname = '" + textBox1.Text + "' and " _
+ "loc = '" + textBox2.Text + "'"; // C#
새로운 테이블을 생성, 입력하는 대신 DEPT 테이블을 이용하여 DNAME 컬럼에 사용자 ID가, LOC 컬럼에 패스워드가 저장된 것처럼 동작하게 유도할 수 있습니다.
3. 애플리케이션을 실행하고 아래와 같이 올바른 사용자 ID와 패스워드를 입력합니다:
Enter SALES for User Id.
Enter CHICAGO for Password.
폼의 카운트가 1로 표시되는 것을 확인할 수 있습니다.
3. 애플리케이션을 실행하고 아래와 같이 올바르지 않은 패스워드를 입력합니다:
SALES, for User Id.
INVALID, for Password.
폼의 카운트가 0으로 표시되는 것을 확인할 수 있습니다.
4. 애플리케이션을 실행합니다. 첫 번째 텍스트 상자에 아래와 같이 올바르지 않은 텍스트를 입력합니다:
SALES' and 1=1 --
이는 패스워드는 입력하지 않고 올바른 사용자 ID만 입력한 경우와 동일합니다. 입력이 올바르지 않으므로 카운트가 0으로 표시되어야 하겠지만 실제로는 그렇지 않습니다. 그 대신 카운트는 1로 표시됩니다. 이는 사용자가 패스워드를 입력하지 않고도 애플리케이션에 로그인할 수 있음을 의미합니다.
5. 쿼리 문자열에서 매개변수를 사용하도록 수정합니다:
cmd.CommandText = "select count(deptno) from dept where " _
+ "dname = :dname and loc = :loc" ' VB
cmd.CommandText = "select count(deptno) from dept where " _
+ "dname = :dname and loc = :loc"; // C#
6. 입력 값을 쿼리의 매개변수에 바인딩하는 코드를 추가합니다:
Dim p1 As New OracleParameter("dname", OracleDbType.Varchar2)
p1.Value = TextBox1.Text
cmd.Parameters.Add(p1)
Dim p2 As New OracleParameter("loc", OracleDbType.Varchar2)
p2.Direction = ParameterDirection.Input ' optional
p2.Size = 13 ' optional
p2.Value = TextBox2.Text
cmd.Parameters.Add(p2) ' VB.NET
OracleParameter p1 = new OracleParameter("dname", OracleDbType.Varchar2);
p1.Value = textBox1.Text;
cmd.Parameters.Add(p1);
OracleParameter p2 = new OracleParameter("loc", OracleDbType.Varchar2);
p2.Direction = ParameterDirection.Input; ' optional
p2.Size = 13; ' optional
p2.Value = textBox2.Text;
cmd.Parameters.Add(p2); // C#
7. C#을 사용하는 경우라면 아래와 같이 수정해 줍니다:
label1.Text = dr.GetOracleDecimal(0).ToString(); // C#
8. 애플리케이션을 실행하고 첫 번째 텍스트 상자에 아래와 같이 올바르지 않은 문자열을 입력합니다:
SALES' and 1=1 --
이번에는 카운트가 0으로 표시됩니다. 입력된 텍스트는 SQL 쿼리 문자열의 일부분이 아니라 varchar2 변수 포맷의 문자열로만 처리되었습니다.
|
John Paul Cook (johnpaulcook@email.com)은 휴스턴에 거주 중인 데이터베이스, .NET 컨설턴트입니다. 그는 .NET, 오라클 등에 관련한 다양한 아티클을 저술하였으며 1986년 이후로 관계형 데이터베이스 애플리케이션을 개발해 왔습니다. 존은 보안, IT 거버넌스, Visual Studio 2005, Oracle 10g 등을 전문 분야로 하고 있습니다. 그는 Oracle Certified DBA, Microsoft MCSD for .NET 자격증을 보유하고 있습니다.
여러분의 의견을 보내 주십시오
|