본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 44일차 (2023-07-24) 자바 프로그래밍_JDBC 프로그래밍_PreparedStatement 사용법

by prometedor 2023. 7. 24.
- 자바 프로그래밍
  - JDBC 프로그래밍(com.eomcs.jdbc)
    - PreparedStatement 사용법

 

MySQL 에 root 계정으로 접속하기

 

사용자 목록 확인

 

원격에서만 접속할 수 있는 사용자 추가

원격에서만 접속할 수 있는 사용자를 만들기:
> CREATE USER '사용자명'@'%' IDENTIFIED BY '비밀번호';
  => 이 경우 study 사용자는 원격에서만 접속 가능하다.

 

MySQL 사용자에게 데이터베이스 사용 권한 부여

GRANT ALL ON 데이터베이스명.* TO '사용자아이디'@'서버주소';

ㄴ studydb 데이터베이스에 대한 모든 권한을 부여함

 

 

생성한 원격 접속이 가능한 사용자로 로그인

 

기본으로 사용할 테이블 설정

 

 

Statement 와 SQL 삽입 공격

jdbc_ex3_Exam0110.java

 SQL 삽입 공격
 => 입력 문자열에 SQL 명령을 삽입하여 프로그램의 의도와 다르게 데이터를 조작하는 행위.
 => 사용자가 입력한 값을 가지고 SQL 문장을 만들 때 이런 문제가 발생한다.
 => 예를 들어 이 예제를 실행할 때 다음과 같이 입력해 보라!
 제목? aaaa
 내용? bbbb'), ('haha', 'hoho'), ('hehe', 'puhul
// Statement 와 SQL 삽입 공격
package com.eomcs.jdbc.ex3;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Scanner;

public class Exam0110 {

  public static void main(String[] args) throws Exception {
    String title = null;
    String contents = null;

    try (Scanner keyboard = new Scanner(System.in)) {
      System.out.print("제목? ");
      title = keyboard.nextLine();

      System.out.print("내용? ");
      contents = keyboard.nextLine();
    }

    try (Connection con = DriverManager.getConnection( //
        "jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement()) {

      int count = stmt.executeUpdate( 
          "insert into x_board(title, contents) values('" + title + "','" + contents + "')");

      // 위에서 사용자가 입력한 값을 가지고 SQL 문장을 만들면 다음과 같다.
      //
      // insert into x_board(title, contents) values('aaaa','bbbb'), ('haha', 'hoho'), ('hehe', 'puhul')
      //

      System.out.println(count + " 개를 입력하였습니다.");
    }
  }
}

 

게시물 관리 - 목록

jdbc_ex2_Exam0120.java

// 게시물 관리 - 목록
package com.eomcs.jdbc.ex2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

// 다음과 같이 게시물 목록을 출력하라!
// 게시물 번호를 내림차순으로 정렬하라.
// ----------------------------
// 번호, 제목, 등록일, 조회수
// 2, aaa, 2019-1-1, 2
// 1, bbb, 2018-12-31, 3
// ----------------------------
public class Exam0120 {

  public static void main(String[] args) throws Exception {
    try (Connection con = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery(
            "select board_id, title, created_date, view_count from x_board order by board_id desc")) {

      System.out.println("번호, 제목, 등록일, 조회수");
      while (rs.next()) {
        // 레코드에서 컬럼 값을 꺼낼 때 컬럼 번호를 지정하는 것 보다
        // 컬럼의 이름을 지정하는 것이 유지보수에 더 좋다.
        //
        System.out.printf("%d, %s, %s, %d\n", //
            rs.getInt("board_id"),
            rs.getString("title"),
            rs.getDate("created_date"),
            rs.getInt("view_count"));
      }
    }
  }
}

 

 

jdbc_ex3_Exam0110.java 실행

ㄴ Exam0110.java 실행 후 위와 같이 입력하면 아래와 같이 입력됨

=>

insert into x_board(title, contents) values('xxx','yyy'), ('하하하', 'okok')

=>

ㄴ 데이터가 2개가 입력됨

 

jdbc_ex2_Exam0120.java 이용하여 확인

ㄴ 11, 12 번 데이터가 2개 입력됨을 확인

 

 

PreparedStatement를 이용하여 SQL 삽입 공격 차단하기

jdbc_ex3_Exam0120.java

// PreparedStatement를 이용하여 SQL 삽입 공격 차단하기
package com.eomcs.jdbc.ex3;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Scanner;

public class Exam0210 {

  public static void main(String[] args) throws Exception {
    String title = null;
    String contents = null;

    try (Scanner keyboard = new Scanner(System.in)) {
      System.out.print("제목? ");
      title = keyboard.nextLine();

      System.out.print("내용? ");
      contents = keyboard.nextLine();
    }

    try (Connection con = DriverManager.getConnection(
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
        PreparedStatement stmt = con.prepareStatement(
            "insert into x_board(title,contents) values(?,?)")) {

      // 위에서 준비한 SQL 문에 값을 설정한다.
      // => ? : 값이 놓일 자리를 의미한다. 'in-parameter' 라 부른다.
      // => in-parameter 에 들어갈 값의 타입에 따라 적절한 setXxx() 메서드를 호출한다.
      //
      stmt.setString(1, title);
      stmt.setString(2, contents);

      // => 이미 SQL 을 준비한 상태이기 때문에 실행할 때는 SQL를 줄 필요가 없다.
      // => setXxx()로 설정된 값은 단순한 텍스트로 처리한 후
      //    SQL을 실행할 때 파라미터로 전달되기 때문에 
      //    SQL 삽입 공격이 불가능 하다.
      int count = stmt.executeUpdate();

      System.out.println(count + " 개를 입력하였습니다.");
    }
  }
}

ㄴ PreparedStatement 이용하기

ㄴ ? 는 값이 놓일 자리를 의미하며 'in-parameter' 라고 부름

=>

=>

kkkkk
ddd'), ('kdkdk', 'kdkdk

ㄴ 이렇게 입력해도  setXxx()로 설정된 값은 단순한 텍스트로 처리한 후 SQL을 실행할 때 파라미터로 전달되기 때문에 SQL 삽입 공격이 불가능

 

jdbc_ex2_Exam0120.java 이용하여 목록 조회

ㄴ 13번 데이터만 입력됨

=>

게시물 관리 - 조회

jdbc_ex2_Exam0120.java

// 게시물 관리 - 조회
package com.eomcs.jdbc.ex2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

// 다음과 같이 게시물을 조회하는 프로그램을 작성하라!
// ----------------------------
// 번호? 1
// 제목: aaa
// 내용: aaaaa
// 등록일: 2019-1-1
// 조회수: 2
//
// 번호? 100
// 해당 번호의 게시물이 존재하지 않습니다.
// ----------------------------
public class Exam0130 {

  public static void main(String[] args) throws Exception {
    String no = null;
    try (Scanner keyScan = new Scanner(System.in)) {
      System.out.print("번호? ");
      no = keyScan.nextLine();
    }

    try (Connection con = DriverManager.getConnection( 
        "jdbc:mariadb://localhost:3306/studydb?user=study&password=1111");
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery( 
            "select * from x_board where board_id = " + no)) {

      if (rs.next()) {
        // 레코드에서 컬럼 값을 꺼낼 때 컬럼 번호를 지정하는 것 보다
        // 컬럼의 이름을 지정하는 것이 유지보수에 더 좋다.
        //
        System.out.printf("제목: %s\n", rs.getString("title"));
        System.out.printf("내용: %s\n", rs.getString("contents"));
        System.out.printf("등록일: %s\n", rs.getDate("created_date"));
        System.out.printf("조회수: %d\n", rs.getInt("view_count"));
      } else {
        System.out.println("해당 번호의 게시물이 존재하지 않습니다.");
      }
    }
  }
}

ㄴ 13번 데이터에 내용에 입력한 그대로가 저장됨을 확인할 수 있음

 

 

PreparedStatement 를 이용하면 SQL 템플릿을 준비해둔 상태가 되기 때문에 실행할 때는 SQL를 줄 필요가 없음

=> 이를 통해 SQL 삽입 공격 방지