ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SQL Injection 예제
    보안(Security)/Injection 2018. 9. 10. 09:38

    ※ 크롬, 오페라 등 브라우저는 인터넷 익스플로러와 비교해 구현 방법에 차이가 있다. 같은 html 문서를 작성해도 브라우저에 따라 결과가 다를 수 있다는 말이다. 보안 포스팅을 위해 문제 상황을 발생시키기에 인터넷 익스플로러가 적합해 보안 포스팅에서는 항상 인터넷 익스플로러를 실습 브라우저로 사용한다. 이번 예제는 수강중인 교육 강사님이 주신 예제를 통해 확인했다.

    CWE 는 Common Weakness Enumeration의 약어를 가진 미 산하 기관이 운영하는 사이트로
    - 웹 개발시 취약점에 대한 정의설명해당 취약점의 플랫폼빈도 및 예제코드완화방법 기술 등을 참고할 수 있는 사이트이다. 개발을 업으로 삼는다면 PL이나 PM이 되고 나서야 참고할 일이 있을만한 사이트이다. CWE에서는 웹 보안을 위해 반드시 지켜야 할 사례들을 리스트로 구분해뒀는데 그중 가장 대표적인 하나가 SQL / Command Injection이다.

    Injection은 Form이 있으면 언제든 시도할 수 있는 공격기법으로동적 SQL문 사용하는 경우 입력에 쿼리문 삽입시켜 계정이 없더라도 로그인 및 테이블에 대한 정보를 추출할 수 있는 기법이다.
    가장 대표적인 예가 로그인시 Id에 ‘ 입력이 가능한 경우이다.


    @Override
    	public MemberVO login(LoginVO loginVO) {
    		Connection conn = null;
    		Statement stmt = null;
    		ResultSet rs = null;
    		
    		//FIXME SQLInjection 방어하기
    		String query = " SELECT USER_ID, USER_NAME, USER_PASSWORD "
    				+ "FROM USERS WHERE USER_ID = '" + loginVO.getId() + "' AND USER_PASSWORD = '" + loginVO.getPassword() + "'";
    		
    		
    		try {
    			conn = dataSource.getConnection();
    			
    			stmt = conn.createStatement();
    			
    			rs = stmt.executeQuery(query);
    			MemberVO memberVO = null;
    			
    			if(rs.next()) {
    				memberVO = new MemberVO();
    				memberVO.setId(rs.getString(1));
    				memberVO.setUserName(rs.getString(2));
    				memberVO.setPassword(rs.getString(3));
    			}
    			return memberVO;
    		}
    		catch(SQLException sqle) {
    			throw new RuntimeException(sqle.getMessage(), sqle);
    		}
    		finally {
    			DBCloseUtil.close(conn, stmt, rs);
    		}
    	}
    

    2000년대 초반 로그인 기능의 Dao 클래스. Statement 객체를 사용하고 있다.

    ※ 데이터베이스에는 id : admin / password : admin의 유저 하나만 등록된 상태이다.
    - 다음과 같이 로그인을 할 때 아이디 뒤에 '를 붙인 후 -- 를 입력한 후 비밀번호는 틀린 것으로 입력한다. admin 계정의 경우 password 또한 5자이기 때문에 6자를 입력하면 로그인이 실패해야 하지만 완료되고, 이후 사이트의 페이지들에 접근이 가능해진다. 어떤 이유일까?

    로그인 Dao의 쿼리문을 볼 수 있도록 사이트를 제작했다. 아이디와 비밀번호를 맞게 입력한 경우 인증성공이라고 나온다.

    다른 비밀번호 (11111)을 입력한 경우 인증 실패가 출력된다.

    admin'--을 입력한 순간의 실행 쿼리문을 보면 USER_ID 칼럼만 확인 후 닫아버리고 -- 를 통해 이후 부분을 주석화 시키는 것이다. 따라서 아래 AND 조건을 확인하지 않는 것이다. 이를 응용하면

    다음과 같이 or 로 항상 참인 명제를 넣으면 아이디조차 몰라도 접근이 가능해진다.
    Dao코드에서 Statement 객체를 사용하면 이러한 SQL Injection을 피할 수 없다. 오라클에서는 이를 극복하기 위해 PreparedStatement를 제공해 -- 를 주석이 아닌 Text로 인식하도록 처리를 해 보안성을 강화한다. 따라서 Dao 코드는 다음과 같이 변경되어야 한다.




    @Override
    	public MemberVO login(LoginVO loginVO) {
    		Connection conn = null;
    		PreparedStatement stmt = null;
    		ResultSet rs = null;
    		
    		//FIXME SQLInjection 방어하기
    		String query = " SELECT USER_ID, USER_NAME, USER_PASSWORD "
    				+ " FROM USERS WHERE USER_ID = ? AND USER_PASSWORD = ? ";
    		
    		try {
    			conn = dataSource.getConnection();
    			stmt = conn.prepareStatement(query);
    			
    			stmt.setString(1, loginVO.getId());
    			stmt.setString(2, loginVO.getPassword());
    			
    			rs = stmt.executeQuery();
    			
    			MemberVO memberVO = null;
    			
    			if(rs.next()) {
    				memberVO = new MemberVO();
    				memberVO.setId(rs.getString(1));
    				memberVO.setUserName(rs.getString(2));
    				memberVO.setPassword(rs.getString(3));
    			}
    			return memberVO;
    		}
    		catch(SQLException sqle) {
    			throw new RuntimeException(sqle.getMessage(), sqle);
    		}
    		finally {
    			DBCloseUtil.close(conn, stmt, rs);
    		}
    	}
    

    개선된 Dao 클래스. PreparedStatement를 사용해 보안성을 강화했다. 다음과 같이 코드를 수정하면


    '보안(Security) > Injection' 카테고리의 다른 글

    커맨드 인젝션 (Command Injection) 예제  (0) 2018.09.10
Designed by Tistory.