IP주소는 하드코딩을 피해라.

책 '자바 코딩, 이럴 땐 이렇게'에서 이번에도 꼭 알아두어야 할 것 같은 부분을 보고 남기려고 한다. 서버 프로그램을 작성할 때 서버의 IP를 소스코드에 하드코딩하는 습관이 바람직하지 않다고 한다.

첫 번째 이유는 관리적인 측면과 유지보수의 측면에서 볼 수 있다. 말 그대로 서버의 IP가 바뀌었거나, 소스 수정을 할 때 하나하나 찾아가며 바꿔야 하기 때문이다. 

두 번째 이유는 보안적인 측면인데, 자바 소스는 디컴파일(decompile)이 가능하기 때문이다. 즉 JVM이 인식하는 코드 .class코드가 유출된다면 IP정보가 쉽게 유출된다는 점이다.


해결 방안

package Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Example {
	private static final String DEFAULT_PROPERTIES_PATH = "d://test.properties";

	private static String serverIP;

	public static void main(String[] args) throws Exception {
		setServerIP(Example.getKey("serverIp"));
	}

	public static String getKey(String key) throws Exception {
		String value = null;
		InputStream is = new FileInputStream(DEFAULT_PROPERTIES_PATH);
		Properties properties = null;
		try {
			properties = new Properties();
			properties.load(is);
			value = properties.getProperty(key);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			try {
				is.close();
			} catch (IOException e) {

			}
		}
		return value;
	}

	public static String getServerIP() {
		return serverIP;
	}

	public static void setServerIP(String serverIP) {
		Example.serverIP = serverIP;
	}

}

summary

먼저 아파치 톰캣이 이클립스에 연동되어있다고 가정한다.
서블릿으로 웹요청을 받아들여 디비랑 통신하기 위한 코드에 대한 템플릿이다. 

DB와의 통신은 서블릿 코드에서 DBConnect.java를 통해 디비에 접속하고 Dao를 통해 질의문을 작성한다.


template_server.zip

git

프로젝트 생성

이클립스 > File > New > Dynamic Web Project

서블릿 파일 생성

File > New > Servlet > 아래 코드

package templete_server.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.ResultSet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class GetData
 */
@WebServlet(name = "GetData", urlPatterns = { "/GetData" })
public class GetData extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public GetData() {
		super();
		// TODO Auto-generated constructor stub
	}

	protected void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();

		/////////////////////////////////////////////////////////////////
		// HTML SOURCE CODE
		out.println("<html>");
		out.println("<head>");
		out.println("<title>Servlet GetData</title>");
		out.println("</head>");
		out.println("<body>");
		out.println("<h1>Servlet GetData at " + request.getContextPath()
				+ "</h1>");
		out.println("<p>");
		out.println("get data : " + request.getParameter("id"));
		out.println("</body>");
		out.println("</html>");

		out.println("");
		// HTML SOURCE CODE
		/////////////////////////////////////////////////////////////////

		String id = request.getParameter("id");
		System.out.println(id);
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

}

DB Connect ( Mysql)

jdbc나 mysql 드라이버는 'WebContent > WEN-INF > lib' 안에 넣어주어야 한다.

첫 번째 코드는 DB를 연결하기 위함이고, 두 번째 코드는 질의문을 서버에 작성할 수 있다.


DBConnector.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBConnector {
	private String driver = "com.mysql.jdbc.Driver";
	private String url = "jdbc:mysql://localhost:3306/dbschema?useUnicode=true&characterEncoding=UTF-8";
	private String user = "userid";	//DB user ID
	private String pwd = "password";	//DB user Password
	private Connection conn = null;
	
	public Connection getConnection(){
		try{
			Class.forName(driver);
		}catch(ClassNotFoundException e){
			System.out.println("BusInfoSystem : 드라이버 로딩 실패(getConnection)");
			System.out.println(e.getMessage());
		}
		try{
			conn = DriverManager.getConnection(url,user,pwd);
		}catch(SQLException e){
			System.out.println("BusInfoSystem : DB 연결 실패(getConnection)");
			System.out.println(e.getMessage());
		}
		return conn;
	}
}

tempateDao.java
package templete_server.dao;

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

public class templateDao {

	private DBConnector dbc;

	public templateDao() {
		dbc = new DBConnector();
	}

	public int getData(int arg1) {
		int result = 0;
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		String sql = new String("select * from dbtable where ID = ?;");

		conn = dbc.getConnection();
		try {
			ps = conn.prepareStatement(sql);
			ps.setInt(1, arg1);
			rs = ps.executeQuery();
			rs.first();
			while (true) {
				result = rs.getInt("id");
				if (rs.isLast())
					break;
				rs.next();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeDB(rs, ps, conn);
		}

		return result;
	}

	/**
	 * DataBase 자원 반환
	 * 
	 * @param rs
	 * @param ps
	 * @param conn
	 */
	private void closeDB(ResultSet rs, Statement ps, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (Exception e) {
			}
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (Exception e) {
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
			}
		}
	}
}


서버 실행환경

 

 학과 서버 사용

 OS

 SunOS 5.10

 GCC

 3.4.6

 Java

 1.5.0_16


클라이언트 실행환경

 Desktop

 조립식

 CPU

 Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz 3.40GHz

 Memory

 8.00 GB

 OS

 Windows 7 Professional K 64bit

 Java

 1.8.0_05

 MySQL

 Ver 14.14 Distrib 5.6.19, for Win64

 Web Server

 Apache Tomcat 7.0.51


서버 프로그램

#include <stdio.h>
#include <sys/types.h> 	// socket, bind, accept, open
#include <sys/socket.h> // socket, bind, listen, accept
#include <unistd.h>		// read, write
#include <sys/stat.h> 	// open
#include <fcntl.h>		// open
#include <errno.h>

#include <string.h>

#define PORT	5500
#define MAXBUF	1024

int main() {
	int server_sockfd;
	int client_sockfd;
	int des_fd;	// file num
	struct sockaddr_in serveraddr, clientaddr;
	int client_len, read_len, file_read_len;	// length
	char buf[MAXBUF];
	
	
	int check_bind;
	

	client_len = sizeof(clientaddr);

	/* socket() */
	server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(server_sockfd == -1) {
		perror("socket error : ");
		exit(0);
	}

	/* bind() */
	bzero(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family		= AF_INET;
	serveraddr.sin_addr.s_addr	= htonl(INADDR_ANY);
	serveraddr.sin_port			= htons(PORT);

	if(bind(server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) > 0) {
		perror("bind error : ");
		exit(0);
	}

	/* listen */
	if(listen(server_sockfd, 5) != 0) {
		perror("listen error : ");
	}

	while(1) {
		char file_name[MAXBUF]; // local val
		memset(buf, 0x00, MAXBUF);
		
		/* accept() */
		client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, &client_len);
		printf("New Client Connect : %s\n", inet_ntoa(clientaddr.sin_addr));

		/* file name */
		read_len = read(client_sockfd, buf, MAXBUF);
		if(read_len > 0) {
			strcpy(file_name, buf);
			printf("%s > %s\n", inet_ntoa(clientaddr.sin_addr), file_name);
		} else {
			close(client_sockfd);
			break;
		}

		/* create file */
FILE_OPEN:
		des_fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL, 0700);
		if(!des_fd) {
			perror("file open error : ");
			break;
		}
		if(errno == EEXIST) {
			close(des_fd);
			size_t len = strlen(file_name);
			file_name[len++] = '_';
			file_name[len++] = 'n';
			file_name[len] = '\0';
			goto FILE_OPEN;
		}
		
		/* file save */
		while(1) {
			memset(buf, 0x00, MAXBUF);
			file_read_len = read(client_sockfd, buf, MAXBUF);
			write(des_fd, buf, file_read_len);

			if(file_read_len == EOF | file_read_len == 0) {
				printf("finish file\n");
				break;
			}


		}


		close(client_sockfd);
		close(des_fd);
	}
	close(server_sockfd);
	return 0;
}


클라이언트 프로그램

#include <WinSock2.h>
#include <stdio.h>
#include <sys/types.h>
#include <io.h>
#include <sys/stat.h>
#include <fcntl.h>


#define PORT	5500
#define IP		"127.0.0.1"
#define MAXBUF	1024

int main() {
	WSADATA		WSAData;
	SOCKADDR_IN	addr;
	SOCKET		s;
	int			sourse_fd;
	char		buf[MAXBUF];
	int			file_name_len, read_len;
	
	if(WSAStartup(MAKEWORD(2,2), &WSAData) != 0) {
		return 1;
	}

	/* socket() */
	s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET) {
		return 1;
	}

	addr.sin_family				= AF_INET;
	addr.sin_addr.S_un.S_addr	= inet_addr(IP);
	addr.sin_port				= htons(PORT);
	if(connect(s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
		perror("connect : ");
		printf("fail to connect.\n");
		closesocket(s);
		return 1;
	}

	memset(buf, 0x00, MAXBUF);
	printf("전송할 파일명 : ");
	scanf("%s", buf);

	printf(" > %s\n", buf);
	file_name_len = strlen(buf);

	send(s, buf, file_name_len, 0);
	sourse_fd = _open(buf, _O_RDONLY);
	if(!sourse_fd) {
		perror("Error : ");
		return 1;
	}
	
	while(1) {
		memset(buf, 0x00, MAXBUF);
		read_len = _read(sourse_fd, buf, MAXBUF);
		send(s, buf, read_len, 0);
		if(read_len == 0) {
			break;
		}
		
	}

	return 0;
}


서버 실행환경

 

 학과 서버 사용

 OS

 SunOS 5.10

 GCC

 3.4.6

 Java

 1.5.0_16


클라이언트 실행환경

 Desktop

 조립식

 CPU

 Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz 3.40GHz

 Memory

 8.00 GB

 OS

 Windows 7 Professional K 64bit

 Java

 1.8.0_05

 MySQL

 Ver 14.14 Distrib 5.6.19, for Win64

 Web Server

 Apache Tomcat 7.0.51


서버 프로그램

#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

#define PORT	5555
#define MAXBUF	1024 /* 클라이언트와 길이를 맞추어준다. */
int main(int argc, char **argv) {
	int server_sockfd, client_sockfd; // 소켓
	int client_len, n;
	int file_read_len;
	char buf[MAXBUF];
	char file_name[MAXBUF];
	struct sockaddr_in clientaddr, serveraddr;
	int source_fd; // 파일 번호
	int chk_bind; // 연결 확인 변수
	int chk_write;
	int read_len;

	client_len = sizeof(clientaddr);

	/* socket() */
	server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(server_sockfd == -1) {
		perror("socket error : ");
		exit(0);
	}

	/* bind() */
	bzero(&serveraddr, sizeof(serveraddr)); // 해당 메모리 영역 초기화
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(PORT);

	chk_bind = bind(server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	if(chk_bind > 0) {
		perror("bind error : ");
		exit(0);
	}

	/* listen() */
	if(listen(server_sockfd, 5)) {
		perror("listen error : ");
	}

	/* 클라이언트 연결 대기 */
	while(1) {
		memset(buf, 0x00, MAXBUF);
		/* accept() */
		client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, &client_len);
		printf("New Client Connect: %s\n", inet_ntoa(clientaddr.sin_addr));
		
		/* 파일명 받기 */
		read_len = read(client_sockfd, buf, MAXBUF);
		if(read_len > 0) {
		    memset(file_name, 0x00, MAXBUF);
			strcpy(file_name, buf);
			printf("%s > %s\n", inet_ntoa(clientaddr.sin_addr), file_name);
		} else {
			close(client_sockfd);
			break;
		}

		/* 파일 열기  */
		source_fd = open(file_name, O_RDONLY); 
		if(!source_fd) {
			perror("file open error : ");
			break ;
		}
		printf("before while\n");
		while(1) {
			memset(buf, 0x00, MAXBUF);
			file_read_len = read(source_fd, buf, MAXBUF);
			printf("\nread : %s",buf); 
			chk_write = write(client_sockfd, buf, MAXBUF);
			if(file_read_len == EOF | file_read_len == 0) {
				printf("finish file\n");
				break;
			}
		}
		close(client_sockfd);
		close(source_fd);
	}
	close(server_sockfd);
	return 0;
}


클라이언트 프로그램

#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h> // _open(), _read() 사용하기 위해서
#include <fcntl.h> // O_CREATIO, _O_RDONLY 사용하기 위해
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h> // _S_IWRITE 사용하기 위해

#define PORT	5555
#define IP		"127.0.0.1"
#define MAXBUF	1024

int main() {
	WSADATA		WSAData;
	SOCKADDR_IN addr;
	SOCKET		s;
	char buffer[1024];
	char file_name[1024];
	int readbytes;
	int i, len;
	int dest_fd;
	int recv_len;
	int totaln = 0;

	if (WSAStartup (MAKEWORD(2,2), &WSAData) != 0) {
		return 1;
	}

	s = socket(AF_INET, SOCK_STREAM, 0);

	if (s == INVALID_SOCKET) {
		return 1;
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	addr.sin_addr.S_un.S_addr = inet_addr(IP);

	if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
		printf("fail to connect\n");
		closesocket(s);
		return 1;
	}

	printf("전송받을 파일명 : ");
	scanf("%s", file_name);

	printf("입력한 파일명 : %s", file_name);
	len = strlen(file_name);
	
	send(s, file_name, len, 0);
	
	// 파일 생성
	dest_fd = _open(file_name, _O_CREAT | _O_EXCL | _O_WRONLY,_S_IWRITE);
	if(!dest_fd) {
		perror("Error ");
		return 1;
	}
	
	if(errno == EEXIST) {
		perror("Error ");
		_close(dest_fd);
		return 1;
	}
	printf("파일 생성 완료\n");


	while(1) {
		memset(buffer, 0x00, MAXBUF);
		recv_len = recv(s, buffer, MAXBUF, 0);
		if(recv_len == 0) {
			printf("finish file\n");
			break;
		}
		totaln += _write(dest_fd, buffer, recv_len);
	}
	_close(dest_fd);
	 
	closesocket(s);
	WSACleanup();

	//system("pause");
	return 0;

}


+ Recent posts