Prototype의 JSON 구현은 이후 브라우저에 내장되는 것이 유력한 것으로 보이는 더글러스 크록포드(Douglas Crockford)의 작업에 크게 기반한다. 불행하게도 크록포드 스타일의 구현은 Object.prototype
을 확장하는 방식으로 인해 Prototype에서 사용하기에는 적당하지 않다(일단 JSON이 브라우저에 내장되기만 한다면 더 이상 문제될 것이 없다는 점에 주목하자).
인코딩(Encoding)
Prototype의 JSON 인코딩은 클록포드 스타일의 구현이 Object.prototype
을 확장하지 않기 때문에 그 방식과는 조금 다르다. Number#toJSON, String#toJSON, Array#toJSON, Hash#toJSON, Date#toJSON, Object#toJSON가 가용 메소드이다.
어떤 타입의 인코딩을 해야하는지 확신할 수 없다면, 최선책은 다음처럼 Object.toJSON
을 사용하는 것이다.
var data = {name: 'Violet', occupation: 'character', age: 25 }; Object.toJSON(data); //-> '{"name": "Violet", "occupation": "character", "age": 25}'
이외의 경우에 대해서는(data가 객체의 인스턴스가 아니라는 사실을 알고 있다면) 대신에 다음처럼 toJSON
메소드를 불러낼 수 있다.
$H({name: 'Violet', occupation: 'character', age: 25 }).toJSON(); //-> '{"name": "Violet", "occupation": "character", "age": 25}'
더군다나, 사용자 객체를 사용하고 있다면 Object.toJSON
로 연결되는 자체적인 toJSON
메소드를 지정할 수 있다. 다음은 예제이다:
var Person = Class.create(); Person.prototype = { initialize: function(name, age) { this.name = name; this.age = age; }, toJSON: function() { return ('My name is ' + this.name + ' and I am ' + this.age + ' years old.').toJSON(); } }; var john = new Person('John', 49); Object.toJSON(john); //-> '"My name is John and I am 49 years old."'
마지막으로 Element.addMethods
를 사용하여 특정 엘레멘트에 지정된 사용자화 toJSON
메소드를 생성할 수 있다.
<input id="firstname" name="firstname" value="john" />
Element.addMethods('input', { toJSON: function(element) { element = $(element); return element.name.toJSON() + ": " + element.getValue().toJSON(); } }) Object.toJSON($('firstname')); //-> '"firstname": "john"'
Parsing JSON
자바스크립트에서 JSON의 분석은 JSON 문자열을 평가하는(evaluating) 전형적인 방식으로 이뤄진다. Prototype은 이 처리를 위해 String#evalJSON를 도입했다.
var data = '{ "name": "Violet", "occupation": "character" }'.evalJSON(); data.name; //-> "Violet"
String#evalJSON는 션택적 매개변수로 sanitize
를 받는데, 이것이 true
로 설정되면 JSON 문자열에서 악의적인 시도를 체크하고 평가를 중지시킨 뒤, 시도가 발견되면 SyntaxError
를 발생시킨다(throw).
var data = 'grabUserPassword()'.evalJSON(true) //-> SyntaxError: Badly formated JSON string: 'grabUserPassword()'
항상 sanitize
매개변수를 true
로 설정하고 (외부 또는 사용자 자작 컨텐츠 등의) 신뢰할 수 없는 소스에서 전달된 데이터의 XSS 공격을 막기 위해 적절한 content-type 헤더(application/json)를 지정해야 한다.
String#evalJSON은 내부적으로 String#unfilterJSON을 호출하고 자동적으로 보안주석 구획문자 부분(옵션으로 Prototype.JSONFilter
에 선언되어 있음)을 제거한다.
person = '/*-secure-\n{"name": "Violet", "occupation": "character"}\n*/'.evalJSON() person.name; //-> "Violet"
하이재킹(hijacking)을 막기 위해 신중을 요하는 JSON이나 자바스크립트 데이터에는 보안주석 구획을 지정해야 한다(자세한 내용은 링크된 PDF 파일을 보라).
Ajax에서 JSON 사용(Using JSON with Ajax)
Ajax와 함께 JSON을 사용하는 법은 간단하다. 그냥 전달객체의 responseText
속성에 String#evalJSON을 적용하기만 하라.
new Ajax.Request('/some_url', { method:'get', onSuccess: function(transport) { var json = transport.responseText.evalJSON(); } });
신뢰할 수 없는 소스에서 전달된 데이터라면 반드시 안전검사(sanitize)를 하라.
new Ajax.Request('/some_url', { method:'get', requestHeaders: {Accept: 'application/json'}, onSuccess: function(transport){ var json = transport.responseText.evalJSON(true); } });
- XSS 공격
- 클라이언트 스크립트나 tag 등을 통한 공격.