https://github.com/innerbloo/frontend-security
npm install
node server.js
Finder 폴더 이동 -> /private/etc/ -> hosts 파일 열기 ->
127.0.0.1 sitemap.example 마지막줄에 추가
주요 메소드는 다음과 같이 5가지가 있습니다.
전달을 희망하는 데이터가 있을 경우 쿼리스트링 을 통해 전달합니다.
GET /member/100?username=lisa
쿼리스트링 외에 메세지 바디를 사용하여 데이터를 전달 가능하지만 서버에서 따로 구성해야 하기 때문에 권장하지 않습니다.
POST또한 조회가 가능하지만 GET 메서드는 캐싱이 가능하기 때문에 GET을 쓰는 것이 유리합니다.
VScode에서 실습해보기
// routes/api.js
const express = require("express");
const router = express.Router();
router.get("/",(req,res)=>{
res.send({message:"hello"});
});
module.exports = router;
// server.js 추가
const api = require("./routes/api");
app.use("/api",api);
console 창에서 실습해보기
const response = await fetch("http://localhost:3000/api");
await response.json();
fetch 함수는 HTTP 요청에 대한 응답을 받을 수 있게 하는 메서드입니다. 응답으로 받은 bod에 대하여 자바스크립트로 조작 가능하고 다양한 용도로 활용 가능할 수 있게 합니다.
쿼리스트링 받기 실습 - VSCode
// routes/api.js
router.get("/",(req,res)=>{
let message = req.query.message;
if (message === ""){
res.statue(400);
message = "message 값이 비었음";
}
res.send({message});
})
// http://localhost:3000/api/?message=hello
쿼리스트링 받기 실습 - console 창
await fetch("http://localhost:3000/api?message=hello")
메시지 바디를 통해 서버로 요청 데이터를 전달하면 요청 데이터를 처리하여 업데이트 합니다.
신규 리소스 등록, 프로세스 처리에 사용합니다.
데이터를 GET 하는데 있어서 JSON으로 조회 데이터를 넘겨야 하는 애매한 경우에도 POST를 사용할 수 있습니다.
신규 자원 생성은 201이나 200으로 응답을 보냅니다.
POST /members
Content-Type: application/json
{
"username":"young",
"age":20
}
📖 form 데이터 전송
- 회원가입, 상품주문, 데이터 변경에 이용
- GET, POST 만 지원
- input 태그안에 들어간 값들이 쿼리스트링으로 서버에 전송됨
- Content-Type : application/x-www-form-urlencoded
VSCode 실습하기
// routes/api.js
router.use(express.json());
router.post("/",(req,res)=>{
const body = req.body;
console.log(body);
res.end();
});
module.exports = router;
console에서 실습하기
await fetch("http://localhost:3000/api",{
method:"POST",
body: JSON.stringify({message:"안녕하세요."}),
headers: {"Content-type":"application/json"}
});


네트워크 탭에서 다음과 같이 api에 대한 응답 헤더를 확인할 수 있습니다.
ℹ️ 이외 다른 Method
PUT - 리소스를 대체(수정)하는 메서드(Update)
요청 메세지에 리소스가 있으면 덮어쓰고, 없으면 새로 생성한다.
일부 데이터만 보낼 경우, 기존 데이터가 삭제되고 일부 데이터로 대체된다.DELETE - 리소스를 제거하는 메소드
상태코드는 기본적으로 200을 사용하고 상황에 따라 204를 사용한다.
PATCH - 일부 리소스만 변경하는 메서드(Update)
patch를 지원하지 않는 서버일 경우, 대신 POST를 사용할 수 있다.
위치 또는 서버 자체에 대한 정보와 같이 응답에 대한 부가적인 정보를 갖는 헤더입니다.
요청 및 응답의 메세지 모두에서 사용되지만 컨텐츠에는 적용되지 않는 헤더
요청 헤더는 HTTP 요청에서 사용되지만 메시지의 컨텐츠와 관련 없는 HTTP 헤더
fetch될 리소스나 클라이언트 자체에 대한 정보를 포함하여 서버로 보내집니다.
컨텐츠 길이나 MIME 타입과 같이 Entity Body에 대한 자세한 정보를 담은 헤더
Content-Type:text/html; charset-latin-1 - 해당 개체가 html 문서이고 iso-latin-1 문자 인코딩 방식으로 표현Content-Encoding:gzip, deflate// routes/api.js
router.get("/",(req,res)=>{
let message = req.query.message;
res.setHeader("X-Timestamp",Date.now());
const lang = req.headers["x-lang"];
if (message === ""){
res.status(400);
if(lang ==="en"){
message="message is empty"
} else {
message = "message 값이 비었음"
}
}
res.send({message});
})
headers 옵션을 통해 임의로 요청 헤더를 설정할 수 있습니다.
// console 창
const res = await fetch("http://localhost:3000/api?message=",{
headers:{"X-Lang":"en"},
});
await res.json();
때문에 기존의 보안 문제를 해결하고자 HTTP에 TLS 통신 프로토콜을 결합한 HTTPS 사용이 권장됩니다.
SSL의 업데이트 버전으로 기존의 SSL에 대한 소유권 변경을 위해 이름이 바뀐 것입니다. 때문에 SSL 3.0과 TLS 최초 버전은 거의 유사합니다.
ℹ️ TMI
SSL은 넷스케이프에서 개발되었으며 이후 국제 인터넷 표준 관리 기구 (IETF)로 넘어가게 됨에 따라 TLS로 이름이 변경되었습니다. 때문에 현재 사용되는 SSL은 TLS입니다.
TLS의 역할
ℹ️ SSL/TLS는 웹에서 전송되는 데이터를 암호화하고, 복호화가 거의 불가능하며, 클라이언트와 서버간의 헨드세이크를 통해 인증이 이뤄집니다. 또 데이터 무결성을 위해 디지털 서명을 하여 조작 여부를 확인할 수 있습니다.

ℹ️ Mixed Context란?
HTTPS와 HTTP 통신을 사용하는 리소스가 혼재되어 사용되는 상태를 말합니다.
Passive Mixed Content - 이미지와 영상, 음성 파일과 같인 리소스가 Mixed Content를 발생시키는 경우. 브라우저에 실행되는 코드를 포함하지 않기 때문에 영향이 적다고 할 수 있습니다.
Active Mixed Content - 자바스크립트와 CSS 등 브라우저에서 실행되는 코드에 대한 Mixed Content 패턴. 보안 공격에 치명적입니다.
대부분의 브라우저들은 이러한 Mixed Content 패턴에 대하여 Active Mixed Content 하부 리소스 접근이 차단되어 있습니다. 때문에 HTTP로 전송되는 자바스크립트와 css는 변경이 없었더라도 차단되어 애플리케이션이 제대로 동작하지 않을 수 있기 때문에 사전에 Mixed Content가 없도록 해야 합니다.
HTTPS 등장 이전까지 HTTP 통신 외에 존재하지 않았기에, 여전히 http://로 시작하는 URL이 남아 있습니다. 웹 애플리케이션에서 이러한 HTTP통신을 멈추게 되면 해당 경로로 접근할 수 있는 URL에 접속 자체가 불가능해지기 때문에, HTTPS를 사용하는 웹 애플리케이션도 HTTP 전송을 계속 허가하는 경우가 있습니다.
하지만 HTTPS가 적용된 웹 애플리케이션을 HTTP로 통신이 가능하게 한다면, 보안을 강화한 이유가 사라집니다. 때문에 유저가 http를 사용하더라도 내부적으로 HTTPS 통신을 사용할 수 있도록 강제할 수 있습니다. 이 기능이 바로 HSTS(HTTP Strict transport security)입니다.
ℹ️ 사용법
Strict-Transport-Security: max-age=
Strict-Transport-Security: max-age=; includeSubDomains
Strict-Transport-Security: max-age=; includeSubDomains; preload
includeSubDomains를 사용하면 하위 서브 도매인에도 HSTS를 적용할 수 있으며, max-age를 통해 유효 시간을 지정할 수 있습니다.
HSTS Preload
HSTS Preload를 통해 처음 접속부터 HTTPS 통신을 사용할 수 있게 할 수 있습니다. 해당 방법은 한 번이라도 접속을 시도하여 HSTS를 유효화해야 합니다. HSTS preload 리스트는 브라우저에 내장되어 해당하는 도메인은 HTTPS로 처음부터 접속할 수 있게 합니다.
인터넷에 공개된 컨텐츠는 언제 어디서 이용되는지 알 수 없습니다. 때문에 민감 정보가 포함된 데이터 설정 실수로 인해 노출 위험이 존재합니다.
보호 정책이 없는 웹 애플리케이션은 다음과 같은 위험에 노출될 수 있습니다.
공격자가 웹 페이지를 조작하여 사용자가 의도하지 않은 동작을 수행하도록 속이는 공격 형태입니다. 웹 애플리케이션의 사용자 인터페이스를 가려서 숨긴 상태로 사용자가 보고 있는 것과 다른 UI 요소나 링크를 클릭하게 유도하는 공격입니다.
공격자가 상대방의 브라우저에 스크립트가 실행되도록 해 사용자의 세션을 가로채거나, 웹사이트를 변조하거나, 악의적 콘텐츠를 삽입하거나, 피싱 공격을 진행하는 것을 말합니다.
사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 공격을 말합니다.
iframe - 페이지 내부에 다른 페이지를 삽입할 수 있는 HTML 요소입니다.
만약 접속 제한이 없는 브라우저의 경우, 피싱 사이트에서 iframe을 이용해 로그인 정보 화면을 삽입하고, 다른 사용자의 로그인 정보를 훔쳐볼 수 있습니다.
출처란?
https://example.com:3000/path
http - 스키마
Example Domain - 호스트명
3000 - 포트
/path - 경로
여기서 스키마 ~ 포트까지를 출처라고 하며, 스키마, 호스트명, 포트 중 하나라도 다를 경우 다른 출처로 인식합니다.
동일출처 정책 - 브라우저에 내장된 접근방식 중 하나로, 브라우저는 웹 애플리케이션 사이에 출처라는 경계를 설정하여 서로의 접근을 제한합니다. 이러한 정책을 통해 별도의 대책을 세울 필요 없이 다른 웹 애플리케이션의 접근 제한이 가능합니다.
ℹ️ 접근 제한 예
- 자바스크립트를 사용해 교차 출처로 요청 전송
- 자바스크립트를 사용해 iframe 내 교차 출처 페이지에 접근
- 교차 출처의 이미지를 불러오는
<canvas>요소의 데이터에 접근- web storage와 indexedDB에 저장된 교차 출처 데이터에 접근
동일 출처 정책은 fetch와 XMLHttpRequest를 사용해 교차 출처에 요청 전송을 제한합니다. 해당 접근 제한을 피하고 교차 출처에 요청을 전송하기 위해서는 CORS를 사용해야 합니다.
// 교차 출처로 인해 요청 차단됨
await fetch("http://localhost:3000/api",{
headers:{"X-Token":"aBcDeF1234567890"}
});
// 동일 출처 정책에 의해 요청이 승인됨
await fetch("http://site.example:3000/api",{
headers:{"X-Token":"aBcDeF1234567890"}
});
동일 출처 정책에 의해 iframe은 교차 출처일 경우 접근이 제한됩니다. 해당 접근 제한을 피하기 위해 postMessage 함수를 사용하여 데이터를 전송하는 출처를 확인 가능하게 하고 안전하게 데이터를 주고받게 할 수 있습니다.
// public/user.html
<!DOCTYPE html>
<html>
<head>
<title> 로그인 사용자 정보 </title>
</head>
<body>
<ul id="user_info">
<li>로그인 ID : frontend_security</li>
<li>메일 주소 : frontend-security@mail.example</li>
<li>주소 : 서울시 광진구 </li>
</ul>
</body>
</html>
// 악의적인 페이지 작성하기 public/attacker.html
<!DOCTYPE html>
<html>
<head>
<title>attacker.example</title>
<script>
function load() {
const userInfo = frm.document.querySelector("#user_info");
console.log(userInfo.textContnet);
}
</script>
</head>
<body>
<div>
<!-- 사용자를 유인할 피싱 콘텐츠 -->
</div>
<iframe
name="frm"
onload="load()"
src="http://site.example:3000/user.html"
width="80%"
/>
</body>
</html>
<canvas> 요소 데이터에 접근 제한<canvas>는 교차 출처의 이미지를 불러올 때 동일 출처 정책에 의해 접근이 제한됩니다. 해당 제한을 회피하기 위해서는 CORS로 이미지를 불러와야 합니다.
브라우저에 내장된 데이터 저장 기능인 web storage와 indexedDB는 동일출처 정책에 따라 교차 출처의 접근이 제한될 뿐만 아니라 새로 연 탭과 윈도우 간의 접근도 제한됩니다. 때문에 사용자가 피싱 사이트에 접속하더라도 브라우저에 저장된 데이터는 피싱 사이트에 유출되지 않습니다.
⚠️ 동일 출처 정책 예외 사항
아래의 요소들은 모두 기본적으로 동일 출처 접근 제한 정책을 따르지만 예외적인 상황이 존재합니다.
<script>요소에서 자바스크립트 등을 불러오기<link>요소에서 CSS 등을 불러오기<img>요소에서 불러오는 이미지(.jpg, .png)<video>요소와<audio>요소에서 미디어 파일을 불러오기<link rel="stylesheet" href="…" /> <script src="…"></script> <img src="…" />출처: 🌐 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏 [Inpa Dev 👨💻:티스토리]
<form>요소로 폼 전송하기<iframe>요소와<frame>요소에서 페이지 불러오기<object>요소와<embed>요소에서 리소스 불러오기- @font-face를 사용해 css에서 폰트 불러오기
해당 사항들은 CORS와 crossorigin 속성을 사용하여 접근을 제한할 수 있습니다.
ℹ️ iframe 동일 출처 접근
기본적으로 iframe 또한 동일 출처 접근 제한 대상입니다. 하지만 태그로 화면을 보여주는 것은 교차 출처에서도 가능합니다.
다음과 같이 localhost:3000 에서 site.exampl:3000 교차 출처를 호출할 경우, 해당 iframe 자체는 화면에 표시되지만 javascript로 접근하여 유저의 정보를 console.log로 출력 시도한 코드가 접근이 제한된 것을 확인할 수 있습니다.