Day 39
웹 보안
- 웹 보안이란 웹 사이트의 취약점을 공격하는 기술적 위협으로, 웹 페이지를 통하여 권한이 없는 시스템에 접근하거나 데이터 유출 및 파괴와 같은 행위를 말한다.
공격 기법들
SQL Injection
정의
- 서버에서 실행되는 SQL을 악의적으로 이용하는 공격
- 기존 SQL에 악의적인 SQL을 삽입한다.
- 데이터 탈취, 삭제 등이 가능하다.
방어
- SQL에서 특별한 의미를 가지는 문자를 이스케이프 한다.
- 준비된 선언을 사용한다.
- placeholder를 담은 SQL을 먼저 DB에 보낸 후 placeholder에 해당하는 입력값을 DB에 보내는 방식
- 요즘은 라이브러리, 프레임워크에서 아주 잘 막아준다.
심화 공격기법
Error based SQL Injection
- 옛날 웹사이트는 발생한 에러를 웹사이트에 그대로 노출하는 경우가 많았다.
- 그래서 일부러 SQL 에러를 발생시켜 원하는 정보(쿼리문 추측, DB명, 테이블명) 등을 취득할 수 있다.
- 특정 테이블에 대한 공격을 할 수 있다.
Blind SQL Injection
- DB명, table명, 속성명 추출에 특화된 공격 기법
- SQLMAP이라는 자동화된 툴을 이용하기도 한다.
Union SQL Injection
XSS
정의
- Cross-site Scripting
- 웹 페이지에 악성 스크립트를 삽입하는 공격
- 사이트 이용자 정보를 탈취한다.
방어
- HTML 필터링을 한 후 DB에 저장
- 만약을 위해 프론트엔드에서도 필터링한다.
심화 공격기법
Stored XSS
- 게시판에 js코드를 작성해두면 사용자가 헤당 게시글을 클릭해서 확인하는 순간 해당 스크립트가 실행되면서 사용자의 쿠키정보나 session 정보를 탈취하는 공격 기법
Reflected XSS
- 검색어등을 보여주는 곳에 스크립트를 심는 공격
- URL을 사용자에게 누르게 만들면 공격 성공
DOM based XSS
- DOM에 악의적인 스크립트를 심는 공격
- 브라우저가 해석하는 단계에서 발생되는 공격
CSRF Attack
정의
- Cross-site Request Forgery
- 공격자가 사용자를 이용하여 웹 서버에 악의적인 요청을 보내는 공격
방어
- Referrer Check : 허용한 도메인만 요청 허락하도록 설정
- CSRF Token : 모든 요청에 토큰을 발급하여 서버에서 검증
- CAPTCHA : 사람이 요청한 것이 맞는지 검증
Command Injection
정의
- 애플리케이션에 사용되는 시스템 명령에 악의적인 명령어를 삽입하는 공격(WebShell attack)
- 서버 root 권한을 취득할 수 있다.
방어
- 가급적 시스템 함수는 사용하지 않는다.
-
민감한 문자를 필터링한다 ex) |
, &, ;, >, <, … |
FileUpload Attack
정의
- 악성 스크립트 파일을 업로드하는 공격
- 업로드 후 파일 위치를 찾아 실행시키면 공격 성공
방어
- 확장자 / 파일 타입 검사
- 업로드 파일을 난수화하여 저장
- 특수 문자가 포함된 경우 업로드 금지
JavaScript Injection
정의
- Client-side에서 JavaScript를 삽입시키는 공격
- 크롬 console 등을 통해 조작이 가능하다.
- Client-side에 민감한 데이터를 넣을 경우 탈취 가능
방어
- Client-side엔 민감한 정보를 절대 넣지 않는다.
- 데이터 유효성 검사가 필요한 경우 서버와 통신한다. 중요한 데이터는 Client와 Server 모두에서 검사한다.
DDos
정의
- Distributed Denial of Service
- 서버에 비정상적으로 많은 트래픽을 보내는 공격
- 서비스가 마비되고 많은 비용이 소모된다.
방어
- 제일 단순한데 제일 막기 어렵다.
- 확장 가능한 서비스 구조 설계
- IP 필터링
- Rate limit
- 솔루션 구매
Dictionary Attack
정의
- 미리 사전에 등록해놓은 문자열을 암호로 대입하는 공격
- Brute Force의 일종
방어
- 의미있는 문자열은 암호로 등록 못하도록 설정
- Account Lockout Policy
- 2-factor 인증
Rainbow Table
정의
- 해시 함수를 이용한 평문을 모두 저장시켜놓은 표를 이용하여 순차적 대입을 통해 비밀번호를 알아내는 공격 방법이다.
방어
- Salt를 사용한다.
- Key Stretching을 사용한다.
- PBKDF2, Bcrypt 등의 암호화 알고리즘을 사용한다.
알아야 하는 보안 정책들
CORS
- Cross-Origin Resource Sharing
- 개발자가 지정한 프로토콜, 도메인, 포트가 아니라면 리소스를 가져올 수 없는 보안 정책이다.
- Response header를 보고 허용 여부를 브라우저가 정한다.
- 브라우저마다 구현이 다를 수 있다.
CSP
- Content-Security-Policy
- 실행 가능한 리소스에 대한 WhiteList를 정하는 정책
- 웹 사이트가 허용되지 않은 리소스를 요청하지 못하도록 막는다.
- XSS 방지에 도움이 된다.
- 기본적으로 inline script는 실행을 막는다.
- 메타 태그 혹은 HTTP Header로 설정 가능하다.

HTTPS
- HTTP 프로토콜의 암호화된 버전
- 소켓 통신에 암호화된 데이터를 전송한다.
- SSL 인증서를 이용한다.

axios를 이용한 네트워크 API 실습
- axios를 이용한 네트워크 API를 호출하고 제어하는 방법에 대해 알아본다.
- axios는 브라우저나 nodejs에서 사용가능한 http client library이다.
- JSON Placeholder : 몇 가지 REST API를 테스트할 수 있는 endpoint를 무료로 제공하는 사이트
useReducer()
: action.type에 따라서 사용자가 원하는대로 state를 변경해줄 수 있다. 여러가지의 행위들을 각각의 상태와 setter로 정의하는 것보다 하나의 useReducer()를 이용하는 것이 훨씬 간편하다. 단, async, await를 통한 네트워크 호출을 할 수 없다.
- 컴포넌트는 최대한 순수할수록 좋다.
- 사이드 이펙트를 걱정하지 않아도 좋다.
- 확장에 유연하다.
- 테스트가 쉽다.
- 그래서 API 호출하는 건 컴포넌트에 삽입하기 보다는 App에서 호출하여 결과를 component에 넘겨서 처리를 해줘야 한다.
- Provider에 API 호출하는 함수들을 넘겨줘서 provider내에서 그 함수를 실행시키도록 만든다.
- axios를 이용해서 데이터를 호출하고 최대한 컴포넌트는 순수하게 유지하면서 context API를 이용하여 데이터를 제어하고 useReducer를 이용하여 상태를 관리하는 방법을 배웠다.
SPA의 역사
SPA가 등장하기까지의 과정
- 웹 애플리케이션의 시초는 MPA(Multi Page Application)이였다.

- 점점 브라우저의 요구사항이 많아졌다.(화려한 애니메이션, 멀티미디어의 재생, 동적인 데이터 전송 => MPA로는 사용자에게 불편하게 되었다.)
- 이를 해결하기 위해 AJAX(Asynchronous JavaScript and XML)가 등장하였다.
- 대표적으로 페이지 이동없이 볼 수 있는 구글 지도가 AJAX의 예시이다.
- Hashbang : URI Fragment(주소 맨 뒤 #부분을 뜻한다.)를 읽은 후 해당 경로에 해당하는 페이지를 Ajax를 불러오는 방식 2010년 쯤부터 유행했다.
- Blick 제거, 로딩 표시 가능 → UX 개선
- Hashbang의 가장 큰 문제점 = 검색 엔진에 잡히지 않는다.
- History API(pushState(), replaceState())와 같은 기능이 생기면서 페이지 이동없이 브라우저의 주소를 변경할 수 있게 되었다.
- pushState + ajax = pjax
- Github Co-founder가 jquery+pjax를 만들면서 더욱 유행하게 되었다.
react-router
Router 추가하기
- Broswer Router를 생성하고 route를 구성한다.
- Not Found Error를 처리한다.
- Contact UI를 route한다.
- 중첩 routes들을 관리한다.
- 최상위 route의 children 속성에 배열을 두고 해당 배열에 자식 routes들을 열거한다.
- 최상위 route의 어떤 요소에 자식 요소들을 배치할지 Outlet으로 결정한다.
- link를 클릭했을 때 full document request를 한다. 하지만
Link
태그를 이용하여 Client Side Routing을 수행하면 서버에 요청을 보내지 않고 URL을 업데이트하도록 해준다.
- Loading Data
- React Router는 data들을 route components로 쉽게 이동시켜줄 수 있는 data conventions이 존재한다.
- 데이터를 가져올 때 2가지 API를 사용할 것이다.
loader
와 useLoaderData
이다.
- Data Writes + HTML Forms
- Creating Contacts
React 배우기 (11) : useImperativeHandle
- 평범한 component에서 쓰이기 보다 라이브러리 등의 특수한 컴포넌트에서 자주 사용되는 특수한 훅이다.
- ref를 통해 사용자 정의 함수를 만들어서 상위 컴포넌트에 전달할 수 있다. 즉, 상위 컴포넌트에서 하위 컴포넌트를 함수호출로 제어할 수 있다.
- 라이브러리를 만든다면 유용하게 쓰일 수 있다.
- 상위 컴포넌트에서 하위 컴포넌트를 이용하기 위한 함수들을 정의해주는 훅이다.
컴포넌트 심화
- 컴포넌트를 잘 설계하기 위한 방법에 대해 소개한다.
- 컴포넌트는 어떻게 설계할까?
- 아키텍쳐는 우리가 일을 잘하기 위한 방법이다.
- 모듈을 잘 나눠서 구현하는 것이 좋다.
- 모듈에서 중요한 것은 응집도와 결합도이다.
응집도
- 모듈 내에 포함된 요소들이 서로 연괸되어 있는 정도
- 즉, 모듈 내 기능들이 하나의 책임으로 잘 뭉쳐있는지를 나타낸다.
- 높은 응집도일수록 좋은 설계이다.
- 응집도가 중요한 이유는 응집도가 높을 수록 하나의 책임에 집중하고 독립성을 높이면서 우리가 수정하기 위한 요소를 빠르게 찾을 수 있기 때문이다.
높은 응집도를 만들기 위한 방법
공통 폐쇄 원칙
- 같은 이유로 동일한 시점에 변경되는 기능을 하나의 모듈로 묶어야 한다는 원칙
- 객체지향 원칙인 단일 책임 원칙을 컴포넌트 관점으로 바라본 것
- 과하게 적용하면 재사용성이 줄어들 수 있다.
- ex) 컴포넌트에 종속된 기능, 스타일 등을 묶기
공통 재사용 원칙
- 모듈 내의 기능들을 함께 재사용이 될 수 있어야 한다는 원칙
- 반대로 이야기하면 함께 재사용이 될 수 없다면 분리해야한다는 뜻
- 과하게 적용되면 개발 용이성이 줄어들 수 있다.

- 무엇이 옳은가 정답은 없다. 상황에 따라 이 사이를 적절하게 조절하는 것이 개발자의 능력이다.
결합도
- 다른 모듈과의 의존성에 대한 정도
- 모듈과 모듈 사이의 관계가 어느 정도인지를 나타낸다.
- 낮은 결합도일 수록 좋은 설계
- 결합도가 높을수록 수정에 대해 영향을 미치는 정도가 증가한다.
- 따라서 낮은 결합도일수록 안정성이 증가한다.
- 결합도가 높으면 어디서 버그가 터질지 모른다.
낮은 결합도를 만들기 위한 방법
안정된 의존성 원칙
- 의존할 바엔 더 안정된 모듈을 의존한다는 원칙
- 의존하는 모듈이 적고 의존되는 모듈이 많을 수록 안정적인 모듈(컴포넌트)이다.
- 안정성 지표는 Fan-out / (Fan-in + Fan-out)으로 계산 가능
- Fan-out은 의존되는 것
- Fan-in은 의존하는 것
- 0에 가까울수록 안정되고 1에 가까울수록 불안정한 컴포넌트
- 당연하지만 변화에 무겁도록 만들어야 한다.
안정된 추상화 원칙
- 컴포넌트는 안정된 만큼 추상적이어야 한다는 원칙
- 추상성은 추상 클래스 수/ 클래스 수로 계산된다.
- React 컴포넌트는 UI 요소가 포함되어 있어 이런식으로 계산할 수 없다.
- 레이어를 잘 나눠야 한다.
React에서 컴포넌트 추상성 계산 방법
- Domain, Style, Semantic, Data, Network, Event, Route, Device, Context
- 컴포넌트에서 고정적으로 사용된다면 해당된다.
- 위 요소 중 1 - 해당하는 부분 / 9로 계산해보자
- 외부에서 주입받아 제한이 없다면 추상적인 것으로 본다.
- 단, 주입받는 요소가 특정 값으로 제한된다면 구체적인 것으로 본다.
- 대략적으로 계산하는 방법이기 때문에 정확할 수는 없다.
컴포넌트를 나누는 방식
- 프론트엔드는 변화가 큰 소프트웨어 백엔드처럼 기능, 도메인 단위로 레이어를 나누기엔 애매하다.
- 웬만하면 추상화 단위로 나누는 것이 편하다.
결론
- 경험과 숙련도가 높을수록 좋은 컴포넌트를 작성할 수 있다.