[F-Lab 모각코 챌린지 36일차] SQL 인젝션, CSRF ...

부추·2023년 7월 6일
0

F-Lab 모각코 챌린지

목록 보기
36/66

TIL

  1. js로 현재 페이지 Host불러오기 + 복사버튼 생성
  2. 초간단 SQL 인젝션
  3. ATTACK!



1. 단축 URL 간단 기능 추가

서버에서 생성된 단축된 URL에 대해, 새로 URL이 생성되었을 때 생성된 전체 URL을 제공하면서도, 버튼을 누르면 전체 URL을 복사할 수 있는 기능을 추가하자 라고 리퀘스트를 받았다. 뭐 어려운 일은 아니겠거니 예상이 갔다. 내 안의 자바스크립트는 웹브라우저와 관련한 기능은 전부 탑재하고 있는 유능 of 유능한 놈이었기에..

자바스크립트로 현재 클라이언트 브라우저가 요청을 보내 위치한 URL 호스트(도메인 주소)를 host라는 변수에 저장할 수 있는 코드이다.

const host = window.location.host;

만약 localhost:8080에 서버를 띄우고 해당 js코드가 위치한 path를 열면 현재 위치한 host에는localhost:8080 문자열이 저장되어 있을 것이다.

fetch API를 통해 서버에서 불러온 단축 URL 문자열은 data.shortenUrl이라는 변수에 저장되어 있다. 컨트롤러의 @GetMapping("/{shortenUrl}")에 리다이렉트 기능이 들어있는 redirect() 핸들러 메소드가 들어있으므로, 해당 컨트롤러를 호출할 수 있는 클라이언트 URL은 다음과 같이 완성된다.

const shortenUrl = host + '/' + data.shortenUrl;

복사할 대상은 완성이 되었으니, 복사할 버튼을 추가할 차례다.

const copyButton = document.createElement('button');
copyButton.textContent = '복사';
copyButton.setAttribute('type', 'button');
copyButton.addEventListener('click',function() {
    navigator.clipboard.writeText(shortenUrl);
});
  1. copyButton을 만든다. button 태크를 가졌다.
  2. 버튼의 내부 텍스트로 "복사", type="button" attribute를 추가했다.
  3. 버튼을 'click'했을 시 수행될 event listener을 추가했다. 복사 버튼을 누르면, navigator.clipboardwriteText()를 통해 클라이언트의 클립보드에 위에서 생성한 shortenUrl문자열이 담길 것이다.

마지막으로 이렇게 생성된 copyButton을 원하는 DOM의 하위에 추가하도록 했다.

generatedDom.appendChild(copyButton);

초간단 실습이 남았다!! 해보자.


실제로 http://naver.com을 입력창에 넣고 생성을 눌러 생성된 full URL과 복사 버튼이 생겼다.
사진에서 "복사"버튼을 클릭하고, 브라우저 검색 창에 command + v 버튼을 눌렀다.

굳....
크롬 개발자도구의 Network 탭에서 3xx 응답과 함께 원하는 리다이렉팅이 일어나는 것까지 확인했다!(disk Cache는.. 두번째 요청이라 모른 척 해주쉐이)



2. 초간단 SQL 인젝션

유저의 input을 받는 form에 아래와 같이 입력했다고 하자.

  • ID : 1
  • PASSWORD : 1 or 1=1

만약 SQL 단에서 별다른 조치 없이 유저의 로그인 성공에 대한 쿼리가 아래와 같이 작성되어버렸다면, 위의 로그인은 성공하고 만다.

SELECT * FROM MEMBER WHERE
	MEMBER.ID=${ID} AND
    MEMBER.PASSWORD=${PASSWORD};

왜냐면 SQL 쿼리의 where절의 or는 후순위로 밀려나기 때문에, 인풋 데이터가 아무런 조치 없이 그대로 SQL문에 바인딩되는 경우 실제 실행되는 쿼리는 아래와 같기 때문이다.

SELECT * FROM MEMBER WHERE
	(MEMBER.ID=1 AND MEMBER.PASSWORD=1) OR
    1=1;

마지막 줄, 1=1 때문에 위의 쿼리는 무조건 성공하고 만다.


# Prepare Statements를 사용하자

사용자 입력이 별다른 사전 작업 없이 그대로 SQL문에 들어가기 때문에 문제가 되는 것이다.

String query = "SELECT * FROM tb_user WHERE uId=" + uId;

uId가 입력이라고 했을 때, 위처럼 SQL 코드를 작성하는 것은 상당히 큰 문제가 된다. 방금 설명한 SQL 인젝션이 그대로 수행될 수 있다.

대신, prepare statemet를 활용하면 안전하게 SQL 쿼리와 쿼리 파라미터를 분리할 수 있다. 아래와 같이 '?'에 지정된 타입의 input값을 안전하게 바인딩할 수 있다.

String query = "SELECT * FROM tb_user WHERE uId= ?";

stmt = conn.prepareStatement(query);
stmt.setString(1, uId);

prepared statement는 단순히 sql에 쿼리 문자열을 더하는 것이 아니라 바인딩되는 파라미터를 안전한 문자열로 바꿔 바인딩시킨다.

또한 아래와 같이 SQL문에 영향을 줄 수 있는 escape character / string에 대한 입력값 검증 역시 필요하다.

–, ', ", ?, #, (, ), ;, @, =, *, +, union, select, drop, update, from, where, join, substr, user_tables, user_table_columns, information_schema, sysobject, table_schema, declare, dual,…

오늘날의 ORM 혹은 DB driver를 제공하는 프레임워크, 라이브러리들은 기본적으로 escape 처리가 되어있는 모양이다.



3. ATTACK!

1) 사전(Dictionary) 공격과 Brute Force 공격

가지고 있는, 혹은 생성할 수 있는 데이터들을 전부 시도하여 보안 관련 정보를 취득하려는 무식한 공격이다.

보통 사용자들이 비밀번호로 사용하는 문자열에는 뭐가 있을까?

0000
1234
qwerty
password
1q2w3e4r
qlalfqjsgh

사이트 가입자가 1000만명이라고 했을 때, 저 비밀번호를 사용하는 사람들이 적어도 한명은 존재하지 않을까? practical한 관점으로 봤을 때 100%라고 봄ㅋㅋ

위와 같이, 보통 사람들이 많이 사용하는 비밀번호, 혹은 생각할 수 있는 암호나 보안 문자의 조합들을 하나의 "사전"으로 만들어놓고 권한을 얻기 위해 사전에 있는 값들을 전부 입력해보는 공격을 사전 공격이라고 한다.

브루트포스는 조금 더 넓은 관점의 사전 공격.. 이라 이해할 수 있다. 알고리즘 문제에서 브루트포스 알고리즘이라고 하면, 문제 해결을 위해 가능한 모든 조합의 변수를 사용하는 것을 의미하는데 암호학에서도 비슷하다. 입력 가능 범위의 모든 조합을 시도해보며 보안 정보를 알아내는 공격을 브루트 포스 공격이라고 한다.


2) XSS

Cross-Site-Scripting의 약자로, 사이트의 HTML 문서에 <script>태그를 추가하여 악성 자바스크립트 코드를 집어넣는 공격이다.

1번의 "단축 URL 기능 추가"에서, 자바스크립트를 통해 현재 클라이언트가 요청을 보내 존재하고 있는 host의 도메인 주소를 알아냈고, 클라이언트의 클립보드에 특정 문자열을 복사하기까지 했다. 별다른 거 없이 코드 몇줄만 작성했는데도 간단하게 사용자 컴퓨터 정보를 얻어내고 조작까지 이뤄냈다.


3) CSRF

Cross Site Request Forgery의 약자로, 사이트간 요청 위조를 말한다. 보통 쿠키-세션 기반 인증 정보를 통해 auth 처리를 하는 사이트에서, 인증된 사용자의 권한을 위조 취득하여 요청하는 공격이다.

사용자가 로그인하면 쿠키에 사용자 session ID가 저장될 것이고, 해당 sessionID를 가지고 하는 요청은 로그인 권한을 가진 사용자의 요청으로 처리될 것이다. CSRF는 이 취약점을 이용한다.

예를들어, 비밀번호를 바꾸는 HTTP 요청 메세지가 다음과 같다 해보자.

POST /password/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE

password=passw0rd

공격자는 사용자의 메일로 아래 HTML 코드가 담긴 메일을 보낸다.

<form action="https://vulnerable-website.com/password/change" method="POST">
          <input type="hidden" name="password" value="mypassword">
   </form>
<script>          
	document.forms[0].submit();
</script>

무슨 일이 일어날지 알겠지?! action attribute의 URL로 POST요청이 간다. 비밀번호는 mypassword이다. 한 줄로 끝나는 단순한 요청이라면 아래와 같이 보이지 않는 이미지 태그를 로드하게 하는 것만으로도 CSRF 공격이 가능하다.

<img src="http://vulnerable-website.com/logout" />

개똑똑해; 이런거 어캐생각하나.


방어를 위해서 할 수 있는 일은 다음과 같다.

  1. Referer 체크 : 보통이라면 호스트(host)와 Referrer 값이 일치하므로 둘을 비교한다. CSRF 공격의 대부분 Referrer 값에 대한 검증만으로도 많은 수의 공격을 방어할 수 있다고...
  2. CAPTCHA 이용 : 민감한 정보에 관한 요청, 특정 권한이 필요한 요청시에 CAPTCHA를 도입해 올바른 입력이 없을 경우 요청을 거부한다.
  3. CSRF 토큰 사용 : 사용자 세션에 임의에 값을 저장하여, 모든 요청마다 해당 값을 포함하여 전송하도록 한다. 서버에서 요청을 받을때마다, 세션에 저장된 값과 요청으로 전송된 값이 일치하는지 확인한다.



REFERENCE

https://devscb.tistory.com/123 (good)
http://blog.plura.io/?p=6056

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글