XSS(Cross-Site-Scripting)는 웹사이트에서 의도치 않은 스크립트를 넣어서 실행시키는 기법을 말합니다.
보통 게시판에 악성 스크립트가 담긴 글을 올리는 형태로 이루어집니다.
그리고 스크립트가 포함된 글을 열어보게 되면 브라우저에서 원치 않는 스크립트가 실행되는 방식이죠.
이걸 통해 유저의 쿠키 정보를 탈취하거나, 유저 비밀번호를 변경하는 api를 호출하는 행위를 할 수 있습니다.
위는 가장 기초적인 방식이고, XSS의 종류는 크게 세 가지로 나눌 수 있습니다.
스크립트를 웹 서버에 저장하기 때문에 Stored라는 이름이 붙게 되었습니다.
조금 더 쉽게 말하면 스크립트는 HTML페이지가 구문분석이 될 때마다 실행됩니다.
그렇기 떄문에 다른 XSS 방식과는 다르게 서버와는 전혀 관계가 없습니다.
쿠키에 중요한 정보를 담지 않고 서버에 중요 정보를 저장하는 방식을 사용할 수 있습니다.
그리고 httponly 속성을 다는 방식을 사용할 수 있습니다.
(document.cookie를 이용해서 쿠키에 직접 접근하는 것을 막는 옵션입니다!)
마지막으로 웹 애플리케이션에서 Content Security Policy (CSP)를 설정하여
외부 스크립트의 실행을 제한하고 안전한 리소스만 허용합니다.
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
하지만 React를 사용하면 기본적으로 XSS(Cross-Site Scripting) 공격에 대한 방어가 강화됩니다.
React는 JSX를 사용하고, JSX는 사용자 입력을 자동으로 이스케이핑하여 안전하게 렌더링하기 때문입니다. 즉, React 컴포넌트를 통해 사용자 입력을 출력할 때 React는 스크립트가 실행되지 않도록 처리합니다.
import React from 'react';
const userInput = "<script>alert('XSS attack');</script>";
function App() {
return (
<div>
{userInput}
</div>
);
}
export default App;
이렇게 해커가 스크립트를 넣어줘도 자연스럽게 이스케이핑 되기 때문에 해결할 수 있습니다.
이스케이핑
사용자 입력 중에서 HTML 태그와 스크립트가 아닌 일반 텍스트로 처리되도록 하는 과정을 의미합니다.
여러 분야에서 이스케이핑이 사용되지만 주로 웹 보안과 관련이 있습니다.
웹 애플리케이션에서는 사용자 입력을 이스케이핑하여 웹 페이지에 안전하게 표시하고,
원치 않는 스크립트 실행을 방지하는 데 사용됩니다.
XSS가 사용자가 특정 사이트를 신뢰하기 때문에 발생하는 문제라면,
CSRF는 특정 사이트가 사용자를 신뢰하기 때문에 발생하는 문제입니다.
즉 XSS는 클라이언트의 브라우저에서 발생하는 문제라면 CSRF는 서버에서 발생하는 문제입니다.
침입자가 XSS를 사용하면 사용자의 쿠키를 탈취할 수 있고,
CSRF를 사용하면 서버로부터 권한을 탈취할 수 있습니다.
다른 예시를 보면 사용자는 mybank에 로그인하고 침입자의 url에 접근하면 CSRF코드가 실행되고,
브라우저는 mybank에게 사용자가 원하지 않는 요청을 보내게 됩니다.
mybank는 이 요청을 사용자가 보낸 요청이라 생각하고 정상적으로 처리되고, 침입자에게 돈이 전달되는 것이죠.
Cross-Site Request Forgery (CSRF)를 방지하는 가장 흔한 방법은
요청에 CSRF 토큰을 포함시키는 것입니다.
이 토큰은 사용자의 세션과 연결되어 있으며,
악의적인 웹 사이트에서 해당 토큰을 갖고 있는 경우에도 CSRF 공격을 실행하지 못하도록 합니다.
// Express.js를 사용하는 예시
const express = require('express');
const session = require('express-session');
const csrf = require('csurf'); // CSRF 미들웨어
const app = express();
app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true }));
const csrfProtection = csrf();
app.use(csrfProtection);
// 라우트 핸들러에서 CSRF 토큰을 클라이언트로 전송
app.get('/my-form', (req, res) => {
res.render('my-form', { csrfToken: req.csrfToken() });
});
// ...