Hyper Text Transfer Protocol Secure Socket layer
기존의 http요청은 중간에 누군가 들여다 본다면 정보가 그대로 노출되었다. https프로토콜은 요청의 내용을 한번 암호화 시킨 것이다.
서버가 클라이언트로부터 요청을 받으면 인증서와 함께 응답을 전송한다. 응답을 받은 클라이언트는 인증서 domain과 응답객체의 domain을 비교한다. 만약 두 도메인이 같으면 요청한 서버가 맞다는 것이 확실해진다.
만일 해커가 중간에 서버인 척 데이터를 제공하면 이것을 판별할 수 있다.
각 브라우저는 저마다 인증서가 다르다.
다른 한 쌍으로 암호화 및 복호화를 할 수 있다. 한 쌍중 하나는 비밀, 다른 하나는 클라이언트에 공개하여 데이터를 안전하게 전송할 수 있게 한다.
공개키는 매번 생성하기에는 알고리즘이 복잡하기 때문에 통신 초창기에서만 비밀키로 사용하기 위한 키를 만들어내기 위해서 사용한다.
제 3자가 서버와 클라이언트가 주고받는 정보를 탈취할 수 없도록 하는 것이다. 이를 위해 서버와 클라이언트는 서로가 합의한 방법으로 데이터를 암호화하여 주고받는다. 따라서 중간에 제 3자에게 데이터가 탈취되더라도 그 내용을 알아볼 수 없다. 기존에 배웠던 HTTP 는 요청 및 응답이 탈취된다면 아래와 같이 전달되는 데이터의 내용을 제 3자가 그대로 확인할 수 있다. 아래 사진은 데이터를 전송하는 요청을 'wireshark'라는 패킷 분석 프로그램을 이용하여 캡처한 사진이다.
데이터를 암호화하여 전송하는 HTTPS를 사용한다면 비밀번호와 같은 중요한 데이터가 유출될 가능성이 HTTP보다 현저히 적어진다. 아래 사진은 위 사진과 동일한 요청을 프로토콜만 HTTPS로 변경했을 때의 데이터를 캡처한 사진이다.
보이는 것 처럼 내용이 암호화되어 전송되기 때문에 정확한 키로 복호화 하기 전까지는 어떤 내용인지 알 수 없다.
HTTPS에서는 클라이언트와 서버가 데이터를 암호화하여 주고받기 위해 비대칭키 방식과 대칭키 방식을 혼용하여 사용한다. 서버와 클라이언트가 통신할 때, 대칭키 방식은 양쪽이 공통의 비밀 키를 공유하여 데이터를 암호화 및 복호화하는 것, 비대칭키 방식은 각각 공개키와 비밀키를 가지고 상대가 나의 공개키로 암호화한 데이터를 개인이 가진 비밀키로 복호화하는 것을 의미한다.
클라이언트와 서버가 데이터를 주고받을 때는 대칭키를 사용한다. 비대칭키 알고리즘은 대칭키 알고리즘보다 훨씬 복잡하기 때문에 대칭키를 사용하여 데이터를 암호화 및 복호화하는 것이 훨씬 컴퓨터에 부담을 덜 주기 때문이다. 그런데 대칭키를 서로 주고 받는 과정에서부터 정보가 탈취된다면 어떻게 될까? 아마 암호화를 하던 안 하던 결국 모든 데이터가 복호화 가능하게 될 것이다.
그래서 HTTPS는 이러한 대칭키를 주고받을 때는 비대칭키 방식으로 주고 받도록 한다. 앞서 말했듯 비대칭키는 공개키로 암호화한 정보는 개인이 가진 비밀키로만 풀 수 있기 때문에 중간에 대칭키가 탈취되더라도 개인키가 없이는 이를 복호화할 수 없기 때문이다.
HTTPS의 또다른 특징 중 하나는 브라우저가 서버의 응답과 함께 전달된 인증서를 확인할 수 있다는 점이다. 이러한 인증서는 서버의 신원을 보증하여 우리가 접속한 Naver가 해커가 정교하게 따라한 가짜 Naver가 아님을 보장해주는 역할을 한다.
이때 이를 보증할 수 있는 제 3자를 Certificate Authority, CA라고 부른다. CA는 인증서를 발급해주는 엄격하게 공인된 기관들을 말한다. 이러한 CA들은 서버의 공개키와 정보를 CA의 비밀키로 암호화하여 인증서를 발급한다.
서버가 클라이언트에게 CA에서 발급받은 인증서를 전달하면 클라이언트는 OS 또는 브라우저에 미리 내장되어 있던 CA 리스트를 통해 브라우저에서 인증된 CA에서 발급받은 인증서인지 먼저 확인한다. 만약 인증된 CA에서 발급한 인증서가 아니라면 아래와 같이 화면에 경고창을 띄워 서버와 연결이 안전하지 않다는 화면을 보여준다.
그 후 인증서가 확인되었다면 브라우저에 제공된 해당 CA 기관의 공개키로 서버 인증서를 복호화한다. 앞서 말했듯 CA의 비밀키로 인증서를 암호화하였기 때문에 CA의 공개키로 복호화가 가능한 것이다. 이렇게 서명을 복호화해 얻은 공개키로 클라이언트는 서버를 믿을만한 대상인 지 신뢰할 수 있습니다. 만약 인증서가 위조되었다면 CA의 공개키로 서버의 인증서를 복호화할 수 없을 것이다.
따라서 브라우저는 인증서의 도메인과 데이터를 제공하는 서버의 도메인을 비교할 수 있기 때문에 '중간자 공격'을 감지하여 보안 위협으로부터 사용자 및 사용자의 데이터를 보호할 수 있다. 또한 위와 같은 경고를 직접 보여줌으로써 브라우저들은 인증된 CA가 발급한 인증서를 이용하여 데이터를 제공하는 안전한 서버를 사용자가 사용하도록 유도한다.
이렇게 서버와 클라이언트간의 CA를 통해 서버를 인증하는 과정과 데이터를 암호화하는 과정을 아우른 프로토콜을 TLS 또는 SSL이라고 말한다. (*SSL과 TLS는 사실상 동일한 규약을 뜻하며 SSL이 표준화되며 바뀐 이름이 TLS입니다.)
위와 같이 DB에 패스워드를 그대로 저장할 경우 해커가 DB정보를 통해 정보를 탈취할 수 있다.
이럴 때 사용하는 것이 Hashing암호화다.
hashing은 어떠한 문자열에 '임의의 연산'을 적용하여 다른 문자열로 변환하는 것이다.
이외에도 SHA256등 다른 유명한 알고리즘이 많다.
암호화해야 하는 값에 어떤 '별도의 값'을 추가하여 결과를 변형하는 것
원본값에 임의로 약속된 '별도의 문자열'을 추가하여 해시를 진행한다면 기존 해시 값과 전혀 다른 해시 값이 반환되어 알고리즘이 노출되더라도 원본 값을 보호할 수 있도록 하는 안전 장치다.
기존: (암호화 하려는 값) => (hash 값)
Salt 사용 : (암호화 하려는 값) + (Salt 용 값) => (hash 값)
Salt 사용 시 주의점
쿠키는 서버에서 클라이언트에 영속성있는 데이터를 저장하는 방법이다. 그러므로 서버가 원한다면 서버는 클라이언트의 쿠키를 이용하여 데이터를 가져올 수 있다.
그러므로 쿠키를 이용하는 것은 단순히 서버에서 클라이언트에 쿠키를 전송하는 것만 의미하지 않고 클라이언트에서 서버로 쿠키를 다시 전송하는 것도 포함된다.
서버는 쿠키를 이용하여 데이터를 저장하고 이 데이터를 다시 불러와 사용할 수 있다.
하지만 데이터를 저장한 이후 아무 때나 데이터를 가져올 수는 없다. 데이터를 저장한 이후 특정 조건들이 만족되어야 다시 가져올 수 있기 때문이다.
'Set-Cookie':[
'cookie=yummy',
'Secure=Secure; Secure',
'HttpOnly=HttpOnly; HttpOnly',
'Path=Path; Path=/cookie',
'Doamin=Domain; Domain=codestates.com'
]
이런 조건들은 위 코드처럼 쿠키 옵션으로 표현할 수 있다. 아래는 주로 사용하는 쿠키 옵션이다.
Domain : 서버와 요청의 도메인이 일치하는 경우 쿠키 전송
Path : 서버와 요청의 세부경로가 일치하는 경우 쿠키 전송
MaxAge or Expires : 쿠키가 유효한 기간을 정하는 옵션
세션 쿠키: MaxAge 또는 Expires 옵션이 없는 쿠키로, 브라우저가 실행 중일 때 사용할 수 있는 임시 쿠키입니다. 브라우저를 종료하면 해당 쿠키는 삭제됩니다.
영속성 쿠키: 브라우저의 종료 여부와 상관없이 MaxAge 또는 Expires에 지정된 유효시간만큼 사용가능한 쿠키입니다.
Secure : 사용하는 프로토콜에 따른 쿠키의 전송 여부를 결정하는 옵션. Secure 옵션이 true로 설정된 경우 HTTPS를 이용하는 경우에만 쿠키를 전송
HttpOnly : 자바스크립트에서 브라우저의 쿠키에 접근 여부를 결정한다. 만약 해당 옵션이 true로 설정된 경우, 자바스크립트로 쿠키에 접근이 불가하다.
세션은 서버에 데이터를 저장하기 때문에 사용량이 매우 많으면 서버의 성능이 저하될 수 있다.
또한 XSS공격으로 인해 세션 쿠키가 탈취되면 보안에 구멍이 뚫릴수도 있다.
SQL Injection은 이름 처럼 데이터베이스에서 임의의 SQL문을 실행할 수 있도록 명령어를 삽입하는 공격 유형이다. 응용 프로그램의 보안상의 허점을 이용해 데이터베이스를 비정상적으로 조작하며, 이로 인해 기록이 삭제되거나 데이터가 유출될 수 있다.
SQL문은 사람이 사용하는 자연어와 비슷하기 때문에 키워드를 막기엔 한계가 있다. 따라서 블랙리스트가 아닌 화이트리스트 방식으로 해당 키워드가 들어오면 다른 값으로 치환하여 SQL Injectino에 대응할 수 있다.
보안에서 화이트리스트란 기본 정책이 모두 차단인 상황에서 예외적으로 접근 가능한 대상을 지정하는 방식 또는 지정된 대상들을 말한다.
Prepared Statement 구문을 사용하면 사용자의 입력이 SQL문으로부터 분리되어 SQL Injection을 방어할 수 있다. 사용자의 입력 값이 전달 되기 전에 데이터베이스가 미리 컴파일하여 SQL을 바로 실행하지 않고 대기하며, 사용자의 입력값을 단순 텍스트로 인식한다. 따라서 입력 값이 SQL문이 아닌 단순 텍스트로 적용되어 공격에 실패하게 된다.
공격자는 데이터베이스의 Error Message를 통해 테이블이나 컬럼 등 데이터베이스의 정보를 얻을 수 있다. 에러가 발생한 SQL문과 에러 내용이 클라이언트에 노출되지 않도록 별도의 에러핸들링이 필요하다.
XSS(사이트 간 스크립팅)는 웹 사이트의 관리자가 아닌 이(공격자)가 웹 사이트에 악의적인 스크립트를 심어놓는 행위를 말한다. 주로 클릭을 유도하는 글을 잘성한 후, 해당 글을 클릭하면 공격자가 심어놓은 스크립트가 실행되어 웹 사이트 이용자에게 피해를 준다.
이 공격의 대상은 클라이언트로, 사용자가 의도하지 않은 행동을 하게되거나 사용자의 쿠키 등 민감한 정보를 탈취한다. XSS로 탈취한 사용자의 민감한 정보를 이용해 권한을 획득한 후 이를 이용해 CSRF 등 다양한 공격에 활용되기도 한다.
Stored XSS는 스크립트가 서버에 저장되어 여러 사용자에게 피해를 줄 수 있는 유형으로, 지속적(Persistent)기법이라고도 한다. 주로 게시판 형태의 웹 사이트에서 발생하며, 공격자가 악의적인 스크립트가 담긴 글을 작성한 후 서버에 저장된 글을 조회할 때마다 스크립트가 실행되는 형식이다.
Reflected XSS는 URL 파라미터를 사용해 스크립트를 만드는 유형으로, 비 지속적(Non-persistent)기법이라고도 한다. 이 스크립트는 서버에 저장되지 않으며 웹 어플리케이션의 지정된 파라미터를 사용할 때 나타나는 취약점을 이용한다.
주로 공격이 일어나는 곳은 검색엔진으로, 검색 결과가 없는 경우 입력 결과를 HTML 문서에 포함하여 응답하는 특성을 이용하기 때문에 반사(Reflected) XSS라고 부른다. 언뜻 CSRF공격과 비슷한 듯 하지만 CSRF는 공격자가 사용자의 권한을 탈취하여 서버의 가짜 요청을 보내는 반면, Reflected XSS의 경우 악성 스크립트가 실행되는 곳, 즉 공격 대상이 서버가 아닌 클라이언트라는 점에서 엄연히 다르다.
스크립트 태그의 입력을 막기
a. 입력값 확인 혹은 필터링
b. 입력값 치환하기
쿠키 설정 확인하기 (httpOnly)
쿠키에 민감한 정보 담지 않기
a. 민감정보를 서버에서 관리하기
b. 암호화 사용하기
다른 사이트에서 유저가 보내는 요청을 조작하는 것
e.g) 이메일에 첨부된 링크를 누르면 내 은행계좌의 돈이 빠져나간다.
다른 오리진이기 때문에 해커가 직접 데이터를 접근할 수 없다.
쿠키를 사용한 로그인 : 유저가 로그인 했을 때, 쿠키로 어떤 유저인지 알 수 있어야한다.
예측할 수 있는 요청/Parameter를 가지고 있어야 함 : request에 해커가 모를 수 있는 정보가 담겨있으면 안된다.
Clickjacking은 사용자가 의도한 클릭 대상이 아닌 다른 대상을 클릭하도록 속이는 공격 기법이다. 사용자 인터페이스 덧씌우기(User Interface Redress)라고도 부르며 공격자는 눈에 보이는 화면 위에 눈에 보이지 않는 다른 행동을 실행하는 화면을 배치한다. 공격자는 이를 통해 민감한 개인 정보를 탈취하거나 컴퓨터의 제어권을 획득할 수 있다.
HTTP의 X-Frame-Options 헤더를 의미한다. 2009년 IE8에 처음 도입되었으며 그 후로 대부분의 브라우저가 이를 채택하고 있다. X-Frame-Options 헤더에 설정한 옵션에 따라 브라우저가 웹 페이지 화면의 iframe 의 렌더링 여부를 제한할 수 있다. 하지만 브라우저에 따라 구 버전에서 작동을 안하거나 브라우저 자체가 지원을 안하는 문제가 있을 수도 있다.
콘텐츠 보안 정책은 컴퓨터 보안 표준으로, 웹 페이지 콘텐츠에서 악의적인 콘텐츠를 실행하게하는 보안 공격을 예방하기 위해 도입 되었다. 웹 사이트 소유자들이 콘텐츠의 출처(오리진)를 신뢰할 수 있도록 표준 방식을 제공하여 외부의 악의적인 스크립트나 데이터 삽입 공격을 막을 수 있다.
세션기반 인증은 서버에 유저 정보를 담는 방식이다.
토큰기반인증은 "이 부담을 클라이언트에게 넘겨줄 수 없을까?"에서 고안되었다.
대표적인 토큰기반 인증은 JWT(JSON Web Token)이다.
JWT는 보통 다음과 같이 두 가지 종류의 토큰을 이용해 인증을 구현한다.
액세스 토큰은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근할 수 있는 권한부여에 사용된다. 클라이언트가 처음 인증을 받게 될 때 (로그인 시) 액세스 토큰, 리프레시 토큰 두 가지를 다 받지만, 실제로 권한을 얻는데 사용하는 토큰은 액세스 토큰이다.
만약 액세스 토큰을 악의적인 유저가 얻어낼 경우 이 유저는 자신이 OO유저인 것 마냥 서버에 여러가지 요청을 보낼 수 있다. 그렇기 때문에 엑세스 토큰에는 비교적 짧은 유효기간을 주어 토큰을 탈취하더라도 오랜시간 사용할 수 없도록 하는 것이 좋다.
액세스 토큰의 유효기간이 만료된다면 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받는다. 이 때, 유저는 다시 로그인할 필요가 없다.
유효기간이 긴 리프레시 토큰마저 악의적인 유저가 얻어낸다면 이는 큰 문제가 된다. 이 때문에 유저의 편의보다 정보를 지키는 것이 더 중요한 웹사이트들은 리프레시 토큰을 사용하지 않는 곳이 많다.
Header는 이것이 어떤 종류의 토큰인지(지금의 경우엔 JWT), 어떤 알고리즘으로 시그니처를 sign(암호화)할지가 적혀있다. JSON Web Token 이라는 이름에 걸맞게 JSON형태로 정보가 담겨있다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload에는 단어 그대로 서버에서 활용할 수 있는 유저의 정보가 담겨 있다.
여기에는 어떤 정보에 접근 가능한지에 대한 권한 또는 유저의 이름과 같은 개인정보 등 담을 수 있습니다. 아니면 두 가지 모두 담을 수도 있다.
payload엔 너무 민감한 정보는 담지 않는 것이 좋다. 디코딩이 쉬운 base64방식으로 인코딩 되기 때문이다.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
base64로 인코딩된 첫번째, 그리고 두 번째 부분이 완성 되었다면, Signature는 이를 서버의 비밀 키(암호화에 추가할 salt)와 헤더에서 지정한 알고리즘을 사용하여 암호화한다.
즉, base64 인코딩 자체는 누구나 쉽게 디코딩할 수 있어 Header와 Payload 모두 쉽게 확인할 수 있지만 비밀키를 사용해 이를 암호화한 값(시그니처)은 비밀키를 보유한게 아니라면 해독해내는데 엄청난 시간과 노력이 들어갈 것이다.
예를들어 만약 HMAC SHA256 알고리즘(암호화 방법 중 하나)을 사용한다면 Signature는 아래와 같은 방식으로 생성된다.
따라서 누군가 권한을 속이기 위해 Payload를 변조하여 base64로 인코딩하더라도 원본 Payload로 암호화한 시그니처 값과 다르기 때문에 서버가 해당 토큰이 변조되었음을 확인할 수 있다.
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
JWT는 권한 부여에 굉장히 유용하다. 새로 다운받은 A라는 앱이 Gmail과 연동되어 이메일을 읽어와야 하는 경우는 다음과 같다.
웹이나 앱에서 흔히 찾아볼 수 있는 소셜 로그인 인증 방식은 OAuth 2.0라는 기술을 바탕으로 구현된다.
전통적으로 직접 작성한 서버에서 인증을 처리해주는 것과는 달리, OAuth는 인증을 중개해주는 메커니즘이다. 보안된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜이다.
즉, 이미 사용자 정보를 가지고 있는 웹 서비스(GitHub, Google, Facebook 등)에서 사용자의 인증을 대신해주고, 접근 권한에 대한 토큰을 발급한 후, 이를 이용해 내 서버에서 인증이 가능해진다.
OAuth는 보안상의 이점이 있다. 검증되지 않은 App에서 OAuth를 사용하여 로그인한다면, 직접 유저의 민감한 정보가 App에 노출될 일이 없고, 인증 권한에 대한 허가를 미리 유저에게 구해야 하기 때문에 더 안전하게 사용할 수 있다.
Resource Owner: 액세스 중인 리소스 유저다. 김코딩의 구글 계정을 이용하여 App에 로그인할 경우, 이때 리소스 오너 김코딩이 된다.
Client: 리소스 오너를 대신하여 보호된 리소스에 액세스하는 애플리케이션이다. 클라이언트는 서버, 데스크탑, 모바일 또는 기타 장치에서 호스팅할 수 있다. 김코딩이 A라는 애플리케이션을 Google의 소셜로그인을 이용해 사용한다면 A가 클라이언트가 된다.
Resource Server: 클라이언트의 요청을 수락하고 응답할 수 있는 서버다. 클라이언트(e.g A애플리케이션)가 Google Photo에서 김코딩의 사진을 가져오는 경우, Goolge Photo가 리소스 서버가 된다.
Authorization Server: Resource Server가 액세스 토큰을 발급받는 인증 서버다. 리소스 오너를 성공적으로 인증한 후 클라이언트에게 액세스 토큰을 발급하는 서버를 말한다.
Authorization Grant: 클라이언트가 액세스 토큰을 얻는 방법을 의미한다. 다음과 같은 방법들이 주로 사용된다.
Authoriztion Code: Authoriztion Grant의 한 타입으로 액세스 토큰을 발급하기 위한 Code를 의미한다. Client ID로 이 Code를 받아온 후, Client Secret을 이용해 받아온 이 Access Token으로 이제 리소스 서버에 접근할 수 있다.
Scope: Scope는 토큰의 권한을 정의한다. 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위다. 이와 같은 설정을 통해 특정 유저의 사진첩에는 접근할 수 있지만, 연락처 등 다른 리소스에는 접근할 수 없도록 접근권한을 지정할 수도 있다.