ng2 получить CSRF токен из cookie опубликовать его в качестве заголовка

потратив 2 полных дня на поиск в интернете и чтение документов и тонны открытых вопросов людей, сталкивающихся с той же проблемой, я все еще не понимаю, как Angular 2 обрабатывает (X-origin) cookies и как получить к ним доступ.

проблема: Back-end отправляет 2 куки с x-csrf-token & JSESSIONID внутри него. Моя задача-сохранить токен csrf в памяти (ng2) и отправить его (только) обратно как заголовок (не cookie) с каждым сообщением в задний конец.

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Set-Cookie: x-csrf-token=8555257a-396f-43ac-8587-c6d489e76026; Path=/app
Set-Cookie: JSESSIONID=73E38392C60370E38FBAF80143ECE212; Path=/app/; HttpOnly
Expires: Thu, 12 Apr 2018 07:49:02 GMT
Cache-Control: max-age=31536000
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 12 Apr 2017 07:49:02 GMT

мое частичное решение: Я создал пользовательский класс RequesstOptions, который расширяет BaseRequestOptions. Добавлены некоторые дополнительные заголовки и установите "withCredentials" как true.

export class MyRequestOptions extends BaseRequestOptions {

  headers: Headers = new Headers({
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  });

  withCredentials = true;
}

в моем HttpService я делаю сообщение и получаю так:

@Injectable()
export class HttpService {

  constructor(
    protected _http: Http,
    protected requestOptions: RequestOptions
  ) {  }

  get(url): Observable<any> {
    return this._http.get(url, this.requestOptions).map( res => res.json() );
  }

  post(url: string, object: any): Observable<any> {
    return this._http.post(url, object, this.requestOptions).map( res => res.json() );
  }
}

и в мое приложение.модуль я делаю магию так:

 providers: [
    { provide: RequestOptions, useClass: DocumentumDefaultRequestOptions },
    { provide: XSRFStrategy, useFactory: xsrfFactory }
  ],

мой xsrfFactory

export function xsrfFactory() {
  return new CookieXSRFStrategy('x-csrf-token', 'x-csrf-token');
}

мой частичный результат: В этот момент угловой отправляет файл cookie с каждым запросом (GET и POST без дискриминации) с помощью jsessionid и X-csrf-токена следующим образом:

POST /app/business-objects/business-objects-type HTTP/1.1
Host: localhost:8040
Connection: keep-alive
Content-Length: 26
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic ZG1hZG1pbjphZG1pbg==
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/json
Accept: application/json
Referer: http://localhost:4200/page
Cookie: JSESSIONID=B874C9A170EFC12BEB0EDD4266896F2A; x-csrf-token=0717876e-f402-4a1c-a31a-2d60e48509d3

мой миллиард вопросов:

  • как и где я могу получить доступ к X-csrf-токену и как добавить его в мои запросы?
  • что значит CookieXSRFStrategy('x-csrf-token', 'x-csrf-token'); именно. Мне не нравится чувство черного ящика / понять, как это объяснили врачи. Могу ли я получить доступ к данным?

перед отправкой HTTP-запроса CookieXSRFStrategy ищет файл cookie с именем XSRF-TOKEN и задает заголовок X-XSRF-TOKEN со значением этого файла cookie.

  • он не устанавливает заголовок в моем случае ... но почему ?

  • прямо сейчас я отправляю cookie на бэкэнд с sessionid и токеном csrf, но что отправляет его? CookieXSRFStrategy или "withCredentials" флаг.

пожалуйста, не отвечайте одним лайнером, как " документ.печенье." Данные бесполезны без метаданных

2 ответов


обновление для angular 5.0+

Http сервис beign устарел в пользу HttpClient, the CookieXSRFStrategy класс также устарел, теперь эта миссия делегирована HttpClientXsrfModule класса. Если вы хотите настроить имена заголовков и файлов cookie, вам просто нужно импортировать этот модуль следующим образом:

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withConfig({
      cookieName: 'My-Xsrf-Cookie',
      headerName: 'My-Xsrf-Header',
    }),
  ]
})
export class MyModule{}

для будущих читателей:

Ajax ответ печенье

вы не можете получить доступ к cookie из любого ответа Ajax, если вы проверяете как XHR spec, вы заметите, что любой доступ к заголовку, соответствующему "Set-Cookie", запрещен:

имя запрещенного заголовка ответа - это имя заголовка, которое не учитывает байт-регистр для одного из:

  • Set-Cookie
  • Set-Cookie2

но мое печенье не httpOnly

хорошо для вас, но httpOnly только заявляет, что ваш cookie не может быть доступен через document.cookie (см. дальнейший.)

на document.cookie API

единственные файлы cookie, к которым вы сможете получить доступ с помощью javascript, это document.cookie но document.cookie относится к файл cookie, который был отправлен вместе с документом (страница, на которой работает ваш скрипт) и не будет изменен в любое время. MDN четко заявляет, что ссылается на текущий документ:

Document.cookie

получить и установить куки, связанные с текущим документ. Для общей библиотеки см. Эту простую структуру cookie.

источник : MDN

любой файл cookie, установленный ответом Ajax, не принадлежит текущему документу.

как мне реализовать защиту csrf ?

на cookie для защиты токена заголовка - это путь. Обратите внимание, что токен, который вы отправите, будет одинаковым в течение всего сеанса, и это не так предполагается изменить в соответствии с дикими Ajax-запросами, отправляющими cookies.

веб-приложения, использующие JavaScript для большинства своих операций, могут использовать метод анти-CSRF, основанный на политике того же происхождения:

  • при входе в систему веб-приложение устанавливает cookie, содержащий случайный токен, который остается неизменным для всего сеанса пользователя

    Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
    
  • JavaScript, работающий на стороне клиента считывает ее значение и копируется в заголовок HTTP отправляется с каждого транзакций запрос

    X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
    
  • сервер проверяет наличие и целостность маркер

безопасность этого метода основана на предположении, что только JavaScript, работающий в одном и том же источнике, сможет прочитать значение cookie. JavaScript, запущенный из файла-изгоя или электронной почты, не сможет прочитать его и скопировать в пользовательский заголовок. Даже хотя файл cookie csrf-token будет автоматически отправлен с запросом rogue, сервер все равно будет ожидать допустимый заголовок X-Csrf-Token.

источник: Википедия: CSRF

С угловым 2+ эта миссия выполняется CookieXSRFStrategy класса.

оригинальный ответ

как и где я могу получить доступ к X-csrf-токену и как добавить его в мои запросы?

используя CookieXSRFStrategy кажется, быть путь, чтобы добавить его к вашему запросу. Для "как", к сожалению, ответ может быть" вы не можете " (см. Далее).

что CookieXSRFStrategy('х-ключ CSRF-токен', 'х-ключ CSRF-токен'); именно. Мне не нравится чувство черного ящика / понять, как это объяснили врачи.

CookieXSRFStrategy

/**
 * `XSRFConfiguration` sets up Cross Site Request Forgery (XSRF) protection for the application
 * using a cookie. See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
 * for more information on XSRF.
 *
 * Applications can configure custom cookie and header names by binding an instance of this class
 * with different `cookieName` and `headerName` values. See the main HTTP documentation for more
 * details.
 *
 * @experimental
 */
export class CookieXSRFStrategy implements XSRFStrategy {
  constructor(
      private _cookieName: string = 'XSRF-TOKEN', private _headerName: string = 'X-XSRF-TOKEN') {}

  configureRequest(req: Request): void {
    const xsrfToken = getDOM().getCookie(this._cookieName);
    if (xsrfToken) {
      req.headers.set(this._headerName, xsrfToken);
    }
  }
}

источник

в основном, он читает cookie из document.cookie и измените Request заголовки соответственно.

прямо сейчас я отправляю cookie на бэкэнд с sessionid и токеном csrf, но что отправляет его? Флаг CookieXSRFStrategy или 'withCredentials'.

это withCredentials флаг, этот флаг указывает, что XHR должен отправить все куки, которые были отправлены (даже те, которые ранее были установлены Ajax response, но как cookies, а не заголовки, и нет никакого способа, чтобы изменить это поведение)

он не устанавливает заголовок в моем случае ... но почему ?

файл cookie, о котором вы говорите, не отправляется с документом (index.html), но из другого запроса ajax. Дело в том, что вы не можете получить доступ к cookies, установленным ajax response (посмотреть этот ответ), потому что это было бы проблемой безопасности: Простой ajax get on www.stackoverflow.com со случайной веб-страницы получит файл cookie переполнения стека, и злоумышленник может легко украсть его (если Ан Access-Control-Allow-Origin: * заголовок присутствует в ответе stackoverflow).

С другой стороны,document.cookie API может получить доступ только к куки, которые установлены, когда документ был загружен, а не любой другой.

таким образом, вы должны переосмыслить поток связи клиент/сервер на стороне сервера, потому что единственный файл cookie, который вы сможете скопировать в заголовки,-это тот, который был отправлен с документом, на котором работает ваш скрипт (индекс.формат HTML.)

это не файл cookie httpOnly, поэтому он должен быть доступен с js, даже если это X origin

httpOnly делает файл cookie недоступным для document.cookie API,но как я уже говорил,document.cookie относится к cookie, который был отправлен с документом, а не с помощью Ajax-ответов. Вы можете сделать тысячи вызовов ajax с помощью Set-Cookie заголовок в ответе,document.cookie будет все та же строка, без каких-либо модификация.

решение млрд. долларов

сервер должен отправить только один x-csrf-token cookie, содержащий маркер с документом, и вы должны использовать этот маркер, который будет действителен для всего сеанса для каждого запроса с помощью CookieXSRFStrategy. Почему ? потому что вот как это работает.


Angular имеет встроенную поддержку XSRF см. здесь: https://angular.io/guide/http#security-xsrf-protection

"при выполнении HTTP-запросов перехватчик считывает маркер из файла cookie, по умолчанию XSRF-TOKEN, и устанавливает его как HTTP-заголовок, X-XSRF-TOKEN"

поэтому, если ваш сервер устанавливает cookie с именем XSRF-TOKEN, то он будет работать автоматически! нечего делать на стороне клиента. Если вы хотите назвать свой cookie / header что-то еще, тогда вы можете сделать это:

imports: [
  HttpClientModule,
  HttpClientXsrfModule.withConfig({
    cookieName: 'My-Xsrf-Cookie',
    headerName: 'My-Xsrf-Header',
  }),
]

и если вы используете spring security, он поддерживает угловые соглашения об именах, чтобы вы могли настроить эту сторону сервера:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .and()
            .authorizeRequests()
            ....