RESTful 애플리케이션에서 CSRF를 방지하는 방법은 무엇입니까?


78

CSRF (Cross Site Request Forgery)는 일반적으로 다음 방법 중 하나로 방지됩니다.

  • 참조 자 확인-RESTful이지만 신뢰할 수 없음
  • 양식에 토큰을 삽입하고 서버 세션에 토큰을 저장하십시오-실제로 RESTful이 아닙니다.
  • 암호화 된 일회용 URI-토큰과 같은 이유로 RESTful이 아닙니다.
  • 이 요청에 대해 수동으로 비밀번호 보내기 (HTTP 인증에 사용 된 캐시 된 비밀번호가 아님)-RESTful하지만 편리하지 않음

내 생각은 토큰을 생성하기 위해 사용자 비밀, 비밀 스럽지만 정적 인 양식 ID 및 JavaScript를 사용하는 것입니다.

<form method="POST" action="/someresource" id="7099879082361234103">
    <input type="hidden" name="token" value="generateToken(...)">
    ...
</form>
  1. GET /usersecret/john_doe 인증 된 사용자로부터 JavaScript가 가져옵니다.
  2. 응답 : OK 89070135420357234586534346이 비밀은 개념적으로는 정적이지만 보안을 향상시키기 위해 매일 / 시간마다 변경할 수 있습니다. 이것은 유일한 기밀입니다.
  3. JavaScript로 암호화 된 (그러나 모든 사용자에게는 정적입니다!) 양식 ID를 읽고 사용자 시크릿과 함께 처리하십시오. generateToken(7099879082361234103, 89070135420357234586534346)
  4. 생성 된 토큰과 함께 양식을 서버로 보냅니다.
  5. 서버는 사용자 시크릿과 폼 ID를 알고 있기 때문에 클라이언트가 두 결과를 전송하고 비교하기 전에했던 것과 동일한 generateToken 함수를 실행할 수 있습니다. 두 값이 동일한 경우에만 작업이 승인됩니다.

JavaScript 없이는 작동하지 않는다는 사실에도 불구하고이 접근 방식에 문제가 있습니까?

추가:


usersecret은 사용자에게 고유하지 않으므로 공격자는 해당 번호를 가져 와서 새 계산을 사용하도록 스크립트를 조정하기 만하면됩니다. 상태가 전혀없는 경우 어떻게 사용자를 인증합니까?
Mike

사용자 비밀은 사용자별로 고유하며 인증 (HTTP 기본 또는 다이제스트 인증 또는 인증서 인증) 후에 만 ​​검색 할 수 있습니다.
deamon

답변:


28

여기에는 많은 답변이 있으며 그중 상당수에 문제가 있습니다.

하지 말아야 할 것 :

  1. JavaScript에서 세션 토큰을 읽어야한다면 끔찍하게 잘못한 것입니다. 세션 식별자 쿠키에는 항상 HTTPOnly가 설정되어 있어야 스크립트에서 사용할 수 없습니다.

    이 하나의 보호를 통해 XSS의 영향을 상당히 줄일 수 있습니다. 공격자가 더 이상 로그인 한 사용자 세션 토큰을 얻을 수 없기 때문에 모든 의도와 목적을 위해 애플리케이션의 자격 증명과 동일합니다. 당신은 왕국에 열쇠를주기 위해 하나의 오류를 원하지 않습니다.

  2. 세션 식별자는 페이지의 내용에 기록되지 않아야합니다. 이는 HTTPOnly를 설정 한 것과 같은 이유 때문입니다. 이것은 귀하의 csrf 토큰이 귀하의 세션 ID가 될 수 없음을 의미합니다. 다른 값이어야합니다.

해야 할 일 :

  1. OWASP의 지침을 따르십시오 .

  2. 특히 이것이 REST 애플리케이션 인 경우 CSRF 토큰의 이중 제출을 요구할 수 있습니다 . 이렇게하는 경우 상위 도메인 (example.com)이 아닌 특정 전체 도메인 ( www.mydomain.com )으로 정의 하고 "samesite"쿠키 속성도 활용해야합니다. 인기.

암호화 된 임의의 항목을 만들고 ASCII Hex 또는 Base64 인코딩으로 저장 한 다음 서버가 페이지를 반환 할 때 쿠키 및 양식에 추가하면됩니다. 서버 측에서 쿠키 값이 양식 값과 일치하는지 확인하십시오. Voila, CSRF를 죽이고 사용자에게 추가 메시지를 표시하지 않았으며 더 많은 취약점에 노출되지 않았습니다.

참고 : @krubo가 아래에 언급했듯이 이중 제출 기술 에는 몇 가지 약점이있는 것으로 밝혀졌습니다 (이중 제출 참조) . 이 약점에는 다음이 필요하기 때문에 :

  1. 상위 도메인으로 범위가 지정된 쿠키를 정의합니다.
  2. HSTS 설정에 실패했습니다 .
  3. 공격자는 사용자와 서버 사이의 일부 네트워크 위치를 제어합니다.

약점은 "실제 보안 위험"보다는 "멋진 데프콘 토크"범주에 더 많이 해당한다고 생각합니다. 어쨌든 이중 제출을 사용하려는 경우 자신을 완전히 보호하기 위해 몇 가지 추가 조치를 취하는 것이 나쁘지 않습니다.


새로운 업데이트 07/06/2020

이중 제출을 수행하는 새로운 방법은 이전과 같이 요청 본문에 암호화 임의 문자열을 생성하고 전달하는 것입니다. 그러나 쿠키가 동일한 정확한 값을 갖기보다는 쿠키가 인증서에 의해 서명되는 문자열의 인코딩 된 값이되도록하십시오. 이것은 여전히 ​​서버 측에서 확인하기 쉽지만 공격자가 모방하기가 훨씬 더 어렵습니다. 내 게시물 앞부분에 설명 된 samesite 쿠키 속성 및 기타 보호 기능을 계속 사용해야합니다.


세션 쿠키가있는 XSS는 JavaScript에서 읽을 수있는 토큰이있는 XSS만큼 취약합니다. 사용자 계정에서 내 계정으로 돈을 이체하는 AJAX 요청을 여전히 만들 수 있다면 서버는 기꺼이 수락합니다.
ghayes

4
@ghayes 동의하지 않습니다. 세션 토큰은 CSRF 토큰보다 훨씬 더 민감합니다. 세션 토큰을 사용하면 내 컴퓨터에서 애플리케이션에 완전히 액세스 할 수 있습니다. CSRF 토큰을 사용하면 브라우저에서 실행되는 사전 스크립팅 된 민감한 작업 목록을 잠재적으로 가질 수 있습니다. 두 번째 시나리오는 실행하기가 훨씬 더 어렵고, 앱에 대한 지식이 필요하고, 실행하는 데 더 많은 시간이 걸리며, 작업은 미리 계획 한 내용으로 제한됩니다. 첫 번째 시나리오는 모든 웹 사이트에 대한 코드 한 줄과 공격자가 자신의 컴퓨터에서 사용할 수있는 쿠키 관리자 앱을 사용합니다.
Doug

여기서 언급 할 가치가 있습니다. cross-origin HTTP request서버에 대한 엄격한 검사와 API의 http 반환 헤더를 사용하면 공격자가 로그인 한 사용자에게 수행 할 수있는 자동화 된 손상을 많이 제한 할 수 있습니다.
LessQuesar

1
업데이트 OWASP 지침은 더 이상 CSRF의 이중 제출은 기본 방어로 토큰을지지 않습니다 있지만, 심층 방어로 이동했습니다. 문제는 공격자가 쿠키를 작성할 수있는 경우 공격을받을 수 있다는 것입니다. 예를 들어 다른 하위 도메인을 제어하는 ​​경우이를 수행 할 수 있습니다.
krubo

1
OWASP 링크는 이제 404를 반환합니다
ParkerD

10

이게 맞습니까?

  • 쿠키를 통해 로그인 한 사용자를 CSRF로부터 보호하기를 원합니다.
  • 동시에 앱의 Basic, OAuth 및 Digest 인증 요청을위한 RESTful 인터페이스가 필요합니다.

그렇다면 사용자가 쿠키를 통해 로그인했는지 확인 하고 CSRF를 적용한 후에 만 ​​적용하는 것은 어떻습니까?

확실하지 않지만 다른 사이트에서 기본 인증 또는 헤더와 같은 것을 위조 할 수 있습니까?

내가 아는 한 CSRF는 쿠키에 관한 것 입니까? RESTful 인증은 쿠키에서 발생하지 않습니다.


나도 이것에 대해 궁금했다! 이 기사에 따르면 mathieu.fenniak.net/… 누군가가 쿠키 / 세션을 통해 오는 경우 CSRF 검사를 켜고, 요청이 일종의 상태 비 저장 인증 체계 기본을 통해 오는 경우 끄는 것이 가능해야합니다. etc.
CMCDragonkai 2013 년

4
기본 인증에주의하십시오. 브라우저가 사용자 편의를 위해 후속 요청에 제공된 Authorization 헤더를 전송하므로 쿠키를 통해 로그인 한 사용자와 사실상 동일합니다.
Simon Lieschke 2015 년

@SimonLieschke는 Windows / ntlm / kerberos를 통합합니다. 설정되면 브라우저는 DC에서 토큰을받습니다.
stuartm9999

9

인증 / 승인하려면 서버에 어떤 상태가 필요합니다. http 세션 일 필요는 없지만 분산 캐시 (예 : memcached) 또는 데이터베이스에 저장할 수 있습니다.

인증에 쿠키를 사용하는 경우 가장 쉬운 해결책은 쿠키 값을 두 번 제출하는 것입니다. 양식을 제출하기 전에 쿠키에서 세션 ID를 읽고 숨겨진 필드에 저장 한 다음 제출하십시오. 서버 측에서 요청의 값이 쿠키에서 가져온 세션 ID와 동일한 지 확인합니다. 다른 도메인의 악한 스크립트는 쿠키에서 세션 ID를 읽을 수 없으므로 CSRF를 방지합니다.

이 체계는 세션 전체에서 단일 식별자를 사용합니다.

더 많은 보호를 원하면 세션별로 고유 ID를 생성하십시오.

또한 JS에서 토큰을 생성하지 마십시오. 누구나 코드를 복사하고 다른 도메인에서 실행하여 사이트를 공격 할 수 있습니다.


1
HTTP 인증에서 보여주는 것처럼 인증에는 세션이 필요하지 않습니다. 토큰을 생성하는 JavaScript 코드는 비밀이 아닙니다. 사용자 비밀 만 비밀이어야합니다.
deamon

3
@Sri는 세션이 보안 및 성능 측면에서이를 처리하는 가장 좋은 방법이라는 데 동의합니다. 확장성에 문제를 일으킬 수있는 사용자 별 상태를 서버에서 추적해야하므로 RESTful이 아닙니다.
루크

1
페이지가 XSS 공격에 취약한 경우 이중 쿠키 제출이 작동하지 않는다고 말하는 것이 맞습니까? 그러면 도메인 자체 내에서 직접 양식을 제출할 수 있고 값이 쿠키와 양식을 통해 전송되기 때문입니다.
Gabriele Cirulli 2012 년

1
@GabrieleCirulli 예, 공정한 진술입니다. XSS는 대부분의 CSRF 보호를 능가합니다. Captcha는 아마도 여전히 효과적인 유일한 형태의 CSRF 일 것입니다.
Sripathi Krishnan

CSRF 보호를 의미합니까? : P하지만 네, 동의합니다.
Gabriele Cirulli 2012 년

6

정적 양식 ID는 전혀 보호를 제공하지 않습니다. 공격자가 직접 가져올 수 있습니다. 공격자는 클라이언트에서 JavaScript를 사용하도록 제한되지 않습니다. 그는 서버 측에서 정적 양식 ID를 가져올 수 있습니다.

제안 된 변호를 완전히 이해하고 있는지 모르겠습니다. 어디에서 GET /usersecret/john_doe왔습니까? 페이지 JavaScript의 일부입니까? 이것이 문자 그대로 제안 된 URL입니까? 그렇다면 이것이 username비밀이 아니라고 가정합니다. 즉, 브라우저 또는 플러그인 버그가 도메인 간 GET 요청을 허용하는 경우 evil.ru가 사용자 비밀을 복구 할 수 있음을 의미합니다. 도메인 간 GET을 수행 할 수있는 사람이 검색 할 수 있도록 허용하는 대신 인증시 사용자 비밀을 쿠키에 저장하지 않는 이유는 무엇입니까?

CSRF에 저항하기를 원하는 자체 인증 시스템을 구현하기 전에 "교차 사이트 위조에 대한 강력한 방어"를 주의 깊게 읽었습니다 . 사실, 나는 내 자신의 인증 시스템을 구현하는 것을 전혀 재고 할 것입니다.


1
양식 ID는 공개 키와 유사합니다. 당신이 맞아요, GET /usersecret/john_doeJavaScript의 일부입니다. 사용자 이름 자체는 비밀이 아니지만 인증 된 (!) 사용자가이 요청으로 가져온 ID입니다. 링크 감사합니다.
deamon

3

CSRF 예방 치트 시트 에는 편안한 서비스에서 사용할 수 있는 몇 가지 방법 이 있습니다. 가장 RESTful 상태 비 저장 CSRF 완화는 Origin 또는 HTTP 리퍼러 를 사용하여 요청이 신뢰하는 도메인에서 시작되었는지 확인하는 것입니다.


2
이것은 위험한 조언이지만 HTTP 리퍼러를 스푸핑하는 것은 어렵지만 불가능하지 않으며 리퍼러 헤더도 보장되지 않습니다 (리퍼러 헤더를 보내지 않으면 앱이 손상됨).
Boy Baukema

3
@RelaXNow 내가 작성한이 CSRF 익스플로잇 프레임 워크를 확인하십시오 : github.com/TheRook/CSRF-Request-Builder . 본문은 물론 임의의 http 헤더를 지정할 수 있습니다. 그러나 http 참조 자는 Flash에서 금지되어 있으므로 변경할 수 없습니다 . CSRF 예방 치트 시트는 매우 좋습니다. 내 게시물의 링크를 읽어야합니다.
루크

공정한 점은 CSRF의 맥락에서 공격자는 피해자의 Referer 헤더를 (내가 아는 한) 스푸핑 할 수 없지만 헤더는 여전히 보장되지 않으며 API에 대한 요구는 가능한 경우에만 수행해야합니다. 회사의 내부 응용 프로그램과 같이 항상 전송되도록 보장합니다.
Boy Baukema

3
@RelaXNow 요청이 HTTPS 페이지에서 시작된 경우 리퍼러는 요청에서 커밋됩니다. 이는 실패로 간주되어야합니다 (위 링크에서 언급 됨). 사람들은이 문제를 해결하기 위해 노력하고 있습니다. Mozilla는 "Origin"http 헤더를 도입했습니다.이 헤더는 멋지고 살펴볼 가치가 있습니다. RESTful csrf 보호 문제를 해결하는 데 사용할 수있을뿐만 아니라 json 포함 공격과 같은 다른 많은 남용도 사용할 수 있습니다. 클릭 재킹. 문제는 모든 브라우저가 지원하는 것은 아니라는 것입니다 :(. 또한 -1을 제거하고 싶을 경우를 대비하여 내 게시물을 편집했습니다.
rook

1
s / comitted / omitted / :). 좋은 포인트 / 정보이지만 오래 전에 -1을 철회하고 유용한 정보를 위해 귀하의 의견을 찬성했습니다.
Boy Baukema 2013

0

JavaScript 없이는 작동하지 않는다는 사실에도 불구하고이 접근 방식에 문제가 있습니까?

사용자 비밀은 클라이언트에 보내는 경우 비밀이 아닙니다. 우리는 일반적으로 이러한 비밀을 사용하여 해시를 생성하고 양식과 함께 보내고 비교를 위해 다시 기다립니다.

RESTful이 되려면 요청에 처리 방법에 대한 모든 정보가 포함되어야합니다. 이를 수행 할 수있는 방법 :

  • REST 클라이언트에 csrf 토큰 쿠키를 추가하고 양식과 함께 숨겨진 입력으로 동일한 토큰을 보냅니다. 서비스와 클라이언트가 서로 다른 도메인에있는 경우 자격 증명을 공유해야합니다. 서비스에서 2 개의 토큰을 비교해야하며 동일하면 요청이 유효합니다.

  • REST 서비스에 csrf 토큰 쿠키를 추가하고 리소스 표현 (숨겨진 입력 등)과 함께 동일한 토큰을 보낼 수 있습니다. 다른 모든 것은 이전 솔루션의 끝과 동일합니다. 이 솔루션은 RESTfulness의 가장자리에 있습니다. (클라이언트가 쿠키를 수정하기 위해 서비스를 호출하지 않을 때까지 괜찮습니다. 쿠키가 http 전용 인 경우 클라이언트는 이에 대해 알지 못하며 그렇지 않은 경우 클라이언트가 설정해야합니다.) 더 많은 작업을 수행 할 수 있습니다. 각 양식에 다른 토큰을 추가하고 쿠키에 만료 시간을 추가하는 경우 복잡한 솔루션입니다. 양식과 함께 만료 시간을 다시 보낼 수 있으므로 토큰 유효성 검사가 실패한 이유를 알 수 있습니다.

  • 서비스의 리소스 상태에서 사용자 암호 (사용자별로 다름)를 가질 수 있습니다. 표현을 작성하여 각 양식에 대한 토큰 (및 만료 시간)을 생성 할 수 있습니다. 실제 토큰 (및 만료 시간, 메서드, URL 등) 및 사용자 시크릿에서 해시를 생성하고 해당 해시를 양식과 함께 보낼 수도 있습니다. 물론 "사용자 비밀"을 비밀로 유지하므로 양식과 함께 보내지 않습니다. 그 후 서비스가 요청을 받으면 요청 매개 변수와 사용자 시크릿에서 해시를 다시 생성하고 비교할 수 있습니다. 일치하지 않으면 요청이 유효하지 않습니다.

REST 클라이언트가 자바 스크립트 주입 가능한 경우 이들 중 어느 것도 보호 할 수 없으므로 모든 사용자 콘텐츠를 HTML 엔터티에 대해 확인하고 모두 제거하거나 innerHTML 대신 항상 TextNodes를 사용해야합니다. SQL 주입과 HTTP 헤더 주입으로부터 자신을 보호해야합니다. 사이트를 새로 고칠 때 간단한 FTP를 사용하지 마십시오. 기타 등등 ... 사이트에 악의적 인 코드를 삽입하는 방법에는 여러 가지가 있습니다.

GET 요청은 항상 서비스와 클라이언트가 읽는다는 것을 언급하는 것을 거의 잊었습니다. 서비스에 따르면 이것은 클라이언트가 브라우저에서 URL을 설정하면 리소스 또는 여러 리소스의 표현을 가져야하며 리소스에 대해 POST / PUT / DELETE 메서드를 호출해서는 안됩니다. 예를 들어 GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resource매우 나쁜 솔루션입니다. 그러나 CSRF를 방해하고 싶다면 이것은 매우 기본적인 기술입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.