반응형
반응형

사실 SHA256이나 MD5 같은 단방향 암호화를 전송하는측에서 왜 해야 하는지 의문이 들기도 하지만,
모든 개발이 생각하는대로 흘러가지 않는만큼 여러가지(?) 이유로 전송하는 문자열을 암호화 해야 하는 이슈가 발생하였다.

암호화를 SHA-256으로 하기로 협업과 약속하고 자바스크립트로 SHA256 암호화 할 수 있는 방법으로 구글링 해 보니, 쉽게 찾을 수 있었다. 이미 외국의 개발자분이 자바스크립트로 구성된 함수를 제작하여 공개하셨다.

함수는 아래와 같으며, 원 출처지인 http://www.webtoolkit.info/javascript_sha256.html#.VuJRJuY4tFC 을 통해서도 확인이 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<script type="text/javascript">
    /**
    *
    *  Secure Hash Algorithm (SHA256)
    *  http://www.webtoolkit.info/
    *
    *  Original code by Angel Marin, Paul Johnston.
    *
    **/
      
    function SHA256(s){
      
        var chrsz   = 8;
        var hexcase = 0;
      
        function safe_add (x, y) {
            var lsw = (x & 0xFFFF+ (y & 0xFFFF);
            var msw = (x >> 16+ (y >> 16+ (lsw >> 16);
            return (msw << 16| (lsw & 0xFFFF);
        }
      
        function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
        function R (X, n) { return ( X >>> n ); }
        function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
        function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
        function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
        function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
        function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
        function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
      
        function core_sha256 (m, l) {
             
            var K = new Array(0x428A2F980x713744910xB5C0FBCF0xE9B5DBA50x3956C25B0x59F111F1,
                0x923F82A40xAB1C5ED50xD807AA980x12835B010x243185BE0x550C7DC3,
                0x72BE5D740x80DEB1FE0x9BDC06A70xC19BF1740xE49B69C10xEFBE4786,
                0xFC19DC60x240CA1CC0x2DE92C6F0x4A7484AA0x5CB0A9DC0x76F988DA,
                0x983E51520xA831C66D0xB00327C80xBF597FC70xC6E00BF30xD5A79147,
                0x6CA63510x142929670x27B70A850x2E1B21380x4D2C6DFC0x53380D13,
                0x650A73540x766A0ABB0x81C2C92E0x92722C850xA2BFE8A10xA81A664B,
                0xC24B8B700xC76C51A30xD192E8190xD69906240xF40E35850x106AA070,
                0x19A4C1160x1E376C080x2748774C0x34B0BCB50x391C0CB30x4ED8AA4A,
                0x5B9CCA4F0x682E6FF30x748F82EE0x78A5636F0x84C878140x8CC70208,
                0x90BEFFFA0xA4506CEB0xBEF9A3F70xC67178F2);
 
            var HASH = new Array(0x6A09E6670xBB67AE850x3C6EF3720xA54FF53A0x510E527F
0x9B05688C0x1F83D9AB0x5BE0CD19);
 
            var W = new Array(64);
            var a, b, c, d, e, f, g, h, i, j;
            var T1, T2;
      
            m[l >> 5|= 0x80 << (24 - l % 32);
            m[((l + 64 >> 9<< 4+ 15= l;
      
            for ( var i = 0; i<m.length; i+=16 ) {
                a = HASH[0];
                b = HASH[1];
                c = HASH[2];
                d = HASH[3];
                e = HASH[4];
                f = HASH[5];
                g = HASH[6];
                h = HASH[7];
      
                for ( var j = 0; j<64; j++) {
                    if (j < 16) W[j] = m[j + i];
                    else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
      
                    T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
                    T2 = safe_add(Sigma0256(a), Maj(a, b, c));
      
                    h = g;
                    g = f;
                    f = e;
                    e = safe_add(d, T1);
                    d = c;
                    c = b;
                    b = a;
                    a = safe_add(T1, T2);
                }
      
                HASH[0= safe_add(a, HASH[0]);
                HASH[1= safe_add(b, HASH[1]);
                HASH[2= safe_add(c, HASH[2]);
                HASH[3= safe_add(d, HASH[3]);
                HASH[4= safe_add(e, HASH[4]);
                HASH[5= safe_add(f, HASH[5]);
                HASH[6= safe_add(g, HASH[6]);
                HASH[7= safe_add(h, HASH[7]);
            }
            return HASH;
        }
      
        function str2binb (str) {
            var bin = Array();
            var mask = (1 << chrsz) - 1;
            for(var i = 0; i < str.length * chrsz; i += chrsz) {
                bin[i>>5|= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
            }
            return bin;
        }
      
        function Utf8Encode(string) {
            string = string.replace(/\r\n/g,"\n");
            var utftext = "";
      
            for (var n = 0; n < string.length; n++) {
      
                var c = string.charCodeAt(n);
      
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if((c > 127&& (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6| 192);
                    utftext += String.fromCharCode((c & 63| 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12| 224);
                    utftext += String.fromCharCode(((c >> 6& 63| 128);
                    utftext += String.fromCharCode((c & 63| 128);
                }
      
            }
      
            return utftext;
        }
      
        function binb2hex (binarray) {
            var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
            var str = "";
            for(var i = 0; i < binarray.length * 4; i++) {
                str += hex_tab.charAt((binarray[i>>2>> ((3 - i%4)*8+4)) & 0xF+
                hex_tab.charAt((binarray[i>>2>> ((3 - i%4)*8  )) & 0xF);
            }
            return str;
        }
      
        s = Utf8Encode(s);
        return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
      
    }
     
     
     
     
 
 
    // 암호화 확인
    console.log(SHA256("Test")) ;
 
</script>
 
cs

출처: http://fruitdev.tistory.com/191 [과일가게 개발자]




레퍼런스 문서

http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf

각 block 내용 및 W[64]의 내용들이 자세하게 나와있음

http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf

위와 비슷한 정보 

http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf



sha256은 hash 함수의 일종이다

hash 함수라는게 뭐냐면 다 알고 있겠지만 간단히 설명해보겠다


y = x + 2 이런 함수는 y를 알면 x도 알 수 있다

하지만 y = hash(x) 이런 해쉬 함수는 y를 알아도 x를 알 수 없는게 특징이다

그리고 모든 x값에 대해 각기 다른 y값을 내뱉는 특징 때문에 x가 원본임을 증명하는 일종의 시그니쳐로도 사용할 수 있다

(정확하게 말하자면 hash 함수는 단사함수가 아니다. hash collision이 있어서 다른 x임에도 같은 y값이 나오는 경우도 있다.

 hash collision을 피하기 위해선 계산을 더 복잡하게 하면 된다. 적당한 기회 비용을 두고 밀당을 해야한다는 소리)


hash 결과값은 32bit 8개를 병렬로 늘어놓은 값 

32bit x 8 = 256bit 그래서 이름이 sha256이다


원본 메세지의 각 문자(ascii 8bit)를 적당히 잘라 붙이고 쉬프트하고 and, or 등등 해서 512bit 단위의 블럭으로 만들고 이를 볶고 지져 hash를 계산한다

512bit 블럭으로 만들기 위해 크면 자르고(parsing) 모자라면 붙인다(padding)


아래의 공식에 맞춰 hash 계산에 필요한 블럭을 만든다

L + 1 + k = 448mod512


L(data length)는 hash를 만들고 싶은 원본 message의 bit길이 이므로 무조건 8의 배수이다

구현할 때 447bit 이런거 머리 빠지게 고민할 필요 없다


그리고 중요한 정보 - little endian을 기준으로 동작하도록 설계되어있다

x86, arm을 보통 사용하는 우리는 짜증나는 endian 전환을 해야 한다



실제 구현 관련 정리 - 블럭 제작 방법 및 hash 계산 방법

hash 계산 및 업데이트

간단하므로 생략, fips 문서에 나온데로 4개의 절차를 수행하면 된다(복잡해 보이는 시그마 같은거 다 문서에 잘 정의 되어있다)


메세지 길이 = len

case 1. len < 56(448bit)

마지막에 1bit를 추가하고 448bit까지 0으로 채움

그리고 마지막 64bit는 총 메세지 길이(lenx8)를 기록한다

hash 계산, 업데이트


case 2. 56 <= len < 64

마지막에 1bit를 추가하고 512bit까지 0으로 채워 block을 만든다

그리고 hash를 계산, 업데이트 한다

그리고 448bit까지 모두 0으로 채우고 마지막 64bit를 총 메세지 길이(lenx8)를 기록한다

그리고 hash 계산, 업데이트


case 3. len >= 64(512bit 이상)

512bit 씩 block으로 잘라 hash 계산, 업데이트 한다

마지막 남는 메세지는 길이에 따라 case1 혹은 case2와 동일하게 진행


last

계산한 32bit 8개의 값을 나열하면 sha256 완성



기타 유용한 참고 소스 코드

kernel 에 포함된 sha....코드.......이해하기 너무 어려움

openssl 의 crypto/sha에 있는 코드들 추천, 다만 최적화 되어있는 부분은 이해하기 힘듬

https://github.com/b-con/crypto-algorithms 매우 추천 깔끔하게 정리 되어있음

반응형
반응형


-XSS 필터



웹 페이지를 만들보면 사용자의 악의적인 공격을 많이 받게되는데



이런 것들중 가장 대표적인것이 XSS 공격이다


크로스 사이트 스크립트 라는것인데 서버로 보내는 입력값에 자바스크립트를 보내서


다른 사용자에게 자신이 만든 스크립트를 실행시켜서 사용자의 정보를 빼내는것이다




지금 당장 자신의 사이트가 XSS 를 방어하고 있는지 확인하는 방법은 간단하다


입력창 아무대서나 <script> alert("script ok"); </script> 라고 입력하고


저장한 후 상세화면으로 들어갔을때 해당 코드가 실행되는지 확인하면 간단하다



이런 문제를 해결하는데에는 많은 방법이 있지만 간단하게 Filter 추가 만으로 해결할 수 있는 방법이 있다


1. CrossScriptingFilter 필터 파일

2. RequestWrapper  필터링을 실행할 파일

3. web.xml 에서 세팅


1. 기본 컨트롤 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
package com.greatwebguy.filter;
 
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
 
 
public class CrossScriptingFilter implements Filter {
 
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
 
    public void destroy() {
        this.filterConfig = null;
    }
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
 
        chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
 
    }
 
}
cs




2. 치환 동작 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.greatwebguy.filter;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
 
public final class RequestWrapper extends HttpServletRequestWrapper {
 
    public RequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }
 
    public String[] getParameterValues(String parameter) {
 
      String[] values = super.getParameterValues(parameter);
      if (values==null)  {
                  return null;
          }
      int count = values.length;
      String[] encodedValues = new String[count];
      for (int i = 0; i < count; i++) {
                 encodedValues[i] = cleanXSS(values[i]);
       }
      return encodedValues;
    }
 
    public String getParameter(String parameter) {
          String value = super.getParameter(parameter);
          if (value == null) {
                 return null;
                  }
          return cleanXSS(value);
    }
 
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return cleanXSS(value);
 
    }
 
    private String cleanXSS(String value) {
                //You'll need to remove the spaces from the html entities below
        value = value.replaceAll("<""& lt;").replaceAll(">""& gt;");
        value = value.replaceAll("\\(""& #40;").replaceAll("\\)""& #41;");
        value = value.replaceAll("'""& #39;");
        value = value.replaceAll("eval\\((.*)\\)""");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("script", "");
        return value;
    }
}
cs



3.(<filter-class> 에는 해당 클래스 파일 경로를 넣어야한다)

1
2
3
4
5
6
7
8
9
 
<filter>
    <filter-name>XSS</filter-name>
    <filter-class>com.greatwebguy.filter.CrossScriptingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>XSS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
cs




-URIEncodin 필터


Tomcat에서 한글 encoding을 설정하는 방법은 요청 메써드 (GET or POST)에 따라 다르다.

get 방식

Tomcat의 sever.xml 을 열고 

1
2
3
4
<Connector port="9090" protocol="HTTP/1.1" 
                 connectionTimeout="20000" 
                redirectPort="8443"
                URIEncoding="utf-8" />
cs


위 URIEncoding 부분을 추가했다.. 그러니 한글이 깨지는 것을 막을 수 있었다.
기본적으로 톰캣은 ISO-8859-1 인코딩 한다.
그래서 한글이 깨졌던거 같다.

post 방식

web.xml 아래와 같이 filter class 를 등록한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <filter>
  <filter-name>Set Character Encoding</filter-name>
  <filter-class>filters.SetCharacterEncodingFilter</filter-class>
  <init-param>
   <param-name>encoding</param-name>
   <param-value>utf-8</param-value>
  </init-param>
 </filter>
 
 <filter-mapping>
  <filter-name>Set Character Encoding</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 
 <filter-mapping>
   <filter-name>encodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
 </filter-mapping>
 
 
cs





출처 사이트:

http://18281818.tistory.com/76 [반이라도 이해하자]

http://kcmuni.tistory.com/entry/한글-깨짐-처리URIEncodingUTF-8 [내일을 항해~]


반응형
반응형
1

+ Recent posts