IgnatiusHeo

구현단계 보안약점 제거 기준-신뢰되지 않는 URL주소로 자동접속 연결 본문

자격/SW보안약점진단원

구현단계 보안약점 제거 기준-신뢰되지 않는 URL주소로 자동접속 연결

Ignatius Heo 2023. 7. 4. 03:41

작성일: 230704

 

※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.

 

정리 내용: 소프트웨어 보안약점 진단 가이드(239~243p)

 

구분 - 입력데이터 검증 및 표현
설계단계 - HTTP 프로토콜 유효성 검증
https://cryptocurrencyclub.tistory.com/95
개요
사용자로부터 입력되는 값을 외부사이트의 주소로 사용하여 자동으로 연결하는 서버 프로그램은 피싱(Phishing) 공격에 노출되는 취약점을 가질 수 있다.

일반적으로 클라이언트에서 전송된 URL 주소로 연결하기 때문에 안전하다고 생각할 수 있으나, 공격자는 해당 폼의 요청을 변조함으로써 사용자가 위험한 URL로 접속할 수 있도록 공격할 수 있다.

진단 세부사항
(설계단계)
 
 ① 외부입력값을 쿠키 및 HTTP 헤더정보로 사용하는 경우, HTTP 응답분할 취약점을 가지지 않도록 필터링해서 사용해야 한다. 외부 입력값이 쿠키 또는 HTTP 헤더에 포함되는 경우, 사용자 입력값을 필터링하도록 설계하고 있는지 확인한다.
  ㅇ  응답헤더에 값을 쓰는 기능이 식별되고, 입력값에 포함된 개행문자를 필터링 하는 기능이 설계되어 있는지 확인
    → 함수 예: setHeader, addCookie, sendRedirect 등
    → 개행문자: CR(\r) LF(\n)
 
  ㅇ  개행문자에 대한 필터링 적용방법에 따라 안전하게 적용되었는지 확인
    → 필터컴포넌트를 이용하여 공통적용하는 경우: 입력값이 헤더에 포함되는 모든 기능에 안전하게 적용되었는지 확인
    → 각 기능에서 필터기능을 사용하는 경우: 코딩규칙을 개발 가이드에 적용하고 있는지 확인
 
  ㅇ  응답분할이 발생 가능한 입력값을 사용하여 응답분할이 발생되는지 점검할 수 있는 테스트 계획 수립 여부 확인
    → 입력값 예: CR(\r) LF(\n)
 
 
 ② 외부입력값이 페이지 이동(리다이렉트, 포워드)을 위한 URL로 사용되어야 하는 경우, 해당값은 시스템에서 허용된 URL 목록의 선택자로 사용되도록 해야 하며, 페이지가 이동할 URL을 사전에 정의하는지 확인한다.
  ㅇ  시스템에서 이동이 허용된 도메인이나 주소 목록의 정의 여부 확인
  ㅇ  페이지 이동 기능 설계에, 미리 정의한 허용 목록을 이용한 입력값을 검증하는 방법과 모듈의 설계 여부 확인
  ㅇ  외부 입력값을 조작하여 의도된 페이지로 리다이렉트 되는지 점검할 수 있는 테스트 계획의 수립 여부 확인
    → 테스트 값 예: 임의 URL 주소
 
보안대책
(구현단계)

자동 연결할 외부 사이트의 URL과 도메인은 화이트 리스트로 관리하고, 사용자 입력값을 자동 연결할 사이트 주소로 사용하는 경우에는 입력된 값이 화이트 리스트에 존재하는지 확인해야 한다.

진단방법
(구현단계)

①외부 사이트로 리다이렉션하는 함수나 메소드(java의 경우 response.sendRedirect 메소드)가 존재
하는지 확인하고,

②리다이렉션 하는 함수나 메소드의 인자값(url)이 외부 입력값인지 확인한다.

일반적으로 외부에서 입력받는 변수가 아닌 경우 안전하다고 판정한다. 외부에서 입력받는 변수인
경우, 리다이렉션 하는 함수나 메소드의 인자값(url)을 관리하는지 확인해서 유효값 검사 및 화이트
리스트로 관리하는 경우 안전한 것으로 판정한다.

 

다. 코드예제


ㅇ 분석

1: String id = (String)session.getValue("id");
2: String bn = request.getParameter("gubun");
3: 
4: String rd = request.getParameter("redirect");
5: if (id.length() > 0) {
6: String sql = "select level from customer where customer_id = ? ";
7: conn = db.getConnection();
8: pstmt = conn.prepareStatement(sql);
9: pstmt.setString(1, id);
10: rs = pstmt.executeQuery();
11: rs.next();
12: if ("0".equals(rs.getString(1)) && "01AD".equals(bn)) {
13: response.sendRedirect(rd);
14: return;
15: }

ㅇ 설명

1. r4에서 getPara로 리다이렉트 주소를 외부에서 입력 받아 r13에서 리다이렉트 페이지를 연결해줌

2. 근데 리다이렉트 페이지에 대해 검증이 안되어있음

 

ㅇ 수정

1: //
2: String allowedUrl[] = { "/main.do", "/login.jsp", "list.do" };
3: ......
4: String rd = request.getParameter("redirect");
5: try {
6: rd = allowedUrl[Integer.parseInt(rd)];
7: } catch(NumberFormatException e) {
8: return "잘못된 접근입니다.";
9: } catch(ArrayIndexOutOfBoundsException e) {
10: return "잘못된 입력입니다.";
11:}
12:if (id.length() > 0) {
13:......
14: if ("0".equals(rs.getString(1)) && "01AD".equals(bn)) {
15: response.sendRedirect(rd);
16: return;
17: }

ㅇ 설명

1. 페이지 별 리다이렉트 허용 URL을 화이트리스트로 만들어 접근 불가하게 설정함

 


ㅇ 분석

1: 
2: string url = Request["dest"];
3: Response.Redirect(url);

ㅇ 설명

1. 리다이렉트 검증 안하잖아~

 

ㅇ 수정

1: public void AttackOpenRedirect()
2: {
3: string url = Request["dest"];
4: // 외부 입력값이 로컬 URL인지 확인합니다. MVC 3 이상의 프레임워크를 사용할 경우,
System.Web.Mvc 에 정의되어있는 Url.isLocalUrl 을 바로 사용할 수 있습니다.
5: if(isLocalUri(url)) Response.Redirect(url);
6: }
7: private bool IsLocalUrl(string url)
8: {
9: if(string.IsNullOrEmpty(url))
10: {
11: return false;
12: }
13: Uri absoluteUri;
14: if(Uri.TryCreate(url, UriKind.Absolute, out absoluteUri))
15: {
16: return String.Equals(this.Request.Url.Host, absoluteUri.Host,
StringComparison.OrdinalIgnoreCase);
17: }
18: else
19: {
20: bool isLocal = !url.StartsWith("http:",StringComparison.OrdinalIgnoreCase)
&& !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase)
&& Uri.IsWellFormedUriString(url, UriKind.Relative);
21: return isLocal;
22: }
}

ㅇ 설명

1. url.isLocal을 사용해 local url 여부를 확인

 

 

 

 

끝.