작성자 : 기술지원부조태준 tedcho@nextline.net MS-SQL Database 백업하기 ASP.NET 및 Microsoft SQL Server과같은강력한데이터베이스서버의고급서버측기술을통해개발자는동적인데이터중심웹사이트를매우쉽게만들수있습니다. 하지만 ASP.NET 및 SQL의기능은 SQL injections 공격이라는너무나일반적인공격방식을알고있는해커들에게도쉽게악용될수있습니다. SQL injections 공격에대한기본개념은다음과같습니다. 사용자가텍스트상자에텍스트를입력할수있도록웹페이지를만들고이러한텍스트는데이터베이스에대한쿼리를수행하는데사용됩니다. 해커는이러한텍스트상자에쿼리의특성을변경하여백엔드데이터베이스에침입하거나데이터베이스를손상시킬수있는잘못형성된 SQL 문을입력합니다. 1. SQL 문의변환여러 ASP.NET 응용프로그램에서는아래와같이표시된것과같은폼을사용하여사용자를인증합니다. private void cmdlogin_click(object sender, System.EventArgs e) { string strcnx = "server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.open(); //This code is susceptible to SQL injection attacks. string strqry = "SELECT Count(*) FROM Users WHERE UserName='" + txtuser.text + "' AND Password='" + txtpassword.text + "'"; int intrecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intrecs = (int) cmd.executescalar(); if (intrecs>0) { FormsAuthentication.RedirectFromLoginPage(txtUser.Text, false); else { lblmsg.text = "Login attempt failed."; cnx.close(); 표 1
사용자가 BadLogin.aspx의 Login 단추를클릭하면사용자가폼의텍스트상자컨트롤에입력한값과 UserName 및 Password가일치하는 Users 테이블에있는레코드수를계산하는쿼리를실행하여 cmdlogin_click 메서드가사용자를인증하도록시도합니다. 대부분의경우폼은정확히의도된대로작동합니다. 사용자는 Users 테이블에있는레코드와일치하는사용자이름및암호를입력합니다. 동적으로생성된 SQL 쿼리를사용하여일치하는행의개수를검색합니다. 그런다음사용자를인증하고요청된페이지로리디렉션합니다. 잘못된사용자이름및암호를입력하는사용자는인증되지않습니다. 하지만이경우에도해커가 UserName 텍스트상자에겉보기에는잘못된것이없는다음과같은텍스트를입력하여유효한사용자이름및암호를알지못하더라고시스템에침입할수있습니다. Or 1=1 --해커는잘못형성된 SQL을쿼리에주입하여시스템에침입합니다. 이경우의해킹은다음과같이사용자가입력한고정문자열및값의연결을통해실행쿼리가형성되기때문에작동됩니다. String strqry = SELECT Count(*) FROM Users WHERE USerName= + txtuser.text + AND PassWord= + txtpassword.text + ; 유효한사용자이름인 Paul 과암호 password 를사용자가입력하는경우 strqry는다음과같이됩니다. SELECT Count(*) FROM Users WHERE UserName= Paul AND Password= password 하지만해커가다음을입력하면 Or 1=1 쿼리가다음과같이되니다. SELECT Cont(*) FROM Users WHERE UserName= Or 1=1 AND Password= 이중하이픈은 SQL 에서주석의시작부분을나타내므로쿼리는다음과같이됩니다. SELECT Count (*) FROM Users WHERE UserName= Or 1=1 식 1=1은테이블의모든행에대해항상 True이고다른식이포함된 True 식 or d는항상 0 이아님레코드개수를반환합니다. 일부 SQL injections 공격에는폼인증이포함되지않습니다. 폼인증과관련한 SQL injections 공격에필요한사항은동적으로구성된일부 SQL과트러스트되지않은사용자입력이있는응용프로그램입니다. 정확한조건만주어진다면이러한공격으로인한피해범위를해커의 SQL 언어및데이터베이스구성에대한지식수준으로만제한할수있습니다. 이제 badproductlist.aspx에서가져온아래 표2 에표시된코드를살펴보시면, 이페이지는 Northwind 데이터베이스의제품을표시하고사용자가 txtfilter라는텍스트상자를사용하여제품결과목록을필터링하도록할수있습니다. 마지막예에서와같이이페이지는실행 SQL이사용자가입력하는값으로동적으로생성되기때문에 SQL injections 공격에당할가능성이높습니다. 이러한특정페이지는약삭빠른해커가공격하여기밀정보를훔치고, 데이터베이스의데이터를변경하고, 데이터베이스레코드를손상시키고, 심지어는새로운데이터베이스사용자계정을만들수도있기때문에해커에게는천국과도같습니다. SQL Server를포함한대부분의 SQL 호환데이터베이스는메타데이터를 sysobjects, syscolumns, sysindexes 등의이름으로일련의시스템테이블에저장합니다. 즉, 해커는이러한시스템테이블을사용하여데이터베이스에대한스키마정보를확신하고추가적인데이터베이스손상을위한도움을얻을수있습니다. 예를들어다음과같이 txtfilter 텍스트상자에입력된텍스트는데이터베이스에서사용자테이블의이름을확인하는데사용될수있습니다. ' UNION SELECT id, name, '', 0 FROM sysobjects WHERE xtype ='U' --
private void cmdfilter_click(object sender, System.EventArgs e) { dgrproducts.currentpageindex = 0; binddatagrid(); private void binddatagrid() { dgrproducts.datasource = createdataview(); dgrproducts.databind(); private DataView createdataview() { string strcnx = "server=localhost;uid=sa;pwd=;database=northwind;"; string strsql = "SELECT ProductId, ProductName, " + "QuantityPerUnit, UnitPrice FROM Products"; //This code is susceptible to SQL injection attacks. if (txtfilter.text.length > 0) { strsql += " WHERE ProductName LIKE '" + txtfilter.text + "'"; SqlConnection cnx = new SqlConnection(strCnx); SqlDataAdapter sda = new SqlDataAdapter(strSQL, cnx); DataTable dtproducts = new DataTable(); sda.fill(dtproducts); return dtproducts.defaultview; 표 2 UNION 문은해커가한쿼리의결과를다른쿼리로분할할수있도록하기때문에해커에게특히유용합니다. 이러한경우해커는데이터베이스의사용자테이블이름을제품테이블의원래쿼리로분할합니다. 여기에사용된방법은단지열의개수와데이터형식을원래의쿼리와일치시키는것뿐입니다. 이전쿼리는 Users라는테이블이데이터베이스에있음을나타낼수있습니다. 두번째쿼리는 Users 테이블에있는열을노출시킬수있습니다. 해커는이러한정보를사용하여 txtfilter 텍스트상자에다음을입력할수있습니다. ' UNION SELECT 0, UserName, Password, 0 FROM Users -- 이쿼리를입력하면아래그림 1과같이 Users 테이블에있는사용자이름및암호를노출시킵니다.
그림 1 SQL injections 공격은또한데이터를변경하거나데이터베이스를손상시키는데에도사용될수있습니다. SQL injections 해커는 txtfilter 텍스트상자에다음을입력하여첫번째제품의가격을 $18에서 $0.01로바꾸고이러한사실을다른사람이눈치채기전에일부제품을재빠르게구매할수있습니다. '; UPDATE Products SET UnitPrice = 0.01 WHERE ProductId = 1-- 이러한해킹은 SQL Server에서세미콜론이나공백을사용하여구분된여러 SQL 문을함께입력할수있도록허용하기때문에가능합니다. 이예에서 DataGrid는아무것도표시하지않지만업데이트쿼리는성공적으로실행됩니다. 이러한같은기술을사용하면 DROP TABLE 문을실행하거나새로운사용자계정을만들고이사용자를 sysadmin 역할에추가하는시스템저장프로시저를실행할수도있습니다. 이러한해킹은모두 표 2 에표시된 BadProductList.aspx 페이지를사용하여가능합니다. 2. 동일한해킹기회 SQL injections 공격은 SQL Server에만국한된문제가아닙니다. Oracle, MySQL, DB2, Sybase 등의다른데이터베이스에서도이러한종류의공격을받을수있습니다. SQL injections 공격은 SQL 언어에다음과같이강력하고유연한여러기능이포함되어있기때문에가능합니다. 이중하이픈을사용하여 SQL 문에주석을포함시킬수있는기능 여러 SQL 문을함께입력하고이를일괄처리로실행할수있는기능 SQL을사용하여표준시스템테이블집합으로부터메타데이터를쿼리할수있는기능 일반적으로데이터베이스에서지원되는 SQL 언어의기능이강력할수록데이터베이스에대한공격가능성도높아집니다. 따라서 SQL Server가 injections 공격의일반적인대상이되는것입니다. SQL injections 공격은 ASP.NET 응용프로그램으로만제한되지않습니다. 기존의 ASP, Java, JSP 및 PHP 응용프로그램도모두같은위험이있습니다. 실제로 SQL injections 공격은데스크톱응용프로그램에대해서도수행될수있습니다. 예를들어이문서에대한다운로드파일 ( 이문서의맨위에있는링크로제공 ) 에 SQL injections 공격을받을수있는
SQLInjectWinForm이라는 Windows Forms 응용프로그램예제를포함되어있습니다. SQL injections 공격방지를위한한두개의핵심방법을쉽게설명할수도있지만이문제에대해서는계층적방식을사용하는것이가장좋습니다. 이러한방식에서는일부취약성으로인해보안방식중하나가무효화되더라도계속해서보호상태를유지할수있습니다. 권장되는계층은아래그림과같습니다. 그림 2 3. 모든입력에대한검사수행위그림에나열된첫번째원칙은매우중요한것입니다. 모든사용자입력은악의적인것으로간주하십시오! 데이터베이스쿼리에서검사되지않은사용자입력을사용해서는안됩니다. 특히 RegularExpressionValidator 컨트롤과같은 ASP.NET 유효성검사컨트롤은사용자입력의유효성을검사하기위한훌륭한도구입니다. 유효성검사에대한기본적인두가지방식은문제가있는문자를허용하지않거나적은수의필수문자만허용하는것입니다. 하이픈과작은따옴표와같은문제가되는일부문자를쉽게허용하지않을수도있지만이방법은두가지이유로인해적합하지않을수있습니다. 첫번째, 해커에게유용하게사용되는문자를놓칠수있으며, 두번째잘못된문자를표현하는방법이여러가지일수있습니다. 예를들어해커는작은따옴표를이스케이프처리하여유효성검사코드에서놓치도록만들거나이스케이프처리된따옴표를데이터베이스에전달하여일반적인작은따옴표문자와동일하게취급되도록할수있습니다. 더나은방법은허용가능한문자를식별하고해당문자만허용하는것입니다. 이러한방식에는더많은작업이필요하지만입력에대해보다세밀한제어가가능하며보다안전합니다. 어떤방식을사용하던간에일부해킹에는많은수의문자가필요하므로입력에대한길이를제한할수있습니다. GoodLogin.aspx( 다운로드코드에서제공 ) 에는두개의일반식유효성검사컨트롤이포함되며, 이중에서하나는사용자이름에대한컨트롤이고다른하나는암호에대한컨트롤입니다. 또한여기에는 4-12개의숫자, 알파벳문자및밑줄로입력을제한하는다음과같은 ValidationExpression 값이포함되어있습니다. [ d_a-za-z]{4,12 사용자가텍스트상자에잠재적으로손상될가능성이있는문자를입력하도록허용해야할
수있습니다. 예를들어사용자이름의일부로작은따옴표 ( 또는어포스트로피 ) 를입력해야할수있습니다. 이러한경우정규식이나 String.Replace 메서드를사용하여각각의작은따옴표를두개의작은따옴표로바꾸면작은따옴표를안전하게렌더링할수있습니다. 예를들면다음과같습니다. string strsanitizedinput = strinput.replace("'", "''"); 4. 동적 SQL 방지이문서에서설명하는 SQL injections 공격은모두동적 SQL의실행을기반으로합니다. 즉, 사용자가입력한값과 SQL을연결하여생성되는 SQL 문입니다. 하지만매개변수가있는 SQL을사용하면해커가 SQL을코드에주입할수있는가능성이크게줄어듭니다. private void cmdlogin_click(object sender, System.EventArgs e) { string strcnx = ConfigurationSettings.AppSettings["cnxNWindBad"]; using (SqlConnection cnx = new SqlConnection(strCnx)) { SqlParameter prm; cnx.open(); string strqry = "SELECT Count(*) FROM Users WHERE UserName=@username " + "AND Password=@password"; int intrecs; SqlCommand cmd = new SqlCommand(strQry, cnx); cmd.commandtype= CommandType.Text; prm = new SqlParameter("@username",SqlDbType.VarChar,50); prm.direction=parameterdirection.input; prm.value = txtuser.text; cmd.parameters.add(prm); prm = new SqlParameter("@password",SqlDbType.VarChar,50); prm.direction=parameterdirection.input; prm.value = txtpassword.text; cmd.parameters.add(prm); intrecs = (int) cmd.executescalar(); if (intrecs>0) { FormsAuthentication.RedirectFromLoginPage(txtUser.Text, false); else { lblmsg.text = "Login attempt failed."; 표 3 위의표3의코드는매개변수가있는 SQL을사용하여 injections 공격을방지합니다. 매개변수가있는 SQL은사용자가임시 SQL을사용해야하는경우뛰어난성능을보여줍니다. 이러한방식은 IT 부서에서저장프로시저를믿지않거나버전 5.0까지이를지원하지않은 MySQL과같은제품을사용하는경우에필수적입니다. 하지만가능하다면추가기능에대해저장프로시저를사용하여데이터베이스에있는기본테이블에대한모든권한을제거하여그림 1에표시된것과같은쿼리를만들수있는가능성을제거해야합니다.
private void cmdlogin_click(object sender, System.EventArgs e) { string strcnx = ConfigurationSettings.AppSettings["cnxNWindBetter"]; using (SqlConnection cnx = new SqlConnection(strCnx)) { SqlParameter prm; cnx.open(); string straccesslevel; SqlCommand cmd = new SqlCommand("procVerifyUser", cnx); cmd.commandtype= CommandType.StoredProcedure; prm = new SqlParameter("@username",SqlDbType.VarChar,50); prm.direction=parameterdirection.input; prm.value = txtuser.text; cmd.parameters.add(prm); prm = new SqlParameter("@password",SqlDbType.VarChar,50); prm.direction=parameterdirection.input; prm.value = txtpassword.text; cmd.parameters.add(prm); straccesslevel = (string) cmd.executescalar(); if (straccesslevel.length>0) { FormsAuthentication.RedirectFromLoginPage(txtUser.Text, false); else { lblmsg.text = "Login attempt failed."; 표 4 표 4에표시된 BetterLogin.aspx는 procverifyuser라는저장프로시저를사용하여사용자에대한유효성을검사합니다. 5. 최소권한으로실행 BadLogin.aspx 및 BadProductList.aspx에서보여진잘못된구현방법중하나는 sa 계정을통해연결문자열을사용한다는점입니다. 다음은 Web.config에서발견할수있는연결문자열입니다. <add key="cnxnwindbad" value="server=localhost;uid=sa;pwd=;database=northwind;" /> 이계정은로그인생성과데이터베이스삭제등거의모든작업을수행할수있는 System Administrators 역할로실행됩니다. 말할필요도없이응용프로그램데이터베이스액세스
에대해 sa( 또는고급권한이있는계정 ) 를사용하는것은매우잘못된생각입니다. 대신제한된액세스계정을만들고이를사용하도록하는것이훨씬좋습니다. GoodLogin.aspx 에사용된계정은다음과같은연결문자열을사용합니다. <add key="cnxnwindgood" value="server=localhost;uid=nwindreader;pwd=utbbeesozg4d; database=northwind;" /> NWindReader 계정은 db_datareader 역할에따라실행되며이역할은데이터베이스에대한테이블의읽기로액세스를제한합니다. BetterLogin.aspx는저장프로시저와 WebLimitedUser 로그인을사용하여상황을더욱향상시켜줍니다. 이로그인에는해당저장프로시저를실행하는권한만포함되어있으며기본테이블에대해서는어떠한권한도없습니다. 6. 기밀정보를안전하게저장하기그림 1에표시된 SQL injections 공격은 Users 테이블의사용자이름과암호를노출시킵니다. 이러한종류의테이블은일반적으로폼인증을적용하는경우에사용되며대부분의응용프로그램에서암호는일반텍스트로저장됩니다. 더나은방법은데이터베이스에서암호화된암호또는해시된암호를저장하는것입니다. 해시된암호는암호를해독할수없기때문에암호화된암호보다안전합니다. 해시에 salt( 암호화방식으로안전한임의값 ) 를추가하여해시된암호의보안성능을더욱높일수도있습니다. BestLogin.aspx에는사용자가입력한암호를 SecureUsers 테이블에저장된암호의 salt로지정된해시된암호와비교하는코드가포함되어있습니다 ( 표 5 참조 ). 해시된퍼즐에대한또다른측면은 AddSecureUser.aspx에서볼수있습니다. 이페이지를사용하면 salt로지정되어해시된암호를생성하고이를 SecureUsers 테이블에저장할수있습니다. BestLogin.aspx 및 AddSecureUser.aspx는모두 표 6 에표시된것과같이 SaltedHash 클래스라이브러리의코드를사용합니다. Jeff Prosise가만든이코드는 System.Web.Security 네임스페이스의 FormsAuthentication.HashPasswordForStoringInConfigFile 메서드를사용하여암호해시를만들고 System.Security.Cryptography 네임스페이스의 RNGCryptoServiceProvider.GetNonZeroBytes 메서드를사용하여 16바이트의임의 salt 값 (Convert.ToBase64String을사용하여문자열로변환할겨우 24 문자가됨 ) 을만들수있습니다. SQL injections 공격과직접관련되지는않지만 BestLogin.aspx는연결문자열의암호화라는또다른최선의보안구현방법을보여줍니다. 연결문자열은포함된데이터베이스계정암호가들어있는경우 BestLogin.aspx에서와같이특히중요하게보호되어야합니다. 데이터베이스에연결하려면연결문자열의암호를해독해야하기때문에연결문자열은해시할수없습니다. 그대신연결문자열을암호화해야합니다. 다음은 Web.config에저장되어있고 BestLogin.aspx에서사용되는암호화된연결문자열을보여줍니다. <add key="cnxnwindbest" value="aqaaancmnd8bfderjhoawe/ Cl+sBAAAAcWMZ8XhPz0O8jHcS1539LAQAAAACAAAAAAADZgAAqAAAABAAAABdodw0Yh WfcC6+ UjUUOiMwAAAAAASAAACgAAAAEAAAALPzjTRnAPt7/W8v38ikHL5IAAAAzctRyEcHxWkzxeq bq/ V9ogaSqS4UxvKC9zmrXUoJ9mwrNZ/
XZ9LgbfcDXIIAXm2DLRCGRHMtrZrp9yledz0n9kgP3b3s+ X8wFAAAANmLu0UfOJdTc4WjlQQgmZElY7Z8" /> private void cmdlogin_click(object sender, System.EventArgs e) { try { // Grab the encrypted connection string and decrypt it string strcnx = SecureConnection.GetCnxString("cnxNWindBest"); // Establish connection to database using (SqlConnection cnx = new SqlConnection(strCnx)) { SqlParameter prm; cnx.open(); // Execute sproc to retrieved hashed password for this user string strhasheddbpwd; SqlCommand cmd = new SqlCommand("procGetHashedPassword", cnx); cmd.commandtype = CommandType.StoredProcedure; prm = new SqlParameter("@username", SqlDbType.VarChar,50); prm.direction = ParameterDirection.Input; prm.value = txtuser.text; cmd.parameters.add(prm); strhasheddbpwd = (string) cmd.executescalar(); if (strhasheddbpwd.length>0) { // Verify that hashed user-entered password is the same // as the hashed password from the database if (SaltedHash.ValidatePassword(txtPassword.Text, strhasheddbpwd)) { FormsAuthentication.RedirectFromLoginPage( txtuser.text, false); else { lblmsg.text = "Login attempt failed."; else { lblmsg.text = "Login attempt failed."; catch { lblmsg.text = "Login attempt failed."; 표 5
public class SaltedHash { static public bool ValidatePassword (string password, string saltedhash) { // Extract hash and salt string const int LEN = 24; string saltstring = saltedhash.substring(saltedhash.length - LEN); string hash1 = saltedhash.substring(0, saltedhash.length - LEN); // Append the salt string to the password string saltedpassword = password + saltstring; // Hash the salted password string hash2 = FormsAuthentication.HashPasswordForStoringInConfigFile( saltedpassword, "SHA1"); // Compare the hashes return (hash1.compareto(hash2) == 0); static public string CreateSaltedPasswordHash (string password) { // Generate random salt string RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider(); byte[] saltbytes = new byte[16]; csp.getnonzerobytes(saltbytes); string saltstring = Convert.ToBase64String(saltBytes); // Append the salt string to the password string saltedpassword = password + saltstring; // Hash the salted password string hash = FormsAuthentication.HashPasswordForStoringInConfigFile( saltedpassword, "SHA1"); // Append the salt to the hash return hash + saltstring; 표 6
public class SecureConnection { static public string GetCnxString(string configkey) { string strcnx; try { // Grab encrypted connection string from web.config string strencryptedcnx = ConfigurationSettings.AppSettings[configKey]; // Decrypt the connection string DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE); byte[] datatodecrypt = Convert.FromBase64String(strEncryptedCnx); strcnx = Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null)); catch { strcnx=""; return strcnx; 표 7 BestLogin은 표 7 에서와같이 SecureConnection 클래스로부터 GetCnxString 메서드를호출하여 cnxnwindbest AppSetting 값을검색하고이를다음코드로암호해독합니다. string strcnx = SecureConnection.GetCnxString("cnxNWindBest"); 순서대로 SecureConnection 클래스는호출을 Win32 DPAPI( 데이터보호 API) 로래핑하는 DataProtect 클래스라이브러리 ( 여기에표시되지는않지만이문서의다운로드에포함되어있음 ) 를호출합니다. DPAPI의다양한기능중하나는사용자를위해암호화키를관리하는것입니다. DataProtect 클래스라이브러리및이라이브러리를사용할때고려해야하는추가옵션에대한자세한내용은 Microsoft 패턴및연습가이드중하나인 " 보안적인 ASP.NET 응용프로그램작성 : 인증, 권한부여및보안통신 ( 영문 )" 을참조하십시오. http://msdn.microsoft.com/ko-kr/library/ms994921(en-us).aspx EncryptCnxString.aspx 페이지를사용하면시스템특정암호화된연결문자열을만들어서구성파일에붙여넣을수있습니다. 이페이지는그림 3에표시되어있습니다. 물론, 암호화하거나해시해야하는기밀정보에는암호및연결문자열외에도신용카드번호및해커에게노출될경우위험할수있는기타모든정보가포함됩니다. ASP.NET 2.0에는암호해싱및연결문자열암호화를단순하게만들어주는여러기능이포함되어있습니다.
그림 3 7. 안전한실패런타임예외를적절하게처리하지못할경우에도해커는이를악용할수있습니다. 따라서모든프로덕션코드에예외처리기를포함시키는것이중요합니다. 또한처리된예외및처리되지안은예외는항상해커에게도움이될수있는정보를최소한으로만제공해야합니다. 처리된예외의경우오류메시지에서원래사용자에대한유용성과악의적인해커에게너무많은정보를제공하지않는보안성간의균형을맞추어야합니다. 처리되지않은예외의경우에는컴파일요소 (Web.config 파일 ) 의디버그특성을 False로설정하고 customerrors 요소의모드특성을 On 또는 RemoteOnly로설정하여해커에게최소한의정보만표시되도록해야합니다. 예를들어다음을보십시오. <compilation defaultlanguage="c#" debug="false" /> <customerrors mode="remoteonly" /> RemoteOnly 설정은 localhost로부터사이트에액세스하는사용자에게는유용한오류메시지를제공하고원격위치로부터사이트에액세스하는다른사용자에게는예외에대한어떤유용한정보도노출시키지않는일반적인오류메시지를제공하도록보장합니다. 로컬사용자를포함한모든사용자에게일반오류메시지를표시하도록하려면 On 설정을사용하십시오. 프로덕션환경에서는 Off 설정을절대로사용하지마십시오. 8. 결론 SQL injections 공격은보안시스템에침입하여데이터를훔치거나, 변경또는삭제할우려가있기때문에응용프로그램개발자가신중하게다루어야하는문제입니다. 사용하는 ASP.NET 버전과는관계없이이러한공격에는너무나쉽게취약해질수있습니다. 실제로 ASP.NET을사용하지않는경우에도 SQL injections 공격에쉽게당할수있습니다. Windows Forms 응용프로그램과같이사용자입력데이터를사용하여데이터베이스를쿼
리하는모든응용프로그램은잠재적으로 injections 공격의대상이될수있습니다. SQL injections 공격으로부터자신을보호하는방법은그렇게어렵지않습니다. 모든사용자입력에대한유효성을검사및확인하고, 동적 SQL을절대로사용하지않고, 최소권한으로계정을사용하고, 해당기밀정보를해시또는암호화하고, 해커에게유용한정보를제공하지않도록거의정보를표시하지않는오류메시지를제공하는응용프로그램은 SQL injections 공격을매우효과적으로방지할수있습니다. 여러계층의공격방어방법을사용할경우하나의방법이실패하더라도계속해서보호상태를유지할수있습니다.