[java] PreparedStatement는 SQL 주입을 어떻게 방지하거나 방지합니까?

PreparedStatements가 SQL 주입을 피 / 방지한다는 것을 알고 있습니다. 어떻게하나요? PreparedStatements를 사용하여 생성 된 최종 양식 쿼리는 문자열입니까?



답변

SQL 삽입의 문제점은 사용자 입력이 SQL 문의 일부로 사용된다는 것입니다. 준비된 명령문을 사용하여 사용자 입력을 매개 변수의 컨텐츠로 (SQL 명령의 일부가 아닌) 처리하도록 강제 할 수 있습니다.

그러나 사용자 입력을 준비된 명령문의 매개 변수로 사용하지 않고 대신 문자열을 결합하여 SQL 명령을 빌드하는 경우 준비된 명령문을 사용할 때에도 여전히 SQL 삽입에 취약 합니다.


답변

동일한 작업을 수행하는 두 가지 방법을 고려하십시오.

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

또는

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

“user”가 사용자 입력에서 왔고 사용자 입력이

Robert'); DROP TABLE students; --

그런 다음 첫 번째 경우에, 당신은 푹 빠질 것입니다. 두 번째로, 당신은 안전하고 Little Bobby Tables가 학교에 등록됩니다.


답변

PreparedStatement가 SQL 주입을 어떻게 방지하는지 이해하려면 SQL 쿼리 실행 단계를 이해해야합니다.

1. 컴파일 단계. 2. 실행 단계.

SQL 서버 엔진이 쿼리를받을 때마다 아래 단계를 거쳐야합니다.

쿼리 실행 단계

  1. 구문 분석 및 정규화 단계 :
    이 단계에서는 구문과 의미에 대해 쿼리를 확인합니다. 쿼리에 사용 된 참조 테이블과 컬럼이 존재하는지 확인합니다. 또한해야 할 다른 많은 작업이 있지만 자세히 설명하지 않겠습니다.

  2. 컴파일 단계 :
    이 단계에서는 select, from, where 등과 같은 쿼리에 사용 된 키워드가 기계가 이해할 수있는 형식으로 변환됩니다. 질의를 해석하고 취해야 할 조치를 결정하는 단계입니다. 또한해야 할 다른 많은 작업이 있지만 자세히 설명하지 않겠습니다.

  3. 쿼리 최적화 계획 :
    이 단계에서는 쿼리를 실행할 수있는 방법을 찾기 위해 의사 결정 트리가 생성됩니다. 쿼리를 실행할 수있는 방법의 수와 쿼리를 실행하는 각 방법과 관련된 비용을 알아냅니다. 쿼리 실행을위한 최상의 계획을 선택합니다.

  4. 캐시 :
    쿼리 최적화 계획에서 선택한 최상의 계획은 캐시에 저장되므로 다음에 동일한 쿼리가 들어올 때마다 1 단계, 2 단계 및 3 단계를 다시 통과 할 필요가 없습니다. 다음에 쿼리가 들어 오면 Cache에서 직접 확인하고 거기에서 선택하여 실행합니다.

  5. 실행 단계 :
    이 단계에서는 제공된 쿼리가 실행되고 데이터가 ResultSet객체 로 사용자에게 반환됩니다 .

위 단계에서 PreparedStatement API의 동작

  1. PreparedStatements는 완전한 SQL 쿼리가 아니며 런타임에 실제 사용자가 제공 한 데이터로 대체되는 플레이스 홀더를 포함합니다.

  2. 자리 표시자가 포함 된 PreparedStatment가 SQL Server 엔진으로 전달 될 때마다 아래 단계를 통과합니다.

    1. 구문 분석 및 정규화 단계
    2. 컴파일 단계
    3. 쿼리 최적화 계획
    4. 캐시 (자리 표시자가있는 컴파일 된 쿼리는 캐시에 저장됩니다.)

업데이트 사용자 설정 사용자 이름 =? 및 password =? 어디 id =?

  1. 위의 쿼리는 구문 분석되고 자리 표시 자로 컴파일되고 최적화되고 캐시됩니다. 이 단계의 쿼리는 이미 컴파일되어 컴퓨터에서 이해할 수있는 형식으로 변환됩니다. 따라서 캐시에 저장된 쿼리는 미리 컴파일되고 자리 표시 자만 사용자가 제공 한 데이터로 교체하면된다고 말할 수 있습니다.

  2. 이제 사용자가 제공 한 데이터가 들어오는 런타임에 미리 컴파일 된 쿼리가 캐시에서 선택되고 자리 표시자가 사용자가 제공 한 데이터로 대체됩니다.

PrepareStatementWorking

(자리 표시자가 사용자 데이터로 바뀐 후에는 최종 쿼리가 다시 컴파일 / 해석되지 않으며 SQL Server 엔진은 사용자 데이터를 다시 구문 분석하거나 컴파일해야하는 SQL이 아닌 순수한 데이터로 취급합니다. 이것이 PreparedStatement의 아름다움입니다. )

쿼리가 다시 컴파일 단계를 거치지 않아도되는 경우 자리 표시 자에서 대체 된 모든 데이터는 순수한 데이터로 처리되고 SQL Server 엔진에 의미가 없으며 쿼리를 직접 실행합니다.

참고 : 쿼리 구조를 이해 / 해석하고 의미있는 동작을 제공하는 것은 구문 분석 단계 이후의 컴파일 단계입니다. PreparedStatement의 경우 쿼리는 한 번만 컴파일되고 캐시 된 컴파일 된 쿼리는 항상 선택되어 사용자 데이터를 교체하고 실행합니다.

PreparedStatement의 일회성 컴파일 기능으로 인해 SQL Injection 공격이 없습니다.

https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html 에서 예제와 함께 자세한 설명을 얻을 수 있습니다.


답변

PreparedStatement에서 사용되는 SQL은 드라이버에서 사전 컴파일됩니다. 이 시점부터 매개 변수는 SQL의 실행 가능한 부분이 아닌 리터럴 값으로 드라이버에 전송됩니다. 따라서 매개 변수를 사용하여 SQL을 삽입 할 수 없습니다. PreparedStatements의 또 다른 유익한 부작용 (미리 컴파일 + 매개 변수 만 전송)은 드라이버가 SQL 구문 분석 및 컴파일을 각각 수행 할 필요가 없기 때문에 매개 변수에 대해 다른 값 (드라이버가 PreparedStatements를 지원한다고 가정)을 사용해도 명령문을 여러 번 실행할 때 성능이 향상된다는 것입니다. 매개 변수가 변경되는 시간.


답변

내가 추측 그것이 문자열입니다. 그러나 입력 매개 변수는 데이터베이스로 전송되고 실제 SQL 문을 생성하기 전에 적절한 캐스트 / 변환이 적용됩니다.

예를 들어 CAST / Conversion이 작동하는지 확인하려고 할 수 있습니다.
작동하면 최종 진술을 만들 수 있습니다.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

숫자 매개 변수를 허용하는 SQL 문으로 예제를 시도하십시오.
이제 문자열 변수 (숫자 매개 변수로 허용되는 숫자 내용 포함)를 전달해보십시오. 오류가 발생합니까?

이제 문자열 변수 (숫자 매개 변수로 허용되지 않는 내용 포함)를 전달해보십시오. 무슨 일이 일어나나요?


답변

준비된 진술이 더 안전합니다. 매개 변수를 지정된 유형으로 변환합니다.

예를 들어 매개 변수를 문자열 stmt.setString(1, user);로 변환합니다 user.

매개 변수 에 실행 가능한 명령이 포함 된 SQL 문자열이 있다고 가정합니다. 준비된 명령문을 사용하면이를 허용하지 않습니다.

여기에 메타 문자 (일명 자동 변환)를 추가합니다.

이것은 더 안전합니다.


답변

SQL 주입 : 사용자가 SQL 문의 일부가 될 수있는 것을 입력 할 기회가있을 때

예를 들면 :

문자열 쿼리 = “학생 값에 삽입 ( ‘”+ 사용자 + “‘)”

사용자가 “Robert”를 입력 할 때); DROP TABLE 학생; –”입력으로 SQL 인젝션 발생

어떻게 준비된 진술이 이것을 방지합니까?

문자열 쿼리 = “학생 값에 삽입 ( ‘”+ “: 이름”+ “‘)”

parameters.addValue ( “이름”, 사용자);

=> 사용자가 “Robert ‘를 다시 입력하면); DROP TABLE 학생; –“, 입력 문자열은 리터럴 값으로 드라이버에서 미리 컴파일되며 다음과 같이 캐스팅 될 수 있습니다.

CAST ( ‘로버트’); DROP TABLE 학생; – ‘AS varchar (30))

따라서 마지막에는 문자열이 문자 그대로 테이블에 이름으로 삽입됩니다.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/