grep

Engineering

HTTP 쿠키와 톰캣 버전별 이슈

NHN

2018년 12월 10일

원문에서 보기 ↗

HTTP 쿠키

쿠키의 목적

1. 세션 관리 (로그인)

2. 개인화 (검색 결과 설정, 테마 등)

3. 트래킹 (사용자 행동)

쿠키 만드는 방법

1. 유저가 서버에 페이지를 요청합니다.

GET /test HTTP/1.1
Host: localhost:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
DNT: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: ko,en-US;q=0.9,en;q=0.8

2. 서버에서 응답과 함께 Set-Cookie 헤더를 전송합니다.

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: CookieName1=Example1; Expires=Tue, 27-Nov-2018 02:53:13 GMT
Set-Cookie: CookieName2=Example2; Expires=Tue, 27-Nov-2018 02:53:13 GMT
Set-Cookie: JSESSIONID=8EB8434C5776358C84017077E11A3300; Path=/
Content-Type: text/html;charset=ISO-8859-1
Content-Language: ko
Content-Length: 316

3. 유저 에이전트가 Set-Cookie 헤더에서 전달된 값을 가지고 쿠키를 생성해서 저장합니다.

4.png

4. 이렇게 생성된 쿠키는 클라이언트가 서버에 요청할 때마다 브라우저가 같이 전송해줍니다.

GET /test HTTP/1.1
Host: localhost:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
DNT: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: ko,en-US;q=0.9,en;q=0.8
Cookie: JSESSIONID=8EB8434C5776358C84017077E11A3300; CookieName1=Example1; CookieName2=Example2

쿠키의 속성

1. Domain

2. Path

3. Expires / Max-Age

4. Secure

5. HttpOnly

쿠키의 종류

세션 쿠키

시큐어 쿠키

Http-only 쿠키

Same-site 쿠키

third-party 쿠키

좀비 쿠키

쿠키의 버전별 차이

쿠키 버전 0, 1의 차이

RFC 6265, RFC 2109의 쿠키 차이.

* This class supports both the RFC 2109 and the RFC 6265 specifications.
* By default, cookies are created using RFC 6265.
/**
* Sets the version of the cookie protocol this cookie complies with.
* Version 0 complies with the original Netscape cookie specification.
* Version 1 complies with RFC 2109.
* <p>
* Since RFC 2109 is still somewhat new, consider version 1 as experimental;
* do not use it yet on production sites.
*
* @param v
*            0 if the cookie should comply with the original Netscape
*            specification; 1 if the cookie should comply with RFC 2109
* @see #getVersion
*/
public void setVersion(int v) {
        version = v;
    }
private final String name;
private String value;

private int version = 0; // ;Version=1 ... means RFC 2109 style

//
// Attributes encoded in the header's cookie fields.
//
private String comment; // ;Comment=VALUE ... describes cookie's use
private String domain; // ;Domain=VALUE ... domain that sees cookie
private int maxAge = -1; // ;Max-Age=VALUE ... cookies auto-expire
private String path; // ;Path=VALUE ... URLs that see the cookie
private boolean secure; // ;Secure ... e.g. use SSL
private boolean httpOnly; // Not in cookie specs, but supported by browsers

톰캣 6.0~8.5 버전별 쿠키 이슈.

톰캣 6.0

6.0 쿠키 버그 리스트

1. DIGEST 인증이 6.0.x Manager App에서 WWW-Authenticate 헤더 중복문제로 깨지는 경우


* 해결 방법     
 1. 6.0.36으로 업데이트합니다.
 2. 401.jsp에서 해당 라인을 삭제합니다. (상황에 따라 다를 수 있으므로 확인이 필요합니다.)
 3. 만약 헤더가 설정돼있는지 확인하고 없는 경우에만 추가합니다.
        
### 2. [쿠키 값을 ""로 묶을 때 헤더 값이 손상되는 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=57896)
* 재현 방법
	1. 스크린샷같이 쿠키를 생성합니다. 
		![8.png](https://images.gogumang.com/1929f0c76a/08.png)
	2. 페이지를 재방문하면 헤더 값이 손상됩니다.
		![9.png](https://images.gogumang.com/1929f0c76a/09.png)
* 해결 방법
	1. 6.0.45로 업데이트합니다.

## 톰캣 7.0

[7.0 전체 버그 리스트](https://bz.apache.org/bugzilla/buglist.cgi?bug_status=__all__&content=cookie&no_redirect=1&order=bug_id&product=Tomcat%207&query_format=specific)

### 1. [의도하지 않게 JSESSIONID가 수정되는 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=60854)
* 재현 방법
	1. 사용자가 요청을 보내서 유저 인증과 동시에 세션을 만듭니다.
	2. JSESSIONID 쿠키가 반환됩니다.
	3. 클라이언트가 쿠키를 2번째 요청과 같이 보내면 새로운 JSESSIONID 쿠키가 생성됩니다.
* 해결 방법
	1. alwaysUseSession="true"로 설정합니다. (사용자가 옵션으로 선택 가능하게 수정하지 않았음)
        
### 2. [톰캣 시작시 ClusterSingleSignOn valve의 SingleSignOnEntry 캐시가 동기화되지 않는 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=57338)
* 해결 방법
   1. 7.0.62로 업데이트합니다.

## 톰캣 8.0 ~ 8.5

[8.0 ~ 8.5 전체 버그 리스트](https://bz.apache.org/bugzilla/buglist.cgi?bug_status=__all__&content=cookie&no_redirect=1&order=bug_id&product=Tomcat%208&query_format=specific)

### 1. Domain 속성이 .으로 시작하면 에러가 발생하는 현상
* 재현방법  
	1. domain의 속성을 .으로 시작하게 쿠키를 생성합니다.
        ![10.png](https://images.gogumang.com/1929f0c76a/10.png)
* 해결 방법
	1. Domain 앞에 .을 제거합니다.
    2. 기존 LegacyCookieProcessor를 사용하도록 context.xml 수정합니다.
        
### 2. [Request.parseCookies\(\)에서 NullPointerException이 발생하는 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=58578)
*  해결 방법
	1. 8.0.29로 업데이트합니다.
        
### 3. [Rfc6265CookieProcessor에서 유효한 도메인 문자가 불완전한 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=58445)
* 해결 방법
   1. 8.0.27로 업데이트합니다.
        
### 4. [잘못된 쿠키가 들어온 경우 Rfc6265CookieProcessor가 모든 쿠키를 무시하는 현상](https://bz.apache.org/bugzilla/show_bug.cgi?id=60627)
* 해결 방법
   1. 8.5.12로 업데이트합니다.
   2. version이 0일 때도 RFC6265로 파싱할 수 있도록 합니다.
        
### 5. [Set-Cookie 헤더가 RFC 스펙과 다름](https://bz.apache.org/bugzilla/show_bug.cgi?id=60876)
* 해결 방법
	1. 8.5.13으로 업데이트합니다.

## 모든 버전에서 발생

### 1. 쿠키에 한글을 저장할 때 에러가 나는 현상 
![11.png](https://images.gogumang.com/1929f0c76a/11.png)
* 해결 방법
	1. URLEncoder.encode를 사용하여 저장합니다.
       ![12.png](https://images.gogumang.com/1929f0c76a/12.png)
        2. Tomcat 버전 8.5는 자동으로 처리됩니다.
       ![13.png](https://images.gogumang.com/1929f0c76a/13.png)
       
### 2. 쿠키값에 =가 들어 있으면 =뒤의 문자가 잘리는 현상
  ![14.png](https://images.gogumang.com/1929f0c76a/14.png)
   * 재현 방법
        1. 클라이언트 쪽에서 `=`가 들어간 값을 `""`로 감싸지 않고 보낼 경우 발생합니다.
        2. `org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true`로 설정이 돼 있어서 `=`가 들어간 쿠키 값을 `""`로 감싸지 않을 경우 발생합니다.
	* 해결 방법
        1. catalina.properties에 `org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE=true`를 추가합니다.
        ![15.png](https://images.gogumang.com/1929f0c76a/15.png)
        2. URLEncoder.encode를 사용하여 저장합니다. (값을 읽을 때 Decoding 필요합니다.)
       ![16.png](https://images.gogumang.com/1929f0c76a/16.png)
        3. value에 =가 들어가는 값은 원칙대로면 유효하지 않은 값이라 ""안에 있어야 합니다.
        
### 3. 쿠키값에 @가 들어 있으면 @뒤의 문자가 잘리는 현상
  ![17.png](https://images.gogumang.com/1929f0c76a/17.png)
  
* 재현 방법
  1. 클라이언트 쪽에서 `@`가 들어간 값을 ""로 감싸지 않고 보낼 경우 발생합니다.
  2. `org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true` 로 설정이 돼 있어서 `@`가 들어간 쿠키 값을 ""로 감싸지 않을 경우 발생합니다.

* 해결 방법
   1. catalina.properties에 `org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true`를 추가합니다.
        * 버전 8.5 
         ![18.png](https://images.gogumang.com/1929f0c76a/18.png)
        * 버전 7
         ![19.png](https://images.gogumang.com/1929f0c76a/19.png)
        * 버전 6 (<span style="color:#e11d21">해결 불가능</span>) 설정값이 버전7 부터 존재합니다.
   2. URLEncoder.encode를 사용하여 저장합니다. (값을 읽을 때 Decoding이 필요합니다.)
        ![20.png](https://images.gogumang.com/1929f0c76a/20.png)
   3. value에 `@`가 들어가는 값은 원칙대로면 유효하지 않은 값이라 ""안에 있어야 합니다.
      
### 4. 쿠키 Value에 +가 들어있을 때 " "(공백)으로 바뀌는 현상
* 재현 방법
	1. 쿠키를 전송할 때 Content-type 이 applicatoin/x-www-form-urlencoded 일 때 발생합니다.
* 해결 방법
	1. Url-safe base64 encode를 사용합니다.  (Encoding을 하면 `+`와 `/`가 `-`랑`_`로 변경됩니다.)

```java
        /**
         * This array is a lookup table that translates 6-bit positive integer
         * index values into their "Base64 Alphabet" equivalents as specified
         * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
         */
        private static final char[] toBase64 = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
        };

        /**
         * It's the lookup table for "URL and Filename safe Base64" as specified
         * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
         * '_'. This table is used when BASE64_URL is specified.
         */
        private static final char[] toBase64URL = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
        };

5. 쿠키 이름에 괄호가 들어가는 경우 에러가 발생하는 현상

21.png

출처

[1]https://en.wikipedia.org/wiki/HTTP_cookie#History [2]http://web.archive.org/web/20020803110822/http://wp.netscape.com/newsref/std/cookie_spec.html [3]https://tools.ietf.org/html/rfc2109 [4]https://tools.ietf.org/html/rfc6265