안녕하세요? 허니입니다. 검증되지 않은 Redirect/Forward 취약점에 대해서 알아보고 대응 방법에 대해 알아보도록 하겠습니다. 사이버 해킹에 대해 공부하시는 학생이나 연구원 분이 계시면 도움이 될 것이라 생각합니다.


사용자 인증을 필요로 하는 페이지에 접근을 하면, 사이트는 로그인 페이지로 이동시킨 뒤 로그인에 성공하면 다시 이전 페이지로 돌아가기 위해 Redirect페이지 주소를 받게 됩니다. 악의적인 사용자가가 Redirect되는 페이지의 값을 피싱 사이트로 변조하여 사용자에게 전송하게 되면 사용자는 로그인 정보가 노출될 수 있습니다.


- Redirect : 클라이언트에서 다른 페이지 변경이 발생
- Forward : 서버 단 자체에서 페이지 변경이 발생

 

 대응방법
타 사이트로의 자동전환에 사용할 URL과 도메인들의 화이트리스트를 사용합니다. 사이트 전환 시 사용자가 기존 사이트를 벗어나려 한다면 경고 하여야 합니다. 요청하는 페이지의 인자와 쿠키에 허용되지 않은 타 사이트 URL이 있는지 점검합니다. 제공된 값이 유효한지, 입력된 사용자에게 허용된 것인지를 점검해야 합니다. Redirect URL에 대한 검증을 실시하여 인가된 URL에 대해서만 Redirect를 허용합니다. 불필요한 Redirect와 Forward의 사용은 피해야 합니다. 자동 연결할 외부 사이트의 URL과 도메인은 화이트리스트로 관리하고, 사용자 입력값을 자동 연결할 사이트 주소로 사용하는 경우에는 입력된 값이 화이트 리스트에 존재하는지 확인해야 합니다. 다음의 예제와 같이, 외부로 연결할 URL과 도메인들은 화이트 리스트를 작성한 후, 그 중에서 선택함으로써 안전하지 않은 사이트로의 접근을 차단할 수 있습니다.

 

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException
{
// 다른 페이지 이동하는 URL 리스트를 만든다.
String allowURL[] = { "http://url1.com", "http://url2.com", "http://url3.com" };
// 입력받는 URL은 미리 정해진 URL의 order로 받는다.
String nurl = request.getParameter("nurl");
try
{
Integer n = Integer.parseInt(nurl);
if ( n >= 0 && n < 3)
response.sendRedirect(allowURL[n]);
}
catch (NumberFormatException nfe)
{
 // 사용자 입력값이 숫자가 아닐 경우 적절히 에러를 처리한다.

}

}

 

접근할 URL 주소를 외부에서 받은 경우, 허용할 URL과 도메인들의 화이트리스트의 범위 안에서만 작동하도록 함으로써 악의적인 사이트 접근을 근원적으로 차단할 수 있습니다.

 

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import _022_Open_Redirect.before.ShortcutInfo;

public class IndexService extends HttpServlet
{
private final String REGISTER_SUBPAGE_SHORTCUT_COMMAND =
"register_sub_page";
private final String OPEN_SUBPAGE_SHORTCUT_COMMAND = "read_writing";
private final String SHORTCUT_ID_PARAM = "writing_id";
private final String TITLE_PARAM = "title";
private final String URL_PARAM = "url";

private Set<String> allowedUrls;

public IndexService()
{
allowedUrls = new HashSet<String>();
allowedUrls.add("http://www.site.com/subpage1.html");
allowedUrls.add("http://www.site.com/subpage2.html");
allowedUrls.add("http://www.site.com/subpage3.html");

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException
{
String command = request.getParameter("command");
...
// sub page의 shortcut을 등록
if (command.equals(REGISTER_SUBPAGE_SHORTCUT_COMMAND))
{
shortcutInfo.id = request.getParameter(SHORTCUT_ID_PARAM);
shortcutInfo.title = request.getParameter(TITLE_PARAM);
shortcutInfo.url = request.getParameter(URL_PARAM);

registerShortcut(shortcutInfo);
...
}

// shortcut을 click하면 sub page로 이동
if (command.equals(OPEN_SUBPAGE_SHORTCUT_COMMAND))
{
String shortcutId = request.getParameter(SHORTCUT_ID_PARAM);

ShortcutInfo shortcutInfo = readShortcut(shortcutId);

if(allowedUrls.contains(shortcutInfo.url) == false)
{
// Error 처리
return;
}
response.sendResponse(shortcutInfo.url);

...

}

...

}

...

}


 

+ Recent posts