1. 개요

공격자는 생각하지도 못한 방법으로 연산을 수행하여 결과를 도출한다. 한 기술을 정리하는 과정에 ![] 연산의 불린값이 생성되는 과정에 의문을 품게 되었다. 브라우저 개발자 도구를 통해 몇가지 테스트해보았다.

[ ] 불린 테스트

그러면 흔히 반대를 의미하는 ! 기호를 쓰면 결과가 달라진다. 예를 들면 true인 경우 false를 출력하고, false 인 경우 true를 출력한다. 이러한 연산을 자바스크립트 내부에서는 == false 를 연산한다. 그러면 ![]true가 출력되어야 한다. 하지만 실제 테스트하면 어떠할까?

그림 2. ![ ] 불린 테스트

결과는 의외의 결과로 false가 출력된다. 왜 그럴까?

2. 동등 연산자와 일치 연산자

동등 연산자는 느슨한 비교 연산자(Loose Equality Operators) 또는 더블 이퀄 연산자(Double Equals Operator)로 불리며 사용하는 방법은 == 연산자를 사용한다. 자바스크립트에서 동등 연산자는 자동 형변환을 제공한다. 다음 그림을 보면 동등 연산자가 자동 형변환을 사용하는 것을 알 수 있다.

그림 3. 동등 연산자를 통해 자동 형변환 테스트

이러한 이유로 "자동 형변환에 의해 []의 형태가 변환되고, 이로 인해 ![]false가 출력되지 않을까?" 가정을 세워보았다. 하지만 자동 형 변환되는 0""false 값을 가지기에 !0!""true을 출력한다.

그림 4. 0과 ""의 반대 불린 연산 수행 결과

[] 오브젝트에는 다른 형변환이 있지 않을까? 다음 그림을 보면 다른 형변환 요소는 존재하지 않는 것을 볼 수 있다.

그림 5. 동등 연산 테이블

실험에 의미는 없지만, 동등 연산자와 다른 개념이 일치 연산자이다. 일치 연산자는 엄격한 비교 연산(Strict Equality Operators) 또는 트리플 이퀄 연산자(Triple Equals Operator)로 불리며, 동등 연산자와 달리 자동 형변환 기능을 사용하지 않고 비교 연산을 수행한다.

그림 6. 일치 연산 테스트

일치 연산 테이블은 다음과 같다.

그림 7. 일치 연산 테이블

3. 조건 연산을 통한 데이터 성질 이해

본론으로 돌아와서 왜 ![]false를 출력하는지 그 이유를 찾아야 한다. 그 이유를 조건문 사용에서 찾을 수 있었다. []false이기 때문에 if([ ]){ source code }; 형태로 사용하게 되면 분명 source code 부분은 동작하지 않아야한다. 하지만 실제 사용해보면 어떻게 될까?

그림 8. [ ]을 조건 구문에 사용한 테스트

조건 구문이 실행되는 것을 볼 수 있다. 그렇다면 조건문에서는 []true로 동작하는 것이 증명된 것이다. 이 또한 다음과 같은 테이블로 표현할 수 있다.

그림 9. 조건 구문을 통한 자료의 성질 테이블

이러한 이유로 ![]false로 출력된다.

4. 결론

왜 그렇게 동작하는지는 찾았지만, 왜 그렇게 만들어졌는지는 잘 모르겠다. 아마도 자바스크립트를 설계하고 개발한 사람만 알고 있지 않을까? 처음 ![]false로 출력되는 물음을 해결하는게 상당히 어려웠지만, 이정도 결론으로 만족한다. 향후에 ECMA-262 6th 문서를 한번 뒤져봐야겠다.

5. 참조 사이트


  1. 사랑은 2016.01.27 19:35

    도움 얻어 갑니다..!

  2. BlogIcon . 2017.07.13 13:55

    배워갑니다

MIT 공대에서 컴퓨터 공학과 프로그래밍 소개에 관한 강의가 유튜브에 올라왔다. 최근에 업로드된 강의가 있음에도 이 강의들을 정리한 이유는 아무래도 한글 자막이 잘 되어 있기 때문이다.

출처 : MIT OpenCourseWare YouTube

교수 : Eric Grimson, John Guttag

제 01강 - 연산이란 - 데이터 타입, 연산자 및 변수 소개

제 02강 - 연산자와 피연산자 - 분기문, 조건문 그리고 반복문

제 03강 - 공통 코드 패턴, 반복 프로그램

제 04강 - 기능을 통한 분해 및 추상화, 재귀 소개

제 05강 - 부동 소수점, 계통적 명세화, 루트 찾기

제 06강 - 이분법, 뉴턴/랩슨, 그리고 리스트 소개

제 07강 - 리스트와 가변성, 딕셔너리, 의사코드, 그리고 효율성 소개

제 08강 - 복잡성 - 로그, 선형, 이차 방정식, 지수 연산 알고리즘

제 09강 - 이진 탐색, 버블 그리고 분류 선택

제 10강 - 분할 정복 방법, 합병 정렬, 예외

제 11강 - 테스트와 디버깅

제 12강 - 디버깅 추가 강의, 배낭 문제, 동적 프로그래밍 소개

제 13강 - 동적 프로그래밍 - Overlapping subproblems, Optimal substructure

제 14강 - 배낭 문제 분석, 객체 지향 프로그래밍 소개

제 15강 - 추상 데이터 타입, 클래스와 메소드

제 16강 - 캡슐화, 상속, 쉐도잉

제 17강 - 연산 모델 - 랜덤워크 시뮬레이션

제 18강 - 시물레이션 결과 제시, Pylab, Plotting

제 19강 - 편향된 랜덤워크, 배포

제 20강 - 몬테카를로(Monte Carlo) 시뮬레이션, 추정 파이

제 21강 - 시뮬레이션 결과 검증, 곡선 적합, 선형 회귀

제 22강 - 일반, 균등 그리고 지수 분포 - 통계의 오류

제 23강 - 주식 시장 시뮬레이션

제 24강 - 과정 개요 - 컴퓨터 과학자들은 무엇을 하나요?

  1. cabin 2016.04.19 15:02

    한글 자막이 않나오는데요?

    • 박문용 2016.04.19 16:20

      유튜브영상 우측 하단의 설정>자막에서 한국어 선택하세요

    • TongGoon 2016.04.19 18:36

      유튜브 영상 우측 하단에 톱니바퀴(설정) 버튼 클릭 후 자막을 클릭하면 한국어로 변경해서 볼 수 있습니다.

  2. gkdn 2016.04.20 01:11

    13강 부터는 한글 자막이 없는 것 같네요

  3. Favicon of http://moneycoach.kr/ BlogIcon 소액결제 현금화 2017.12.06 07:38

    잘보고 갑니다 ~~~

  4. 과객 2018.12.06 20:30

    한글 자막 만들어 주셔서 감사합니다만 그냥 번역기 돌린 수준이예요.
    그냥 끄고 듣는게 집중도 잘 되고 낫네요.
    다른 분들은 이 점 참고하세요.

    • Favicon of https://www.hakawati.co.kr BlogIcon hakawati 2018.12.14 14:01 신고

      제가 만든게 아닙니다~ 만들어져 있던거 공유했을 뿐입니다~

  5. 김태순 2020.06.28 19:26

    좋은 글 올려주신 덕분에 보기 시작했습니다.
    파이썬 교재로 따라하기만 하다보니 어떻게 사고해야 할지 막막했는데 강의 보니까 도움이 많이 되네요.
    감사합니다.

1. 개요

JavaScript Garden은 자바스크립트 언어의 핵심적인 내용을 모아 놓은 웹 문서이다. 이 문서는 자바스크립트 언어 자체를 설명하지 않고, 이 언어를 익히고, 사용하는데 있어 자주 겪는 실수, 미묘한 버그, 성능 이슈, 나쁜 습관 등 자바스크립트 언어의 독특한 특징들을 설명하고 있다.

2. 난독화 관련 내용

JavaScript Garden 내용을 기반으로 자바스크립트 난독화에서 이해하기 어려웠던 언어의 특징들을 잘 설명하고 있어 공부하는데 많은 도움이 되었다.

2.1. 객체 > 객체와 프로퍼티 > 프로퍼티 접근

객체이름 다음에 점을 찍어(Dot Notation) 접근하거나 각괄호를 이용해(Square Bracket Notation, []) 접근할 수 있다. 두 방식 모두 거의 동일하게 동작한다. 다만 차이가 있다면 각괄호 방식은 프로퍼티 이름을 동적으로 할당해서 값에 접근 할수 있지만 점을 이용한 방식은 구문 오류를 발생시킨다.

var foo = {name: 'kitten'}
foo.name; // kitten
foo['name']; // kitten

var get = 'name';
foo[get]; // kitten

foo.1234; // SyntaxError
foo['1234']; // works

document.write()함수는 document["write"] 가 가능하고 다시 window["document"]["write"] 가 가능하다. 각괄호 안의 값은 문자열이기 때문에 인코딩 형태로 변형할 수 있다.

그림 1. 16진수 난독화

2.2. 함수 > 함수 선언과 함수 표현식 > 표현식

자바스크립트에서 함수는 First Class Object다. 즉, 함수 자체가 또 다른 함수의 인자가 될 수 있다는 말이다.

var foo = function() {}

그래서 기존에 정의된 함수를 다른 변수로 대체 가능하다. 예를 들면 난독화에서 함수명을 쉽게 읽히지 않도록 하기위해 djEfUz = eval 형태로 변경 가능하다. 다음 소스코드는 공다팩의 일부이다.

그림 2. 공다팩 소스코드 중

2.3. 함수 > 스코프와 네임스페이스

자바스크립트는 '{ }' Block이 배배 꼬여 있어도 문법적으로는 잘 처리하지만, Block Scope은 지원하지 않는다. 그래서 JavaScript에서는 항상 함수 스코프를 사용한다. 난독화에서는 이러한 특징을 이용하여 자바스크립트 압축을 구현할 수 있다.

자바스크립트 컴프레셔는 자바스크립트 압축기술로 YUI Compressor 같은 도구들이 존재한다. 압축기를 사용하는 이유는 다운로드 받는 자바스크립트 파일의 용량을 줄이면, 사용자 입장에서는 빠르게 받고 실행할 수 있어 브라우징 속도가 빨라지고, 서버 입장에서는 트래픽 소비량을 줄여 비용을 절감할 수 있다.

var is = {
    ie:      navigator.appName == 'Microsoft Internet Explorer',
    java:    navigator.javaEnabled(),
    ns:      navigator.appName == 'Netscape',
    ua:      navigator.userAgent.toLowerCase(),
    version: parseFloat(navigator.appVersion.substr(21)) ||
             parseFloat(navigator.appVersion),
    win:     navigator.platform == 'Win32'
}
is.mac = is.ua.indexOf('mac') >= 0;
if (is.ua.indexOf('opera') >= 0) {
    is.ie = is.ns = false;
    is.opera = true;
}
if (is.ua.indexOf('gecko') >= 0) {
    is.ie = is.ns = false;
    is.gecko = true;
}

이 코드를 압축시키면 대부분 이런형태로 생성된다.

var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloatnavigator.appVersion),win:navigator.platform=='Win32'}is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;}if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}

2.4. 타입 > 객체 비교하기 > 타입 캐스팅

자바스크립트는 Weak Typing 언어이기 때문에 필요할 때마다 알아서 타입을 변환한다.

Strong / Weak Typing 은 타입에 제약이 많을 수록 Strong, 적어질 수록 Weak에 가까워진다. 예를 들면 var a = 3 으로 했을 때 정수형으로 자동 형 변환을 해주는 형태가 Weak Type, 정확하게 int a = 3 으로 선언해야 정수형으로 저장되는 경우가 Strong Type 으로 이해하면 된다. 자바스크립트는 Weak Typing을 따르기 때문에 자동 형 변환을 해주며, 이런 변환을 타입 캐스팅이라 부른다.

그래서 [] == 0 비교를 하면 true로 출력되는데, 이 때 [] 객체가 0과 비교에서 숫자 타입으로 타입 캐스팅된다. 이중 등호 연산자(==)는 두 객체의 자료형을 강제로 변환하기 때문에 비교가 가능하고, []이 변환되었을 경우 0을 가지기 때문에 true가 출력된다.

이와 같은 맥락으로 ~ 연산이나 + 연산 같이 숫자를 대상으로 연산이 포함될 경우 타입 캐스팅에 의해 강제 형 변환되어 연산이 진행되며 jjencode에서 사용된 ~[]-1이라는 값을 가지게 되는 이유이다.

그림 3. jjencode

그림 4. [ ] 오브젝트와 타입 캐스팅 테스트

2.5. 핵심 > 왜 eval을 사용하면 안 될까?

eval() 함수는 자바스크립트 문자열을 지역 스코프에서 실행한다.

var number = 1;
function test() {
    var number = 2;
    eval('number = 3');
    return number;
}
test(); // 3
number; // 1

eval() 함수는 eval() 이라는 이름으로 직접 실행할 때만 지역 스포크에서 실행된다.

var number = 1;
function test() {
    var number = 2;
    var copyOfEval = eval;
    copyOfEval('number = 3');
    return number;
}
test(); // 2
number; // 3

어쨌든 eval()은 사용하지 말아야 한다. eval()을 사용하는 99.9%는 사실 eval() 없이도 만들수있다. eval()은 어떤 코드라도 무조건 실행하기 때문에 보안 문제도 있다. 따라서 신뢰하지 못하거나 모르는 코드가 포함되어 있을 경우 절대로 사용해서는 안된다. 어쨋든 eval()은 사용하지 않는게 좋다. eval()을 사용하는 모든 코드는 성능, 보안,버그 문제를 일으킬 수 있다. 만약 eval()이 필요해지면 설계를 변경해서라도 eval()이 필요 없게 만들어야 한다.

전역과 지역 스코프에서 사용될 때 보안적 이슈와 차이점에 대해 자세한 설명이 없어 이해하기가 어렵다. 하지만 자바스크립트 난독화에서 흔히 사용하는 함수가 eval() 인것은 확실하고, eval() 함수를 사용했을 경우 안의 내용을 무조건적으로 실행하기 때문에 많은 문제를 가지고 있는 것은 확실하다.

2.6. 핵심 > undefined와 null > undefined도 변수

undefinedundefined라는 값을 가지는 데이터 형식이다. undefined는 상수도 아니고 자바스크립트의 키워드도 아니다. 그냥 undefined라는 이름의 Global 변수이고 이 변수에는 undefined라고 할당돼 있다. 그래서 이 Global 변수의 값을 쉽게 바꿀 수 있다.

같은 맥락으로 0/0으로 출력되는 NaN이나 123/0으로 출력되는 Infinity도 동일하다. 이렇게 출력되는 값을 다시 재활용 가능한데, 이러한 형태를 jjencode에서도 볼 수 있다.

그림 6. ECMAScript 6th 내용

타입 캐스팅에 의존하지 않고 정확한 형을 지정하기 위해 사용한 방법 중에 +"" 또는 ""+ 가 있다. (더블 쿼터 대신 싱글 쿼터를 써도 무방하다.) + 연산자 옆에 쓰여질 피연산자를 문자열로 자동 형 변환을 시켜준다. 이렇게 형 변환된 데이터를 ()로 감싼다. 괄호로 감싸는 이유는 연산자 우선순위를 두기 위해서이다.

그림 7. 선언된 글로벌 변수 변형

2.7. 보이지 않게 사용되는 eval 함수

setTimeout() 함수와 setInterval() 첫 파라미터는 실행가능한 문자열을 넘길 수 있다. 하지만 이 실행은 내부적으로 eval()을 사용하는 것이기 때문에 두 함수를 사용하지 않는 것이 좋다.

그림 8. setTimeout의 첫 파라미터 테스트

3. 참조 사이트

오픈소스 웹킷 - http://www.webkit.org/building/build.html

구글 V8 자바스크립트 엔진 - https://code.google.com/p/v8/

팬텀js within Headless Webkit - http://phantomjs.org/

캐스퍼js - navigation scripting & testing utility - http://casperjs.org/

슬리머js - A scriptable browser for Web developers - http://slimerjs.org/

1. 개요


개발자들의 암묵적인 사항으로 자바스크립트 파일은 *.js확장자를 가지도록 하며, 실제로 운영체제도 *.js는 자바스크립트 파일로 인식하고 있다. 하지만 자바스크립트 엔진을 가지고 자바스크립트 언어 및 파일을 인지하고 인식하는 브라우저는 어떨까? 자바스크립트 언어의 특징 중 하나는 파일 확장자와 무관하게 동작한다는 점이다.


그림 1. 악성코드 유포지가 삽입된 경유지


그림 1에서 보는 것과 같이 script 태그로 자바스크립트를 선언한 후 ***/top.jpg를 리디렉션 하고 있다. 확장자만 보았을 땐 jpg 그림파일이어야 하지만 script로 인식하여 파일 안의 자바스크립트 코드를 실행하게 된다. 이러한 이유는 script 태그를 사용하면 자바스크립트 엔진이 운영되고 엔진은 확장자 확인 없이 파일 안의 소스코드를 읽고 자바스크립트로 해석하기 때문이다. HTML 랜더링 엔진은 script 태그만 인지할 뿐 그 후의 동작은 자바스크립트 엔진에 의해 해석되고 운영된다.


2. 실험


한 가지 의문이 생겼다. 자바스크립트 엔진은 확장자와 무관하게 파일 안의 소스코드를 읽어 들인다면 HTML 랜더링 엔진 또한 그럴까? 자바스크립트 엔진과 HTML 랜더링 엔진은 브라우저별로 다르기 때문에 두 가지 브라우저를 통해 간단하게 테스트를 해 보았다.


2.1. 준비물


IE 10, Chrome, 그림.jpg, 텍스트.txt, test.html


그림 2. 테스트 할 그림.jpg


그림 3. 테스트 할 텍스트.txt


그림 4. 테스트 할 test.html


간단하게 html문서를 메모장으로 작성하고 브라우저별로 로드하여 어떻게 표현하는가를 테스트해보았다.


2.2. 테스트 1


정상적인 jpg 파일 랜더링, txt 파일 렌더링


그림 5. 그림.jpg 랜더링


그림 6. 텍스트.txt 랜더링


예상한 결과 그대로 보여진다. 그림을 그림으로 인식하여 랜더링 하며, 텍스트는 텍스트대로 랜더링하여 보여준다. 보여주는 방식(테두리, 그림크기, iframe의 default값)은 브라우저별로 조금 차이가 있어 보인다.


2.3. 테스트 2


그림.jpg를 강제로 그림.txt로 변환, 텍스트.txt를 강제로 텍스트.jpg로 변환


그림 7. 그림.txt 랜더링


그림 8. 텍스트.jpg 랜더링


3. 결론


재미있는 결과가 나왔다. 그림.jpg를 강제로 txt파일로 만들어 랜더링하면 Chrome은 그림으로 인식하여 보여주며 IE10은 텍스트로 인식하여 보여준다. 즉, Chrome은 파일 구조를 보고 판단하며, IE는 파일의 확장자를 보고 판단을 한다고 추측할 수 있다.


하지만 텍스트.txt를 텍스트.jpg로 변경하여 랜더링 하였을 땐, 둘다 엑박(x 박스)로 표현된다. 앞서 추측한 Chrome은 파일구조를 보고 판단한다는 추측이 틀리게 된고 IE는 확실히 확장자를 보고 판단하는 것으로 확인 되었다.


최신버전에서 blink엔진을 사용하고 있는 chrome의 동작 방식을 추측해보면 다음과 같다.


파일 구조 -> 확장자 -> 랜딩


먼저 파일 구조를 살펴본 후 파일 구조에 맞게 랜딩하게 되고, 파일구조를 파악 할 수 없다면 확장자를 확인하여 랜딩하는 구조로 추측할 수 있다.

브라우저 내부에는 자바스크립트 엔진이라는 특수한 소프트웨어을 포함하고 있으며 이 엔진은 자바스크립트 코드를 인터프리터 형태로 실행할 수 있도록 해준다. 그래서 일반적인 언어는 컴파일러가 필요하지만, 자바스크립트 언어의 경우 브라우저가 직접 파싱하고 직접 해석하게 된다.


페이지가 로딩 후 자바스크립트 코드는 웹 브라우저와 함께 작동하기 때문에, 실시간으로 사용자의 이벤트에 응답하고, 페이지를 수정하기도 한다. 사용자와 대화하는 자바스크립트 언어의 특징으로 인해 좀더 빠르게 사용자와 소통하기 위해 자바스크립트 언어는 클라이언트에 임시 저장을 하게 된다. 그래서 클라이언트용 언어라고 알려져 있으며, 사용자는 쉽게 소스코드를 볼 수 있다.


자바스크립트 코드는 웹 페이지에 소스코드를 그대로 사용하지 않아도 되며, 외부 파일 형태로 소스코드를 작성한 후 로드한 페이지에서 사용할 수 있다. 마치 해당 파일을 임포트(import)한 형태이다. 임포트된 자바스크립트 파일은 브라우저로 인해 페이지가 로드 될 때 사용된다. 이렇게 사용하는 이유는 재사용 가능한 코드를 파일로 만듦으로써 재사용하고 관리하기 쉽기 때문이다. 모든 페이지에서 공통으로 사용하는 모듈 형태의 코드를 자바스크립트 파일로 만들어 재사용하며, 공격자는 이런 자바스크립트 파일을 공략함으로써 악성 스크립트 한 줄로 모든 페이지에서 악성코드를 유포하는 형태로 만들 수 있다.


또한 CDN(Contents Delivery Network) 서비스를 통해 대중적으로 사용하는 자바스크립트 서비스를 제공하기도 한다. 그래서 공격자가 CDN의 자바스크립트 파일을 변조하여 악성코드를 유포했다면, 수많은 사이트에서 악성코드를 유포하게 된다.


자바스크립트는 실시간 사용자와 대화하는 언어이다. 사용자와의 대화는 브라우저를 통해 이루어지며, 사용자의 반응(당연히 브라우저 밖의 반응은 인식하지 못한다.)을 이벤트라 한다. 브라우저에서 이벤트가 발생 시 브라우저는 발생한 이벤트를 수집하고 수집한 정보를 토대로 관련있는 함수로 전달한다. 이 함수는 이미 자바스크립트 언어에서 이벤트에 대한 응답을 하기 위해 설계된 함수이다.


만약 onclick() 이라는 함수를 사용했다면, 사용자의 클릭에 해당하는 이벤트를 브라우저가 수집하고 적절히 설계된 함수를 통해 응답하게 된다.


자바스크립트에 의해 규정된 몇 가지 법칙이 있다. 그 중 하나가 식별자의 구성 방법이다.


  • 식별자는 최소한 하나의 문자로 구성되어야 하며, 구성하는 첫 번째 글자는 문자, _, $로 시작된다.
  • 첫 글자 다음에 오는 각 글자는 문자, _, $, 숫자이어야 하며, 특수문자와 공백은 식별자로 구성할 수가 없다.

이러한 구성 방법으로 _와 $는 독립적으로 변수를 선언할 수 있으며, 이렇게 만들어진 난독화가 Non alphanumaric 형태 중에서 jjencode 같은 형태이다.


자바스크립트는 클라이언트 환경에 접근 할 수 있다. 방문했던 웹 페이지에 대한 기록이나 브라우저늬 크기를 알아낼 수 있고, 브라우저의 버전, 제작 회사, 쿠키값을 통한 제한적인 기억 등을 할 수 있다. 또한 시간을 설정하여 특정 시간이 지난뒤 자바스크립트를 실행 할 수 있다. 이러한 이유로 자바스크립트는 사용자의 정보를 수집가능하며, 이러한 기능을 서비스화 한 것이 애널리틱스(Analytics)이다. 하지만 이 정보들은 충분히 변조할 수 있다는 점을 기억하자.


document.writeln("<script>");
document.writeln("var referer=document.referrer;");
document.writeln("if (referer.indexOf(\"baidu\") >0 || referer.indexOf(\"google\") >0)");
document.writeln("location.replace(\"http:\/\/www.naver.com\");");
document.writeln("<\/script>")

리퍼러에서 baidu나 google이 있으면 현재 페이지를 naver로 바꾸어 주는 스크립트입니다. 구글이나 바이두의 검색을 통해 유포지로 흘러들어온다면 다른페이지로 리디렉션 시킬 수 있지 않을까? 생각해봅니다.

+ Recent posts