Ajax(Asynchronous Java Script and XML)는 비동기식 JavaScript를 사용하는 웹 애플리케이션 개발 방식으로서 Http 프로토콜을 통해 XML 데이터를 웹 서버와 교환한다. 따라서 웹 페이지는 동적으로 업데이트 되는 기존 웹과 는 달리 응답속도가 떨어지지 않는다. Ajax는 기술이 아니며, 오히려 여러가지 기술이 복합된 방법론 또는 패턴에 더 가깝다. 해당 문서에서는 Ajax에서 나타날수 있는 위험 요소에 대한 보안 대책에 대해서 다루고있다. 또한 Ajax는 기존에 웹과 함께 구현되는 경우가 많으므로 해당 보안가이드 라인에 제시된 내용 외에도 기존 웹에서 발생하는 취약점(OWASP TOP 10)도 고려하여 개발시에 적용되어야 한다.
XSS(Cross-site Scripting)는 해커들이 웹 어플리케이션에 몰래 침투하기 위해 이용하는 가장 일반적인 어플리케이션 계층 공격 중에 하나이다. JavaScript로 구성된 Ajax에게 XSS는 치명적인 공격이 될 수 있다. 일반 사용자에게 악의적인 스크립트를 실행시키는 공격 기법 종류와 발생 지점에 따라서 위험요소를 분류 하였으며 해당 취약점에 대해서는 공통적으로 XSS 취약점에 대한 보안대책이 적용된다.
JavaScript는 객체지향 프로그래밍을 제공하기 때문에 사용자가 원하는 많은 임의의 객체를 만들 수 있다. New object()를 사용하여 새로운 객체를 만들거나 다음과 같은 코드를 통하여 만들수 있다.
message = { from : "john@example.com", to : "jerry@victim.com", subject : "I am fine", body : "Long message here", showsubject : function(){document.write(this.subject)} };
위 코드는 이메일 작성시에 필요한 각 필드를 message 오브젝트로 간단히 작성한 예제이다. 작성된 message 개체를 JavaScript 코드 또는 Ajax를 이용하여 사용된다.
악의적인 사용자는 게시물이나 메일 제목에 악의적인 스크립트를 삽입하여 메일을 열람한 사용자에게 XSS 공격을 수행할 수 있다.
JavaScript 객체는 메소드와 중요 데이터를 모두 소유할 수 있기 때문에 부적절한 JavaScript 객체사용은 악의적인 코드 삽입으로부터 보안 허점이 발생하여 타 사용자가 공격 당할 수 있다.
JavaScript 객체 표기법 (json)은 자료를 주고 받을 때 경량의, 텍스트 기반, 언어로 독립적인 데이터 교환 포맷이다. ECMAScript 언어의 하위 세트에 기반하고 있으며(JavaScript 언어의 일부이다.), 포맷팅 규칙 세트를 정의하여 구조화 된 데이터를 생성한다.
그 형식은 JavaScript의 구문 형식을 따르지만, 프로그래밍 언어나 플랫폼에 독립적으로 C, C++, 자바, JavaScript, 펄, 파이썬 등 많은 언어에서 이용할 수 있다.
Json 은 web 2.0 어플리케이션에서 매우 효과적인 데이터 교환 형식을 제공한다. 개발자는 매우 자주 DOM 에서 필요한 정보를 가져오기위해 Ajax에서 JSON를 사용하는데 다음과 같은 문제점이 존재한다.
아래는 즐겨찾기 객체를 다른이름으로 만든 간단한 코드이다.
{“bookmarks”:[{“Link”:“www.example.com”,“Desc”:“Interesting link”}]}
위 코드중 공격자는 Desc 또는 Link 부분에 악의적인 스크립트를 삽입하여 스크립트를 삽입하여 XSS 공격을 실행할 수 있다.
JS array는 객체 직렬화를 위한 가장 중요한 객체 중 하나이며, 이것은 쉽게 플랫폼을 이용하거나 Cross-language framework에 효과적이다. JS Array를 포이즈닝(Poisoning)하는 것은 DOM 문맥을 망쳐 버릴 수 있으며, JS array에 간단한 스크립트를 삽입해서 사용자에게 XSS 공격을 할수 있다. 아래에 간단한 JS array 예제가 있다.
new Array(“Laptop”, “Thinkpad”, “T60”, “Used”, “900$”, “It is great and I have used it for 2 years <script>alert('xss');</script>”)
위의 배열은 경매사이트에서 중고 노트북거래시에 데이터를 전송하기 위한 배열이다. 만약에 배열 객체가 적절하지 않다면 서버사이드에서 사용자가 마지막 필드에 삽입한 스크립트에 대해서 검증해야 한다. 이 공격기법은 브라우저를 위태롭게 하거나 공격에이전트에 의해서 공격기법으로 사용될수 있다.
Ajax은 다양한 위치에서 XML을 불러온다. 이러한 XML 블록들은 SOAP, REST 또는 XML-RPC등 웹서비스 구동으로 시작된다. 이러한 웹서비스들은 Third Party로 부터 Proxy brideges를 통해 이용된다.
만약 Third Party XML Stream이 공격자에게 조작된다면 공격자는 악성 컨텐츠를 삽입할 수 있다.
브라우저가 가지고 있는 작은 XML parser가 이 스트림을 처리하며, 이 XML parser는 다른 XML Bombs에 취약할 수 있다. 또한 또다른 XSS를 스크립트 삽입도 가능하다. 브라우저에서 적절히 검증되지 않은 XML 사용은 엔드유저에게 악의적인 영향을 미칠수 있다.
Web 2.0 어플리케이션은 그 자체인 처음 인스턴스는 안전할지 모르지만, 이벤트 발생시 야기되는 인젝션 공격에는 취약할 수 있다. 클릭으로 액션을 유발하는 onclick은 JavaScript 인젝션 공격으로 악의적인 링크가 삽입될 수 있는 위험이 있다. 예를 들어, bomb (일명, 악성코드 폭탄) 위에 놓인 브라우저는 종단 사용자로부터 폭발을 일으키는 정상적인 이벤트가 발생하길 기다린다. 링크 혹은 버튼을 클릭함으로써 특정한 이벤트가 발생한다면, 그 익스플로잇은 성공한 것이다. 이것은 악의적인 코드를 통해 세션 하이제킹을 이끌 수 있다. 이런 보안에 취약한 요소는 올바른 검증 없이 신뢰되지 못한 소스로부터 정보의 흐름을 노출 시키게 된다. 이런 보안에 취약한 부분을 악용하기 위해 종단 사용자로부터 이벤트를 발생하게 만드는 것이 필요하다. 이런 이벤트는 아마도 악의가 없는 이벤트처럼 보일지 모르지만, 굉장히 위험할 수 있다.
DOM(Document Object Model)은 객체지향 모델로써 구조화된 문서를 표현하는 형식으로 동적으로 문서의 내용 및 구조, 스타일에 스크립트를 통해 다이나믹하게 접근 변경할 수 있는 수단이다. JavaScript 코드로 DOM을 프로그램적으로 수정하는 것이 API로 가능하도록 되어있어 아래와 같은 JavaScript 객체를 이용할 경우 XSS 공격에 노출될 수 있다.
DOM을 이용하기 위한 클라이언트 자바 스크립트 객체에 대한 검증을 실시하고. Server Side에서 사용자 입력값 중 특정 기능을 가질 수 있는 특수문자를 아래와 같이 필터링을 하여 악성스크립트의 삽입을 제한한다.
다음과 같이 허용 가능한 문자에 대하여 필터링을 한다.
<SCRIPT> Var pos=document.URL.indexOf(“name=”)+5; Document.write(document.URL.substring(pos.document.URL.length)); </SCRIPT>
<SCRIPT> var pos=document.URL.indexOf("name=")+5; var name=document.URL.substring(pos,document.URL.length); if (name.match(/^[a-zA-Z0-9]*$/)) { document.write(name); } else { window.alert("Security error"); } </SCRIPT>
XSS는 공격자들이 웹 어플리케이션에 몰래 침투하기 위해 이용하는 가장 일반적인 공격 중에 하나이다. XSS는 JavaScript를 통해 이루어지는 공격이므로 JavaScript로 구성된 Ajax에게 치명적인 공격이 될 수 있다. Ajax가 비동기 방식으로 웹 페이지를 불러오는 과정에서 악성 스크립트가 삽입된다면 Ajax의 특성상 사용자는 공격 당하고 있다는 사실을 모른 채 공격 코드가 사용자의 시스템에서 실행된다. 또한 Ajax는 한번 신뢰된 사이트는 지속적으로 신뢰를 하게 되기 때문에 방화벽 등을 우회할 수 있다는 것이 가장 큰 문제점이라 할 수 있다.
결국 악의적인 사용자는 Ajax의 XSS 취약점을 가지고 여러 가지 방법으로 악성 스크립트를 삽입하여 임의의 사용자의 세션 및 쿠키, 패스워드, 중요한 개인정보 등을 훔치는 등의 공격을 행할 수 있기 때문에 다음과 같은 대응 방안을 권고한다.
From | To |
---|---|
< | < |
> | > |
( | ( |
) | ) |
# | # |
& | & |
XSS는 대부분의 웹 개발자들이 현재까지 여전히 고통 받는 문제점이라고 이야기한다. 검증 없이 사용자 입력 값을 출력하는 코드의 한 라인 한 라인의 모든 사용자 입력 값을 검증하는 게 힘들기 때문에, 한번 취약한 몇 개의 공격 메소드들을 웹 어플리케이션 사용자에게 적용 된다면 이 공격들은 방어 할 수 없을 정도로 위험할 수 있다.
우리가 이미 알다시피, 완전한 코드는 현실세계에 존재하지 않으며 몇몇 버그들은 방어 없이 검사 구문을 빠져나간다. 특히 코드 검사자가 보안 마인드 없이 검사를 수행한다면, 몇 번의 검사를 반복해서 수행 하더라도 웹 어플리케이션을 XSS 공격에 취약할 것이다. 실제 환경에서 제품에 XSS취약점이 나타난 경우,XSS 공격을 피하는 위험 완화 계획에 대한 해답을 얻기위해서는 다음에 대해 생각해 보아야 한다.
위의 두 관점을 살펴보면 우리는 서버 단에의 방어와 검증을 빠져 나오는 XSS공격을 멈추기 위해 클라이언트 단에서 이를 막아야 한다는 결론이 나온다. 이는 XSS공격이 <script>를 사용하는 javascript에 기본을 두고 있으므로, 지금 이런XSS공격을 막기 위해서 넘겨지는 HTML을 서버 단에서 필터 하는 JavaScript를 사용한다. 이는 공격자 Javascript를 확인하고 사용자와 관리자에게 공격을 알리는 기능을 한다.
지금 당면한 진짜 과제는 어떻게 정상적인 javascript에서 공격자 javascript를 확인하는가 하는 것이다. 우리는 정상적인 javascript 어떤 시그니쳐 같은 것을 적용하여 웹 페이지에 삽입된 악의적인 javascript인지 확인할 수 있고, 다른 javascript를 사용해 공격자의 비정상적인 javascript인지를 확인해 페이지 내용을 필터 할 수 있을 것이다. 다음은 클라이언트 단에서 비정상적인 javascript가 발견되었을 때 비정상 적인 행동을 유발할 수 있는 javascript의 예이다.
위의 예제에서 모든 javascript에 사용되는 랜덤 값을 발생 시키기 위해 rand()함수를 사용했다. 사용자 악성 스크립트가 아래와 같이 보이는 키로그 스크립트에 보내지는 동안 그의 패스워드를 변경하는 것은 경고 될 수 있다. 이런, rand()함수는 다른 악성 script로부터 정상적인 모든 javascript를 서명하기 위해 사용된다.
<? $file = fopen("log.txt","a"); $timestamp = date("D dS M,Y h:i a"); fwrite($file, "$timestamp\n"); fwrite($file, "$attackscript\n--------------------\n"); fclose($file); ?>
악성 스크립트 컨텐츠들은 로그를 남길수 있는데, 후에 사이트 관리자는 이 공격을 분석 할 수 있다. 로그 파일은 다음과 같다.
-------------------- Fri 20th Jul,2007 12:38 am alert(\"evil code\") -------------------- Fri 20th Jul,2007 12:41 am alert(\"more evil code\") --------------------
또한 우리는 사용자가 타고 온 링크에 referral URL 더 중요한 정보를 남길 수 있다. 그래서 우리는 어떻게 공격자가 공격했는지 메일이나 다른 방법으로 알 수 있을 것이며, 사용자 이름을 기록할 수 있고, 도움을 요청하기 위해 연락하거나 또한 그 공격에 대해 더 많은 정보를 얻을 수도 있다.
Ajax 코딩 기법을 사용해서 사용자나 운영자에게 미리 경고하는것이 가능하고, 이 점은 눈치 채지 못하게 하면서 공격자가 어플리케이션에 XSS 취약성을 테스트 하는것을 더 어렵게 만들 것이다.
그러나 이 기법은 큰 과제를 남기는데, 이것은 단지 사고가 일어나서 사용자가 손해를 입은 후에 경고를 한다는 점이다. 이는 웹서버로부터 브라우져에 의해 순차적으로 읽히는 순차적 언어인 JavaScript의 특징때문이다. 반면에 이는 아직 읽지 않은 스크립트에 대해서는 파싱하지 못할 것이다. 그러면 순차적인 언어를 이용하여 페이지가 로딩 되는 동안 기다리게 만들거나 각각에 milliseconds 정도의 대기 시간을 둘 수 있다. 그러나 실행되기 전에 악성 스크립트가 로드된 때를 정확하게 알고 이를 수행하기가 힘들다.
JavaScript가 sleep()함수를 지원한다면 이는 아주 좋은 해결책이 될 것이다. 그러나 JavaScript는sleep함수를 지원하지 않는다.
다른 해결책은 완전한 Ajax 웹 페이지에 XMLHttpRequest 객체를 사용해서 HTML 컨텐츠를 요청하는 것이다. 그리고 사용자가 클릭할 때마다 그 페이지를 검증 JavaScript가 담긴 새로운 링크로 업데이트 하는 것이다. 그러나 이 작업은 많은 부하가 걸릴 것이다.
일반적인 웹 어플리케이션은 클라이언트 측의 쿼리 요청에 대해 서버에서 결과 데이터를 파싱하여 사용자가 보는 HTML에서 결과를 출력해 준다. 하지만 Ajax 어플리케이션은 서버에서 원시 데이터의 결과를 클라이언트에게 돌려주고 클라이언트는 받은 원시 데이터를 분석하여 HTML로 변형시키도록 설계하는 경우가 있다.
성능 측면에서 이런 접근 방법은 상당히 좋다. 데이터 변형 루틴은 시간이 걸리므로, 클라이언트 단에서 이를 처리하면 속도 향상의 결과를 가져올 수 있다. 하지만 잠재적인 보안 문제가 있다. 서버가 원시 결과를 클라이언트에 보내므로 쿼리 명령 로직에 어떠한 형태로든 인젝션 공격을 할 수 있기 때문이다. 수천개의 참/거짓 질의를 하는 대신에, 데이터 자체만을 요청해 받을 수 있다. 대개 뒷단에 저장된 데이터는 한 두개의 요청문만으로 모두 추출할 수 있다. 요청 횟수가 적으므로 IDS와 같은 보안장비에서 탐지될 가능성이 낮아지고 결국 공격 성공 가능성이 높아지게 된다.