9. 불충분한 세션 만료

O 세션 타임아웃 기능을 사용 및 구현하지 않거나 세션 만료 기한을 너무 길게 설정하여 악의적인 사용자가 세션을 불법적으로 사용할 수 있는 취약점


O 근거 자료

개인정보의 안전성 확보조치 기준 제6조(접근통제)

주요정보통신기반시설 기술적 취약점 분석 평가 상세 가이드(p.698)

OWASP TOP10 2021


O 판단 기준

양호세션 종료 시간이 설정되어 있는 경우
취약세션 종료 시간이 설정되어 있지 않아 세션 재사용이 가능한 경우


O 점검 방법

사용자 로그인 및 10분 이상 대기 후 세션 유지 여부 확인

- 로그아웃 이후 세션 재사용 가능 여부 확인


O 조치 방법

- 세션이 발급된 후 장시간 동안 행동이 없을 경우 접속 종료되도록 구현

- 로그인 마다 예측 불가능한 새로운 세션 ID를 발급하여야 하며, 기존 세션 값은 재사용이 불가하도록 로그아웃 시 파기하거나 유효기간 설정

- ASP의 경우 접속자 별로 세션을 생성하여 사용자 정보를 각각 저장할 수 있는 세션 오브젝트를 사용하여 기능 구현

:  사용자가 로그아웃할 경우 세션은 바로 삭제되며, 로그아웃 하지 않고 설정 시간동안 웹 서버로의 요청이 없을 경우 해당 세션은 만료됨

- JSP의 경우  session.getLastAccessedTime()을  이용하여 세션의 마지막 접근 시간으로부터 일정 시간 이내에 다시 세션에 접근하지 않은 경우 자동 세션 만료

: web.xml 파일 내 <seesion-config> 태그를 사용하여 타임아웃 기능 구현

: web.xml, weblogic.xml 두 곳 모두 설정 시 web.xml의 설정이 우선 적용됨 

: 세션 기본 객체가 제공하는 setMaxInactiveInterval() 메소드 사용하여 기능 구현 

: 해당 메소드를 통해 설정할 경우, 타임아웃 시간단위는 초단위임 

- PC 어플리케이션(XML, SOAP API 등)의 경우 응용프로그램 자체 타임아웃이 아닌 로그인 시 세션을 생성하고 DB에 저장하여 관리할 수 있도록 구현

: 로그인 시 세션 생성 및 관리가 어려울 경우 타임스탬프 등 차선책을 적용하여 세션 타임아웃 구현

# 세션 오브젝트

구분설명
PropertyEvent 사용자마다 갖게 되는 고유한 세션 값
Timeout
세션이 유지되는 기간
MethodAbandon
강제로 세션을 소멸시키는 함수
Event
Onstart
각각의 사용자가 처음 방문할 때 발생
Onend
사용자의 세션이 끝나는 시점에 발생


O 시큐어 코딩(Secure Coding) 예시

# JAVA ▶
public class LogoutServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
   
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        session.removeAttribute("userLogin");
        session.invalidate();
        response.sendRedirect(request.getContextPath());
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
}


# ASP ▶
... 중략 ...
// Session의 유지 시간 Setting
Session.timeout = 10
... 중략 ...


# JSP (1) ▶
// web.xml ("분" 단위)
<session-config>
    <session-timeout>10</session-timeout>
</session-config>
        
// weblogic.xml ("초" 단위)_1
<session-descriptor>
    <timeout-secs>10</timeout-secs>
</session-descriptor>

// weblogic.xml ("초" 단위)_2
<session-param>
    <param-name>TimeoutSecs</param-name>
    <param-value>600</param-value>
</session-param>


# JSP (2) ▶
... 중략 ...
// Session의 유지 시간을 Setting
String strTime = Param.getPropertyFromXML("SessionPersistenceTime");
    if (strTime == null) {
        session.setMaxInactiveInterval(600);
    } else {
        session.setMaxInactiveInterval((new Integer(strTime)).intValue());
    }
... 중략 ...


# SOAP, SOAP API ▶
using System;
using System.Text;
using System.Security.Cryptography;

namespace SugarSoap {
    class Program {
        static void Main(string[] args) {
            string UserName = "admin";
            string Password = "password";
            string URL = "http://{site_url}/service/v4/soap.php";
            string SessionID = String.Empty;
            
            // SugarCRM is a web reference added that points to http://{site_url}/service/v4/soap.php?wsdl
            SugarCRM.sugarsoap SugarClient = new SugarCRM.sugarsoap();
            SugarClient.Timeout = 900000;
            SugarClient.Url = URL;

            // Create authentication object
            SugarCRM.user_auth UserAuth = new SugarCRM.user_auth();

            // Populate credentials
            UserAuth.user_name = UserName;
            UserAuth.password = getMD5(Password);

            // Try to authenticate
            SugarCRM.name_value[] LoginList = new SugarCRM.name_value[0];
            SugarCRM.entry_value LoginResult = SugarClient.login(UserAuth, "SoapTest", LoginList);

            // get session id
            SessionID = LoginResult.id;

            if (SessionID != String.Empty) {
                // print session
                Console.WriteLine(SessionID);
            }

            // Pause Window
            Console.ReadLine();
        }

        static private string getMD5(string TextString) {
            MD5 md5 = MD5.Create();
            byte[] inputBuffer = System.Text.Encoding.ASCII.GetBytes(TextString);
            byte[] outputBuffer = md5.ComputeHash(inputBuffer);
            StringBuilder Builder = new StringBuilder(outputBuffer.Length);

            for (int i = 0; i < outputBuffer.Length; i++) {
                Builder.Append(outputBuffer[i].ToString("X2"));
            }

            return Builder.ToString().ToLower(); // lowercase as of 7.9.0.0
        }
    }
}


# XML ▶
<?xml version="1.0" encoding="utf-8"?>
<ADL version="1.0">
    <TypeDefinition url="default_typedef.xml"/>
    <GlobalVariables url="globalvars.xml"/>
    <Application id="xxxx" codepage="utf-8" language="Korean" httptimeout="120" proxyretry="0" proxytimeout="120">
        <Layout>
            .....