본문 바로가기
Database/MYSQL

SQL Injection

by 반화넬 2023. 8. 28.
반응형

mysql mariadb sql injection java


A1Injection (인젝션)SQL, OS, XXE(Xml eXternal Entity), LDAP 인젝션 취약점은 신뢰할 수 없는 데이터가 명령어나 쿼리문의 일부분으로써, 인터프리터로 보내질 때 발생한다. 공격자의 악의적인 데이터는 예상하지 못하는 명령을 실행하거나 적절한 권한 없이 데이터에 접근하도록 인터프리터를 속일 수 있다.

Injection은 주입이라는 뜻으로,  SQL 언어를 주입하여 해킹을 하는 기법을 뜻한다.


방어방법.
Statement 대신, PreparedStatement같이 안전한 구문을 사용한 보안코딩 사용.

statement구문을 사용시에는 꼭 컬럼값에 Injection을 유발할 수 있는 '(외따옴표) ;(세미콜론) 같은 DB에서 사용하는 특수문자 값을 제거하여 보안성 강화

그외 SQL Injection을 방지하기 위한 노력

불필요한 권한은 삭제하여, Drop Table같은 공격을 미연에 방지 필요
Mybatis같은 프레임워크 사용시, 별도의 보안 코딩 기법 확인 필요
웹페이지에서 직접 SQL Injection 공격을 시도하여, 빠진 부분이 있는지 체크 필요
Replace같은 경우, 자바스크립트에서만 처리하지 말고, Backend 단에서도 처리 해야 함(URL로 변조 가능)
출처: https://needjarvis.tistory.com/175 [자비스가 필요해]

기본 방어


옵션 1: 준비된 문 사용(매개변수화된 쿼리 포함)
옵션 2: 저장 프로시저 사용
옵션 3: 허용 목록 입력 유효성 검사
옵션 4: 모든 사용자 제공 입력 이스케이프

 

추가 방어

또한: 최소 권한 적용
또한: 보조 방어로 허용 목록 입력 유효성 검사 수행
안전하지 않은 예:

SQL 주입 결함은 일반적으로 다음과 같습니다.

다음(Java) 예제는 UNSAFE이며 공격자가 데이터베이스에서 실행할 쿼리에 코드를 삽입할 수 있도록 합니다. 단순히 쿼리에 추가된 검증되지 않은 "customerName" 매개변수를 사용하면 공격자가 원하는 SQL 코드를 삽입할 수 있습니다. 불행히도 데이터베이스에 액세스하는 이 방법은 너무 일반적입니다.

String query = "SELECT account_balance FROM user_data WHERE user_name = "
             + request.getParameter("customerName");
try {
    Statement statement = connection.createStatement( ... );
    ResultSet results = statement.executeQuery( query );
}

 

1차 방어


방어 옵션 1: 준비된 명령문(매개변수화된 쿼리 포함)


변수 바인딩과 함께 준비된 명령문(매개변수화된 쿼리라고도 함)을 사용하는 것은 모든 개발자가 먼저 데이터베이스 쿼리를 작성하는 방법을 배워야 하는 방법입니다. 작성하기 쉽고 동적 쿼리보다 이해하기 쉽습니다. 매개변수화된 쿼리는 개발자가 먼저 모든 SQL 코드를 정의한 다음 나중에 각 매개변수를 쿼리에 전달하도록 합니다. 이 코딩 스타일을 사용하면 제공된 사용자 입력에 관계없이 데이터베이스에서 코드와 데이터를 구분할 수 있습니다.

준비된 명령문은 공격자가 SQL 명령을 삽입하더라도 공격자가 쿼리의 의도를 변경할 수 없도록 합니다. 아래의 안전한 예에서 공격자가 의 userID를 입력하면 tom' or '1'='1매개변수화된 쿼리는 취약하지 않고 대신 문자 그대로 전체 문자열과 일치하는 사용자 이름을 찾습니다 tom' or '1'='1.

언어별 권장 사항:

Java EE – PreparedStatement()바인드 변수와 함께 사용
.NET – 바인드 변수와 SqlCommand()같은 매개변수화된 쿼리 사용OleDbCommand()
PHP – 강력한 형식의 매개변수화된 쿼리와 함께 PDO 사용(bindParam() 사용)
Hibernate - createQuery()바인드 변수와 함께 사용(Hibernate에서는 명명된 매개변수라고 함)
SQLite - 명령문 개체sqlite3_prepare() 를 만드는 데 사용


드문 경우지만 준비된 명령문이 성능을 저하시킬 수 있습니다. 이러한 상황에 직면했을 때 a) 모든 데이터를 강력하게 검증하거나 b) 준비된 명령문을 사용하는 것보다 아래에 설명된 대로 데이터베이스 공급업체에 특정한 이스케이프 루틴을 사용하여 사용자가 제공한 모든 입력을 이스케이프하는 것이 가장 좋습니다.

Safe Java 준비 명령문 예 :

다음 코드 예제에서는 PreparedStatement매개변수화된 쿼리의 Java 구현을 사용하여 동일한 데이터베이스 쿼리를 실행합니다.

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );


안전한 C# .NET 준비된 명령문 예 :

.NET을 사용하면 훨씬 더 간단합니다. 쿼리 생성 및 실행은 변경되지 않습니다. Parameters.Add()여기에 표시된 대로 호출을 사용하여 쿼리에 매개변수를 전달하기만 하면 됩니다.

String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
  OleDbCommand command = new OleDbCommand(query, connection);
  command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
  OleDbDataReader reader = command.ExecuteReader();
  // …
} catch (OleDbException se) {
  // error handling
}


Java 및 .NET에서 예제를 보여주었지만 Cold Fusion 및 Classic ASP를 포함한 거의 모든 다른 언어는 매개변수화된 쿼리 인터페이스를 지원합니다. HQL( Hibernate Query Language ) 과 같은 SQL 추상화 계층에도 동일한 유형의 주입 문제가 있습니다(이를 HQL 주입 이라고 함). HQL은 매개변수화된 쿼리도 지원하므로 이 문제를 피할 수 있습니다.

HQL(Hibernate Query Language) 준비된 문(명명된 매개변수) 예 :

//First is an unsafe HQL Statement
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
//Here is a safe version of the same query using named parameters
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
Ruby, PHP, Cold Fusion 및 Perl을 비롯한 다른 언어로 된 매개변수화된 쿼리의 
예는 쿼리 매개변수화 치트 시트 또는 이 사이트 를 참조하십시오 .

 


개발자는 모든 SQL 코드가 애플리케이션 내에 있기 때문에 Prepared Statement 접근 방식을 선호하는 경향이 있습니다. 이렇게 하면 응용 프로그램이 상대적으로 데이터베이스에 독립적입니다.

방어 옵션 2: 저장 프로시저


저장 프로시저는 SQL 주입으로부터 항상 안전한 것은 아닙니다. 그러나 특정 표준 저장 프로시저 프로그래밍 구성은 대부분의 저장 프로시저 언어의 표준인 안전하게 구현될 때 매개변수화된 쿼리를 사용하는 것과 동일한 효과를 가집니다.

개발자가 일반적으로 크게 벗어나는 작업을 수행하지 않는 한 개발자는 자동으로 매개변수화되는 매개변수를 사용하여 SQL 문을 빌드해야 합니다. 준비된 명령문과 저장 프로시저의 차이점은 저장 프로시저에 대한 SQL 코드가 정의되어 데이터베이스 자체에 저장된 다음 응용 프로그램에서 호출된다는 것입니다. 이 두 기술은 SQL 주입을 방지하는 데 동일한 효과를 가지므로 조직은 가장 적합한 접근 방식을 선택해야 합니다.

참고: '안전하게 구현됨'은 저장 프로시저에 안전하지 않은 동적 SQL 생성이 포함되지 않음을 의미합니다. 개발자는 일반적으로 저장 프로시저 내에서 동적 SQL을 생성하지 않습니다. 그러나 할 수는 있지만 피해야 합니다. 피할 수 없는 경우 저장 프로시저는 이 문서에 설명된 대로 입력 유효성 검사 또는 적절한 이스케이프를 사용하여 저장 프로시저에 대한 모든 사용자 제공 입력을 사용하여 동적으로 생성된 쿼리에 SQL 코드를 삽입할 수 없도록 해야 합니다. 감사자는 항상 SQL Server 저장 프로시저 내에서 sp_execute, execute 또는 exec 사용을 찾아야 합니다. 다른 공급업체의 유사한 기능에 대해서도 유사한 감사 지침이 필요합니다.

저장 프로시저가 위험을 증가시킬 수 있는 몇 가지 경우도 있습니다. 예를 들어, MS SQL 서버에는 3가지 기본 역할이 있습니다. db_datareader, db_datawriter및 db_owner. 저장 프로시저가 사용되기 전에 DBA는 요구 사항에 따라 웹 서비스 사용자에게 db_datareader 또는 db_datawriter 권한을 부여했습니다. 그러나 저장 프로시저에는 기본적으로 사용할 수 없는 역할인 실행 권한이 필요합니다. 사용자 관리가 중앙 집중화되었지만 이러한 3가지 역할로 제한되는 일부 설정에서는 모든 웹 앱이 db_owner 권한으로 실행되어 저장 프로시저가 작동할 수 있습니다. 당연히 이는 서버가 침해당할 경우 공격자가 데이터베이스에 대한 모든 권한을 가짐을 의미합니다. 이전에는 읽기 액세스 권한만 있었을 수 있습니다.

Safe Java 저장 프로시저 예 :

다음 코드 예제에서는 CallableStatementJava의 저장 프로시저 인터페이스 구현을 사용하여 동일한 데이터베이스 쿼리를 실행합니다. 저장 프로 시 sp_getAccountBalance저는 데이터베이스에 미리 정의되어 있고 위에서 정의한 쿼리와 동일한 기능을 구현해야 합니다.

// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
  CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
  cs.setString(1, custname);
  ResultSet results = cs.executeQuery();
  // … result set handling
} catch (SQLException se) {
  // … logging and error handling
}


안전한 VB .NET 저장 프로시저 예 :

다음 코드 예제에서는 SqlCommand.NET의 저장 프로시저 인터페이스 구현을 사용하여 동일한 데이터베이스 쿼리를 실행합니다. 저장 프로 시 sp_getAccountBalance저는 데이터베이스에 미리 정의되어 있고 위에서 정의한 쿼리와 동일한 기능을 구현해야 합니다.

Try
   Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance", connection)
   command.CommandType = CommandType.StoredProcedure
   command.Parameters.Add(new SqlParameter("@CustomerName", CustomerName.Text))
   Dim reader As SqlDataReader = command.ExecuteReader()
   '...
 Catch se As SqlException
   'error handling
 End Try


방어 옵션 3: 허용 목록 입력 유효성 검사
SQL 쿼리의 다양한 부분은 테이블 또는 열의 이름 및 정렬 순서 표시기(ASC 또는 DESC)와 같은 바인드 변수를 사용할 수 있는 올바른 위치가 아닙니다. 이러한 상황에서는 입력 유효성 검사 또는 쿼리 재설계가 가장 적절한 방어 방법입니다. 테이블 또는 열 이름의 경우 이상적으로는 해당 값이 사용자 매개변수가 아니라 코드에서 나옵니다.

그러나 사용자 매개변수 값이 다른 테이블 이름과 열 이름을 대상으로 하는 경우 매개변수 값은 유효한/예상 테이블 또는 열 이름에 매핑되어 검증되지 않은 사용자 입력이 쿼리에서 끝나지 않도록 해야 합니다. 이것은 잘못된 디자인의 증상이며 시간이 허락한다면 전체 재작성을 고려해야 합니다.

다음은 테이블 이름 유효성 검사의 예입니다.

String tableName;
switch(PARAM):
  case "Value1": tableName = "fooTable";
                 break;
  case "Value2": tableName = "barTable";
                 break;
  ...
  default      : throw new InputValidationException("unexpected value provided"
                                                  + " for table name");


그런 tableName다음 이 쿼리의 테이블 이름에 대해 합법적이고 예상되는 값 중 하나로 알려져 있으므로 SQL 쿼리에 직접 추가할 수 있습니다. 일반 테이블 유효성 검사 기능은 테이블 이름이 예상하지 않은 쿼리에서 사용되기 때문에 데이터 손실로 이어질 수 있다는 점을 염두에 두십시오.

정렬 순서와 같은 간단한 작업의 경우 사용자가 제공한 입력이 부울로 변환된 다음 해당 부울을 사용하여 쿼리에 추가할 안전한 값을 선택하는 것이 가장 좋습니다. 이것은 동적 쿼리 생성에서 매우 표준적인 요구 사항입니다.

예를 들어:

public String someMethod(boolean sortOrder) {
 String SQLquery = "some SQL ... order by Salary " + (sortOrder ? "ASC" : "DESC");`


 ...
사용자 입력이 쿼리에 추가되거나 쿼리에 추가할 값을 선택하는 데 사용되기 전에 날짜, 숫자, 부울, 열거 유형 등과 같은 문자열이 아닌 형식으로 변환될 수 있는 경우 언제든지 그렇게 하는 것이 안전합니다.

입력 유효성 검사는 이 문서의 뒷부분에서 설명하는 바인드 변수를 사용하는 경우에도 모든 경우에 2차 방어 수단으로 권장됩니다. 강력한 입력 유효성 검사를 구현하는 방법에 대한 추가 기술은 입력 유효성 검사 치트 시트 에 설명되어 있습니다.

방어 옵션 4: 모든 사용자 제공 입력 이스케이프
이 기술은 위의 어느 것도 가능하지 않을 때 최후의 수단으로만 사용해야 합니다. 이 방법론은 다른 방어 수단에 비해 취약하고 모든 상황에서 모든 SQL 주입을 방지할 수 없기 때문에 입력 유효성 검사가 더 나은 선택일 것입니다.

이 기술은 쿼리에 입력하기 전에 사용자 입력을 이스케이프하는 것입니다. 구현 시 매우 데이터베이스에 따라 다릅니다. 일반적으로 입력 유효성 검사를 구현하는 것이 비용 효율적이지 않을 때 레거시 코드를 개조하는 것이 좋습니다. 처음부터 빌드된 응용 프로그램 또는 낮은 위험 허용 오차가 필요한 응용 프로그램은 매개 변수화된 쿼리, 저장 프로시저 또는 쿼리를 빌드하는 일종의 ORM(개체 관계형 매퍼)을 사용하여 빌드하거나 다시 작성해야 합니다.

이 기술은 이렇게 작동합니다. 각 DBMS는 특정 종류의 쿼리에 특정한 하나 이상의 문자 이스케이프 체계를 지원합니다. 그런 다음 사용 중인 데이터베이스에 대한 적절한 이스케이프 체계를 사용하여 사용자가 제공한 모든 입력을 이스케이프하면 DBMS는 해당 입력을 개발자가 작성한 SQL 코드와 혼동하지 않으므로 가능한 SQL 주입 취약점을 피할 수 있습니다.

OWASP ESAPI(Enterprise Security API)는 무료 오픈 소스 웹 애플리케이션 보안 제어 라이브러리로 프로그래머가 위험도가 낮은 애플리케이션을 쉽게 작성할 수 있도록 합니다. ESAPI 라이브러리는 프로그래머가 보안을 기존 응용 프로그램에 쉽게 추가할 수 있도록 설계되었습니다. ESAPI 라이브러리는 또한 새로운 개발을 위한 견고한 토대 역할을 합니다.

ESAPI에 대한 자세한 내용 은 여기 OWASP에서 볼 수 있습니다 .
ESAPI 2.x(레거시) 용 javadoc을 사용할 수 있습니다 . 이 코드는 2014년 11월에 GitHub로 마이그레이션되었습니다.
GitHub의 Java용 레거시 ESAPI는 Javadoc이 충분하지 않은 경우 기존 사용을 이해하는 데 도움이 됩니다.
Java GitHub용 다른 ESAPI에 대한 시도 에는 다른 접근 방식이 있고 테스트나 구체적인 코덱이 없습니다.
특히 데이터베이스 인코더에 대한 javadoc을 찾으려면 Codec왼쪽에 있는 클래스를 클릭하십시오. 많은 코덱이 구현되어 있습니다. 두 개의 데이터베이스 전용 코덱은 OracleCodec, 및 MySQLCodec입니다.

All Known Implementing Classes:인터페이스 코덱 페이지 상단에 있는 이름을 클릭하기만 하면 됩니다.

현재 ESAPI에는 현재 다음을 위한 데이터베이스 인코더가 있습니다.



신탁
MySQL(ANSI 및 기본 모드 모두 지원됨)
데이터베이스 인코더는 다음을 위해 출시될 예정입니다.

SQL 서버
PostgreSQL
데이터베이스 인코더가 없는 경우 저희에게 알려주십시오.

데이터베이스별 이스케이프 세부정보¶
자신만의 이스케이프 루틴을 구축하려는 경우 ESAPI 인코더를 위해 개발한 각 데이터베이스에 대한 이스케이프 세부 정보는 다음과 같습니다.

신탁
SQL 서버
DB2
오라클 이스케이프

이 정보는 Oracle Escape 문자 정보 를 기반으로 합니다 .

동적 쿼리 이스케이프

ESAPI 데이터베이스 코덱을 사용하는 것은 매우 간단합니다. Oracle 예제는 다음과 같습니다.

ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );


따라서 Oracle로 가는 코드에서 다음과 같은 기존 동적 쿼리가 생성되는 경우:

String query = "SELECT user_id FROM user_data WHERE user_name = '"
              + req.getParameter("userID")
              + "' and user_password = '" + req.getParameter("pwd") +"'";
try {
    Statement statement = connection.createStatement( … );
    ResultSet results = statement.executeQuery( query );
}


첫 번째 줄을 다음과 같이 다시 작성합니다.

Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM user_data WHERE user_name = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID"))
+ "' and user_password = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";

그리고 이제 제공된 입력에 관계없이 SQL 주입으로부터 안전할 것입니다.

코드 가독성을 극대화하기 위해 다음을 직접 구성할 수도 있습니다 OracleEncoder.

Encoder oe = new OracleEncoder();
String query = "SELECT user_id FROM user_data WHERE user_name = '"
+ oe.encode( req.getParameter("userID")) + "' and user_password = '"
+ oe.encode( req.getParameter("pwd")) +"'";


이러한 유형의 솔루션을 사용하면 ESAPI.encoder().encodeForOracle( )호출 로 전달되는 각 사용자 제공 매개변수를 래핑 하거나 호출 이름을 지정하면 완료됩니다.

문자 교체 끄기

SET DEFINE OFF또는 를 사용 SET SCAN OFF하여 자동 문자 교체가 꺼져 있는지 확인합니다. 이 문자 교체가 켜져 있으면 & 문자는 공격자가 개인 데이터를 검색할 수 있는 SQLPlus 변수 접두사로 처리됩니다.

자세한 내용은 여기 및 여기 를 참조하십시오 .

Like 절에서 와일드카드 문자 이스케이프

키워드 는 LIKE텍스트 스캔 검색을 허용합니다. Oracle에서 밑줄 _문자는 하나의 문자와만 일치하는 반면 앰퍼샌드 %는 0개 이상의 문자와 일치하는 데 사용됩니다. 이러한 문자는 LIKE 절 기준에서 이스케이프되어야 합니다.

예를 들어:

SELECT name FROM emp WHERE id LIKE '%/_%' ESCAPE '/';

SELECT name FROM emp WHERE id LIKE '%\%%' ESCAPE '\';


Oracle 10g 이스케이프

Oracle 10g 이상에 대한 대안 은 전체 문자열을 이스케이프하기 위해 문자열 주위 {에 배치하는 것입니다. 그러나 문자열에 이미 문자 }가 없는지 주의해야 합니다. }이러한 항목을 검색해야 하며 있는 경우 로 바꿔야 합니다 }}. 그렇지 않으면 해당 캐릭터는 탈출을 일찍 끝내고 취약점을 유발할 수 있습니다.

MYSQL 이스케이프

MySQL은 두 가지 이스케이프 모드를 지원합니다.
ANSI_QUOTES SQL 모드, 그리고 이것을 꺼진 모드를 우리가 호출합니다.

 

MySQL 방법.
ANSI SQL'모드: 모든 (단일 틱) 문자를 ''(단일 틱 2개 )로 인코딩하기만 하면 됩니다.

MySQL 모드에서 다음을 수행합니다.

NUL (0x00) --> \0  [This is a zero, not the letter O]
BS  (0x08) --> \b
TAB (0x09) --> \t
LF  (0x0a) --> \n
CR  (0x0d) --> \r
SUB (0x1a) --> \Z
"   (0x22) --> \"
%   (0x25) --> \%
'   (0x27) --> \'
\   (0x5c) --> \\
_   (0x5f) --> \_
all other non-alphanumeric characters with ASCII values
less than 256  --> \c where 'c' is the original non-alphanumeric character.


이 정보는 MySQL Escape 문자 정보 를 기반으로 합니다 .

SQL 서버 이스케이프

우리는 아직 SQL Server 이스케이프 루틴을 구현하지 않았지만 다음에는 SQL Server에 대한 SQL 주입 공격을 방지하는 방법을 설명하는 기사에 대한 좋은 포인터와 링크가 있습니다. 여기를 참조 하십시오 .

DB2 이스케이프

이 정보는 DB2 WebQuery 특수 문자 와 Oracle JDBC DB2 드라이버의 일부 정보를 기반으로 합니다 .
여러 DB2 Universal 드라이버 간의 차이점에 대한 정보입니다 .

모든 입력을 16진수로 인코딩


이스케이프의 다소 특별한 경우는 사용자로부터 받은 전체 문자열을 16진수로 인코딩하는 프로세스입니다(모든 문자를 이스케이프하는 것으로 볼 수 있음). 웹 애플리케이션은 사용자 입력을 SQL 문에 포함하기 전에 16진수로 인코딩해야 합니다. SQL 문은 이러한 사실을 고려하여 데이터를 비교해야 합니다.

예를 들어 sessionID와 일치하는 레코드를 조회해야 하고 사용자가 abc123 문자열을 세션 ID로 전송했다면 select 문은 다음과 같습니다.

SELECT ... FROM session WHERE hex_encode(sessionID) = '616263313233'
hex_encode사용 중인 데이터베이스에 대한 특정 기능으로 대체되어야 합니다. 
문자열 606162313233은 사용자로부터 받은 문자열의 16진수로 인코딩된 버전입니다
(사용자 데이터의 ASCII/UTF-8 코드의 16진수 값 시퀀스입니다).


공격자가 작은따옴표 문자가 포함된 문자열을 전송한 후 SQL 코드를 삽입하려는 경우 생성된 SQL 문은 다음과 같이 보일 것입니다.

.. WHERE hex_encode ( ... ) = '2720 ... '
27작은 따옴표의 ASCII 코드(16진수)로, 문자열의 다른 문자처럼 단순히 16진수로 인코딩됩니다. 
결과 SQL은 숫자와 문자만 포함 a할 f수 있으며 SQL 삽입을 활성화할 수 있는 특수 문자는 
포함할 수 없습니다.



PHP에서 SQLi 이스케이프
준비된 명령문과 매개변수화된 쿼리를 사용하십시오. 이는 매개변수와 별도로 데이터베이스 서버로 전송 및 구문 분석되는 SQL 문입니다. 이렇게 하면 공격자가 악성 SQL을 주입하는 것이 불가능합니다.

이를 달성하기 위해 기본적으로 두 가지 옵션이 있습니다.

PDO 사용 (지원되는 모든 데이터베이스 드라이버용):

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(array('name' => $name));
foreach ($stmt as $row) {
    // do something with $row
}
MySQLi 사용 (MySQL용):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // do something with $row
}


PDO는 범용 옵션입니다. MySQL 이외의 데이터베이스에 연결하는 경우 드라이버별 두 번째 옵션(예: PostgreSQL의 경우 pg_prepare() 및 pg_execute())을 참조할 수 있습니다.

추가 방어


4가지 기본 방어 중 하나를 채택하는 것 외에도 심층 방어를 제공하기 위해 이러한 추가 방어를 모두 채택하는 것이 좋습니다. 이러한 추가 방어는 다음과 같습니다.

최소 권한
허용 목록 입력 유효성 검사

 

최소 권한
성공적인 SQL 주입 공격의 잠재적인 손상을 최소화하려면 환경의 모든 데이터베이스 계정에 할당된 권한을 최소화해야 합니다. 애플리케이션 계정에 DBA 또는 관리자 유형 액세스 권한을 할당하지 마십시오. 우리는 이것이 쉽고 모든 것이 이런 식으로 하면 '작동'하지만 매우 위험하다는 것을 이해합니다.

제거해야 하는 액세스 권한을 파악하려고 하기보다 처음부터 애플리케이션 계정에 필요한 액세스 권한을 결정합니다. 읽기 액세스 권한만 필요한 계정에는 액세스 권한이 필요한 테이블에 대한 읽기 권한만 부여되어야 합니다.

계정이 테이블의 일부에만 액세스해야 하는 경우 데이터의 해당 부분에 대한 액세스를 제한하는 보기를 만들고 대신 기본 테이블이 아닌 보기에 대한 계정 액세스를 할당하는 것을 고려하십시오. 드물게 데이터베이스 계정에 대한 생성 또는 삭제 액세스 권한을 부여합니다.

어디에서나 저장 프로시저를 사용하는 정책을 채택하고 애플리케이션 계정이 자체 쿼리를 직접 실행하도록 허용하지 않는 경우 해당 계정이 필요한 저장 프로시저만 실행할 수 있도록 제한하십시오. 데이터베이스의 테이블에 직접 권한을 부여하지 마십시오.

SQL 인젝션은 데이터베이스 데이터에 대한 유일한 위협이 아닙니다. 공격자는 단순히 매개변수 값을 자신에게 제공된 법적 값 중 하나에서 승인되지 않은 값으로 변경할 수 있지만 애플리케이션 자체에 액세스 권한이 부여될 수 있습니다. 따라서 애플리케이션에 부여된 권한을 최소화하면 공격자가 익스플로잇의 일부로 SQL 주입을 사용하지 않는 경우에도 이러한 무단 액세스 시도의 가능성을 줄일 수 있습니다.

그 동안 DBMS가 실행되는 운영 체제 계정의 권한을 최소화해야 합니다. DBMS를 루트나 시스템으로 실행하지 마십시오! 대부분의 DBMS는 매우 강력한 시스템 계정으로 즉시 실행됩니다. 예를 들어 MySQL은 기본적으로 Windows에서 시스템으로 실행됩니다! DBMS의 OS 계정을 제한된 권한으로 더 적절한 것으로 변경하십시오.

여러 DB 사용자
웹 애플리케이션 디자이너는 웹 애플리케이션에서 동일한 소유자/관리자 계정을 사용하여 데이터베이스에 연결하는 것을 피해야 합니다. 다른 DB 사용자는 다른 웹 응용 프로그램에 사용할 수 있습니다.

일반적으로 데이터베이스에 액세스해야 하는 개별 웹 응용 프로그램에는 웹 응용 프로그램이 DB에 연결하는 데 사용할 지정된 데이터베이스 사용자 계정이 있을 수 있습니다. 그렇게 하면 애플리케이션 디자이너가 액세스 제어에 대해 세분성을 높일 수 있으므로 권한을 최대한 줄일 수 있습니다. 그러면 각 DB 사용자는 필요한 항목에만 액세스하고 필요에 따라 쓰기 액세스 권한을 갖게 됩니다.

예를 들어 로그인 페이지는 테이블의 사용자 이름 및 비밀번호 필드에 대한 읽기 액세스 권한이 필요하지만 어떤 형식의 쓰기 액세스 권한도 필요하지 않습니다(삽입, 업데이트 또는 삭제 없음). 그러나 등록 페이지에는 해당 테이블에 대한 삽입 권한이 반드시 필요합니다. 이 제한은 이러한 웹 앱이 다른 DB 사용자를 사용하여 데이터베이스에 연결하는 경우에만 적용할 수 있습니다.

견해
SQL 보기를 사용하여 테이블의 특정 필드 또는 테이블 조인에 대한 읽기 액세스를 제한하여 액세스 세분성을 더욱 높일 수 있습니다. 잠재적으로 추가적인 이점이 있을 수 있습니다. 예를 들어, 시스템이 (일부 특정 법적 요구 사항으로 인해) 솔트 해시된 암호 대신 사용자의 암호를 저장해야 한다고 가정합니다.

디자이너는 이러한 제한을 보완하기 위해 보기를 사용할 수 있습니다. 테이블에 대한 모든 액세스 권한을 취소하고(소유자/관리자를 제외한 모든 DB 사용자로부터) 필드 자체가 아닌 비밀번호 필드의 해시를 출력하는 뷰를 생성합니다. DB 정보를 훔치는 데 성공한 모든 SQL 주입 공격은 웹 애플리케이션의 DB 사용자가 테이블 자체에 액세스할 수 없기 때문에 암호 해시(키가 있는 해시일 수도 있음)를 훔치는 것으로 제한됩니다.

허용 목록 입력 유효성 검사
다른 것이 불가능할 때(예: 바인드 변수가 올바르지 않은 경우) 1차 방어가 되는 것 외에도 입력 유효성 검사는 무단 입력이 SQL 쿼리에 전달되기 전에 이를 감지하는 데 사용되는 2차 방어가 될 수도 있습니다. 자세한 내용은 입력 검증 치트 시트 를 참조하십시오 . 여기에서 주의해서 진행하십시오. 검증된 데이터는 문자열 작성을 통해 SQL 쿼리에 삽입하는 것이 반드시 안전한 것은 아닙니다.

 
출처: https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

반응형