삶 가운데 남긴 기록 AACII.TISTORY.COM
JAVA 보안 Path Traversal 본문
Path Traversal 디렉토리 경로 조작
상대 디렉토리 경로 조작(Relative Path Traversal)
외부 입력으로 디렉터리 경로 문자열 생성이 필요한 경우 외부 입력을 필터링 하지 않으면 시스템 경로 조작이나 시스템 정보 유출, 장애, 권한 획득 등을 유발할 수 있습니다.
외부 입력에 대해서 다른 디렉터리 파일에 접근 할 수 없도록 replaceAll() 등을 이용하여 위험한 물자열(", / \ 등)을 제거해서 방어합니다.
외부 입력은 받더라도 내부 처리는 미리 정의해 놓은 데이터(경로)를 사용합니다.
안전하지 않은 예
public void accessFile(Properties request){
...
String name = request.getProperty("filename");
if(name != null){
File file = new File("/usr/local/tmp/" + name);
file.delete();
}
...
}
위에서 name 파라메터로 ../../../root/ 이나 ../../../etc/passwd 등 허가되지 않은 경로 넘기면 해당 경로에 접근 할수도 있습니다.
안전한 코드의 예
public void accessFile(Properties request){
String name = request.getProperty("user");
if(name != null && !"".equals(name)){
name = name.replaceAll("/", "");
name = name.replaceAll("\\", "");
name = name.replaceAll(".", "");
name = name.replaceAll("&", "");
name = name + "-report";
File file = new File("/usr/local/tmp/" + name);
...
}
}
안전하지 않은 예
public class DocumentService extends HttpServlet {
private final String READ_DOCUMENT = "read_document";
private final String USER_ID_PARAM = "user_id";
private final String FILE_NAME_PARAM = "file_name";
private final int BUFFER_SIZE = 256;
...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String command = request.getParameter("command");
...
if (command.equals(READ_DOCUMENT)) {
String userId = request.getParameter(USER_ID_PARAM);
String fileName = request.getParameter(FILE_NAME_PARAM);
byte [] buffer = new byte [BUFFER_SIZE];
FileInputStream inputStream = new FileInputStream(fileName);
inputStream.read(buffer);
...
}
...
}
...
}
경로에 대한 확인하는 코드를 추가하고 먼저 외부에서 입력받은 경로를 가지고 파일을 열어 docFile로 저장하고 그 저장된 파일의 절대 경로를 알아낸 후(getAbsolutePath()) 이것을 원래 입력받은 파일 경로(userHomePath)와 비교하여 같지 않으면 처리하지 않고 에러처리 하는 방식으로 방어합니다.
안전한 예
public class DocumentService extends HttpServlet {
private final String READ_DOCUMENT = "read_document";
private final String USER_ID_PARAM = "user_id";
private final String FILE_NAME_PARAM = "file_name";
private final int BUFFER_SIZE = 256;
...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String command = request.getParameter("command");
...
if (command.equals(READ_DOCUMENT)) {
String userId = request.getParameter(USER_ID_PARAM);
String fileName = request.getParameter(FILE_NAME_PARAM);
fileName = URLDecoder.decode(fileName);
String userHomePath = getUserHomeDir(userId);
File docFile = new File(userHomePath + fileName);
String filePath = docFile.getAbsolutePath();
if(userHomePath.equals(filePath.substring(0, userHomePath.length()))==false) {
// Error 처리
return;
}
byte [] buffer = new byte [BUFFER_SIZE];
FileInputStream inputStream = new FileInputStream(filePath);
inputStream.read(buffer);
...
}
...
}
...
}
Absolute Path Traversal (절대 경로 조작)
외부 입력이 파일 시스템의 경로를 직접 제어할 수 있게 하면 시스템 파일도 접근 가능해지기 때문에 안전하지 않습니다.
외부 입력을 통해 파일이름의 생성 및 접근을 허용하면 안되고, 접근이 허용된 파일의 리스트에서 선택하도록 프로그램을 작성해야 합니다.
안전하지 않은 예
public class DocumentService extends HttpServlet {
private final String APPLY_STYPLE_COMMAND = "apply_style";
private final String USER_ID_PARAM = "user_id";
private final String STYLE_FILE_NAME_PARAM = "styple_file_name";
private final int BUFFER_SIZE = 256;
//생략
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String command = request.getParameter("command");
//생략
if (command.equals(APPLY_STYPLE_COMMAND)) {
String userId = request.getParameter(USER_ID_PARAM);
String styleFileName = request.getParameter(STYLE_FILE_NAME_PARAM);
String userHomePath = getUserHomeDir(userId);
byte[] buffer = new byte[BUFFER_SIZE];
FileInputStream inputStream = new FileInputStream(userHomePath + styleFileName);
inputStream.read(buffer);
//생략
}
//생략
}
//생략
}
외부 입력(userId)로부터 직접 파일을 생성하는 경우 공격에 노출 됩니다.
안전한 예
public class DocumentService extends HttpServlet {
private final String APPLY_STYPLE_COMMAND = "apply_style";
private final String USER_ID_PARAM = "user_id";
private final String STYLE_FILE_NAME_PARAM = "styple_file_name";
private final int BUFFER_SIZE = 256;
private Hashtable<String, String> styleFileNames;
//...
public DocumentService() {
styleFileNames = new Hashtable<String, String>();
styleFileNames.put("Normal", "NormalStyle.cfg");
styleFileNames.put("Classic", "ClassicStyle_1.cfg");
styleFileNames.put("Gothic", "ClassicStyle_2.cfg");
//...
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String command = request.getParameter("command");
//...
if (command.equals(APPLY_STYPLE_COMMAND)) {
String userId = request.getParameter(USER_ID_PARAM);
String styleName = request.getParameter(STYLE_NAME_PARAM);
String userHomePath = getUserHomeDir(userId);
String styleFilePath = userHomePath + styleFileNames.get(styleName);
byte[] buffer = new byte[BUFFER_SIZE];
FileInputStream inputStream = new FileInputStream(userHomePath + styleName);
inputStream.read(buffer);
//...
}
//...
}
//...
}
외부 입력(userID)로부터 값을 받은 후 미리 정해진 styleFileNames 와 매칭시켜 실제적인 파일명으로 사용합니다.
그 후 경로값고 합쳐서 완전한 절대 경로 파일을 만든 후 생성합니다.
참고
http://cwe.mitre.org/data/definitions/23.html
http://cwe.mitre.org/data/definitions/22.html
https://www.owasp.org/index.php
http://cwe.mitre.org/data/definitions/36.html
http://cwe.mitre.org/data/definitions/22.html
'DEV&OPS > Java' 카테고리의 다른 글
JAVA 보안 정수 오버플로우(Integer Overflow) (0) | 2022.08.16 |
---|---|
JAVA 보안 HTTP Response Splitting (0) | 2022.08.16 |
JAVA 보안 Cross-Site Request Forgery (0) | 2022.08.04 |
JAVA 보안 URL Redirection to Untrusted Site (0) | 2022.08.04 |
JAVA 보안 Unrestricted Upload of File with Dangerous Type (0) | 2022.08.04 |