Apache Tomcat Authentication and curl
1. 개요
Apache Tomcat의 가장 대표적인 인증 방식은 다음과 같다.
- BASIC
- DIGEST
- FORM
2. 갈 길이 바쁜 사람들을 위해서
2.1. BASIC, DIGEST 인증 통과
curl -s -k --anyauth --user ${USERNAME}:${PASSWORD} --http1.1 -G ${URL}
-s
: 조용히-k
: insecure, HTTPS 연결에서 서버 인증서가 Let's Encrypt 이고, 클라이언트가 오래된 기기인 경우--anyauth
:--basic
혹은--digest
로 지정할 수도 있다.--user ${USERNAME}:${PASSWORD}
: 아이디와 패스워드--http1.1
: HTTP 1.1-G
: GET 으로 (POST 요청은-X POST
이다)
2.2. FORM 인증 통과
FORM 인증을 통과하는 방법을 요약하면 로그인 정보를 서버로 보내고 나서 서버에서 받은 cooke 를 이후의 요청마다 붙여 보내면 된다.
#!/bin/bash IFS=$'\r\n' USERNAME= PASSWORD= PROTECTED_URL= J_SECURITY_CHECK=$( curl -s -k --http1.1 -G ${PROTECTED_URL} | grep -o "j_security_check.*\" ") J_SECURITY_CHECK_LENGTH=$( expr length $J_SECURITY_CHECK - 2 ) J_SECURITY_CHECK=${J_SECURITY_CHECK:0:$J_SECURITY_CHECK_LENGTH} LOGIN_ACTION_URL="${PROTECTED_URL%/*}/${J_SECURITY_CHECK}" LOGIN_DATA="j_username=${USERNAME}&j_password=${PASSWORD}" HTTP_RESPONSE=$( curl -s -k --data "${LOGIN_DATA}" -X POST --http1.1 -i -L ${LOGIN_ACTION_URL} ) cookie="" index=0 for line in $( echo "$HTTP_RESPONSE" | grep "^Set-Cookie" ); do index=`expr $index + 1` if (( $index > 1 )); then cookie="$cookie&${line:12}" else cookie="${line:12}" fi done echo $cookie
- USERNAME : 로그인 ID
- PASSWORD : 패스워드
- PROTECTED_URL : 로그인이 필요한 URL (http:// 혹은 https:// 로 시작)
다음부터 curl 명령행 옵션에 --cookie "$cookie"
붙이면 된다.
3. FORM Authentication
3.1. 개요
FORM Authentication 은 Apache Tomcat 이 관리한다는 정도만 다르고, 프로그래머들이 구현하는 일반적인 로그인 프로그램과 유사하다.
몇 가지 차이점은 존재하는데, 첫 번째는 Apache Tomcat 의 access_log에 REMOTE_USER 항목이 출력된다.
그 앞에 세운 Apache HTTPD 서버의 access_log 에는 (Tomcat 과 HTTPD 서버가 Session 정보를 공유하지 않는 한) REMOTE_USER 항목이 출력될 수 없다.
두 번째 차이는 GET 요청의 경우, (로그인 페이지로 이동시키지 않고) 바로 로그인 창이 표시되고, 인증에 성공하면, 원래 접근하려고 했던 웹페이지가 (redirect 되어서) 표시된다.
Servlet Filter 를 이용하면 (access_log는 안되겠지만) FORM Authentication 을 비슷하게 흉내낼 수 있다.
웹브라우저에서 FORM Authentication 통과하는 순서는 다음과 같다.
- FORM Authentication 필요한 웹페이지에 접속한다.
- 로그인 페이지가 표시된다(redirect 되지 않는다).
- 아이디와 패스워드를 입력하여 로그인한다.
- (로그인에 성공하면) 1항의 웹페이지가 표시되는데(302 Moved Temporarily 상태를 반환하고, Location 헤더를 따라 1항의 웹페이지를 표시한다),
- 이때 서버에서 Session ID를 cookie 에 담아서 보내고,
- 웹브라우저는 다음 요청부터 Request Header 에 cookie 를 붙인다.
3.2. Apache Tomcat FORM Authentication 로그인 페이지
Apache Tomcat FORM Authentication 로그인 페이지의 핵심 항목은 다음과 같다.
- action : j_security_check;jsessionid= (소스코드는 j_security_check 까지이고, ;jsessionid= 는 자동으로 따라 붙는다)
- method : POST
- 아이디 : j_username
- 패스워드 : j_password
action 은 요청한 URL 과 같은 디렉토리의 j_security_check 이다.
3.3. FORM Authentication이 필요한 웹페이지(${PROTECTED_URL})에 접속해서 action 을 가져온다.
J_SECURITY_CHECK=$( curl -s -k --http1.1 -G ${PROTECTED_URL} | grep -o "j_security_check.*\" ") J_SECURITY_CHECK_LENGTH=$( expr length $J_SECURITY_CHECK - 2 ) J_SECURITY_CHECK=${J_SECURITY_CHECK:0:$J_SECURITY_CHECK_LENGTH} LOGIN_ACTION_URL="${PROTECTED_URL%/*}/${J_SECURITY_CHECK}"
grep -o
: 패턴이 일치하는 부분만 출력한다.- j_security_check 부터
"
까지 가져와서 마지막"
는 잘라낸다. - PROTECTED_URL 에서 디렉토리 부분만 가져와서, j_security_check 에서 마지막
"
를 잘라낸 문자열을 합친다.
만약 PROTECTED_URL 이 https://graha.kr/protected/list.html 이라면, LOGIN_ACTION_URL 은 https://graha.kr/protected/j_security_check 혹은 j_security_check 뒤에 Session ID 가 붙은 모양이 된다.
어떤 이유로든
curl
명령이 실패하면,$J_SECURITY_CHECK
가 공백이 되고,expr
내지length
관련한 에러가 발생한다.
3.4. 로그인 정보(아이디와 패스워드)를 서버로 보낸다.
LOGIN_DATA="j_username=${USERNAME}&j_password=${PASSWORD}" HTTP_RESPONSE=$( curl -s -k --data "${LOGIN_DATA}" -X POST --http1.1 -i -L ${LOGIN_ACTION_URL} )
-s
: 조용히-k
: insecure, HTTPS 연결에서 서버 인증서가 Let's Encrypt 이고, 클라이언트가 오래된 기기인 경우- LOGIN_DATA : 아이디와 패스워드
--data "${LOGIN_DATA}
: 아이디와 패스워드-X POST
: POST 방식으로 요청--http1.1
: HTTP 1.1 로 요청-i
: Response Header 도 출력-L
: Redirect 따라가기
302 Moved Temporarily 상태를 반환하지만,
-L
옵션에 따라 Location 헤더의 URL(PROTECTED_URL) 로 이동하므로,
FORM Authentication 필요했던 웹페이지가 출력된다.
POST 요청은 위와 같은 방식으로는 안되고, 먼저 GET 요청으로 로그인을 한 후에, cookie 붙여 요청해야 한다.
3.5. 다음 요청에서 보낼 cookie 를 가져온다.
Response Header 에서 Set-Cookie :
로 시작하는 것들을 가져와야 한다.
Apache Tomcat 은 JSESSIONID, JSESSIONIDSSO(SSO 설정을 한 경우) 이다.
cookie="" index=0 for line in $( echo "$HTTP_RESPONSE" | grep "^Set-Cookie" ); do index=`expr $index + 1` if (( $index > 1 )); then cookie="$cookie&${line:12}" else cookie="${line:12}" fi done echo $cookie
어떤 이유로든
curl
명령이 실패하면,$HTTP_RESPONSE
가 공백이 되고,$cookie
값이 출력되지 않는다.
3.6. POST 요청
POST 요청의 일반적인 형태는 다음과 같다.
curl -s -k \ --data-urlencode "title=${TITLE}" \ --data-urlencode "contents=${CONTENTS}" \ --http1.1 \ --cookie "$cookie" \ -X POST \ ${POST_URL}
4. 결어
예외처리는 생략했다.
프로그래머들이 HTTP Session 을 이용해서 구현하는 일반적인 로그인 프로그램도 FORM Authentication 과 유사한 방식으로 통과할 수 있다.