본문 바로가기
Web Security/SecureCoding

상대 디렉터리 경로 조작

by KkingKkang 2022. 11. 16.

- 외부 입력을 통하여 디렉터리 경로 문자열 생성이 필요한 경우, 외부 입력값에 대해 경로 조작에 사용될 수 있는 문자를 필터링하지 않으면, 예상 밖의 접근 제한 영역에 대한 경로 문자열 구성이 가능해져 시스템 정보누출, 서비스 장애 등을 유발 시킬 수 있다. 

- 즉, 경로 조작을 통해서 공격자가 허용되지 않은 권한을 획득하여, 설정에 관계된 파일을 변경할 수 있거나 실행시킬 수 있다. 

- 외부의 입력이 직접 파일이름을 생성할 수 없도록 한다. 불가피하게 직접 사용하는 경우, 다른 디렉터리의 파일을 접근할 수 없도록 replaceAll() 등의 메소드를 사용하여 위험 문자열(",/,\)을 제거하는 필터를 거치도록 한다. 

- 외부에서 받아들인 데이터 중 미리 정의된 케이스를 제외하고는 모두 무시하도록 한다. 

 

안전하지 않은 코드 ▼

public void accessFile(Properties request) {
	String name = request.getProperty("fileName");
    if( name != null ) {
    	File file = new File("/user/local/tmp/" + name);
        file.delete();
    }
}

 

안전한 코드 ▼

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("/user/local/tmp/"+name);
        if (file != null) file.delete();
   }
}

 

안전하지 않은 코드 2 ▼

import java.io.*;
import java.net.URLDecoder;
import java.sql.*;
import java.util.regex.*;

import javax.servlet.*;

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);
        
        }
   	}
 }

 

안전한 코드 2 ▼

import java.io.*;
import java.net.URLDecoder;
import java.sql.*;
import java.util.regex.*;

import javax.servlet.*;

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 doFile = new File(userHomePath + fileName);
            String filePath = doFile.getAbsolutePath();
            
            if(userHomePath.equals(filePath.substring(0, userHomePath.length()))==false) {
            //error
            }
            
            byte [] buffer = new byte [BUFFER_SIZE];
            FileInputStream inputStream = new FileInputStream(fileName);
            inputStream.read(buffer);
        
        }
   	}
 }

 

먼저 외부에서 입력받은 데이터경로(userHomePath + fileName)을 가지고 파일을 열어 doFile로 저장해둔다.

그 다음 doFile의 절대경로값을 알아낸 후 (getAbsolutePath()) 이것을 원래 입력받은 file 경로(userHomePath)와 비교하여 같지 않으면 파일을 처리하지 않고 그대로 프로세스를 종료한다. 

 

 

반응형

댓글