Страницы

Поиск по вопросам

Показаны сообщения с ярлыком csrf. Показать все сообщения
Показаны сообщения с ярлыком csrf. Показать все сообщения

четверг, 9 января 2020 г.

Смысл CSRF Token`а

#веб_программирование #token #csrf


Доброго времени суток! 

Изучаю csrf и не могу понять одну вещь, помогите пожалуйста разобраться.
На сайте learn.javascript есть статья по этому поводу. Там сказано:


  Типичный способ защиты сайтов – это «секретный ключ» (secret),
  специальное значение, которое генерируется случайным образом и
  сохраняется в сессии посетителя. Его знает только сервер, посетителю
  мы его даже не будем показывать.
  
  Затем на основе ключа генерируется «токен» (token). Токен делается
  так, чтобы с одной стороны он был отличен от ключа, в частности, может
  быть много токенов для одного ключа, с другой – чтобы было легко
  проверить по токену, сгенерирован ли он на основе данного ключа или
  нет.
  
  Для каждого токена нужно дополнительное случайное значение, которое
  называют «соль» salt.
  
  Формула вычисления токена:
  
  token = salt + ":" + MD5(salt + ":" + secret) Например:
  
  В сессии хранится secret="abcdef", это значение создаётся один раз.
  Для нового токена сгенерируем salt, например пусть salt="1234". token
  = "1234" + ":" + MD5("1234" + ":" + "abcdef") = "1234:5ad02792a3285252e524ccadeeda3401".
Это значение – с одной
  стороны, случайное, с другой – имея такой token, мы можем взять его
  первую часть 1234 в качестве salt и, зная secret, проверить по
  формуле, верно ли он вычислен.
  
  Не зная secret, невозможно сгенерировать token, который сервер
  воспримет как правильный.
  
  Далее, токен добавляется в качестве скрытого поля к каждой форме,
  генерируемой на сервере.При её отправке сервер проверит поле csrf,
  удостоверится в правильности токена, и лишь после этого отошлёт
  сообщение.
  
  «Злая страница» при всём желании не сможет сгенерировать подобную
  форму, так как не владеет secret, и токен будет неверным.


Мне непонятно вот что:


secret хранится в сессии, а сессия хранится в куках на клиентской машине, и каждый
раз отправляется веб серверу. Разве хакер не сможет получить доступ к кукам? К ним
вообще можно же получить доступ? Они хранятся же в файле где-то?
Если токен приходит каждый раз с формой, разве хакер не сможет этот токен из html
который пришел выдрать, заюзать его и отправить для своего запроса?
Я так понимаю смысл всей этой ерунды есть только тогда, когда либо


Сайт разрешает кросс-доменные запросы от всех Origin: *
либо
Злоумышленник мог ранее уже встроить форму на тот же сайт через XSS, чтобы оставаться
в том же домене, так как во всех остальных случаях у нас есть CORS, зачем нам CSRF?

Не понял, почему формула токена именно такая:

token = "1234" + ":" + MD5("1234" + ":" + "abcdef") = "1234:5ad02792a3285252e524ccadeeda3401"


Зачем нужно соль "1234" склеивать со сгенерированной md5-функцией строкой? Ведь соль
1234 становится явно видна в токене!
Т.е. получается бразузер шлет каждый раз "abcdef" в куках как Id-сесии, и также шлет
токен. Сервер еще раз генерирует токен, используя этот id сессии, и сверяет с тем,
что пришел от формы? Не совсем ясно, как это помогает, если токен можно подглядеть(если
можно?)


Спасибо заранее за помощь!
Отличие от данного вопроса в том, что там так и не понятно, зачем нужен CSRF если
есть CORS
    


Ответы

Ответ 1



secret хранится в сессии, а сессия хранится в куках на клиентской машине, и каждый раз отправляется веб серверу. Разве хакер не сможет получить доступ к кукам? К ним вообще можно же получить доступ? Они хранятся же в файле где-то? Только если у него есть доступ к компьютеру пользователя. Но тогда ему вообще CSRF не нужен, т. к. есть куда более простые способы отправить запрос самому. Если токен приходит каждый раз с формой, разве хакер не сможет этот токен из html который пришел выдрать, заюзать его и отправить для своего запроса? Только в случае MITM. При CSRF у хакера нет доступа к серверу, клиенту или каналу связи. я имею в виду разве он не может получить форму просто еще раз скриптом и этот токен потом отправить в своем запросе? Не может - тут сработает CORS. Если запрос get, то сервер его получит, но если он не пошлёт разрешающие заголовки, то запрашивающему скрипту браузер ответ не отдаст. А на POST вообще будет OPTIONS-запрос сначала. А если послать запрос не из браузера, а с сервера, то не будет авторизации пользователя и токен он опять же не получит. Я так понимаю смысл всей этой ерунды есть только тогда, когда либо Сайт разрешает кросс-доменные запросы от всех Origin: * либо ... Нет. Браузер позволит отправит форму, вот:
Не понял, почему формула токена именно такая: Форма может быть разной. Зачем нужно соль "1234" склеивать со сгенерированной md5-функцией строкой? Ведь соль 1234 становится явно видна в токене! Если соли нет, то ключ одного клиента подойдёт другому и хакер сможет использовать свой, поэтому нужна соль. Хм.. А вообще, что-то я задумался, есть тут сомнения... Разобрался: даже если хакер встроит свой валидный токен в форму, он не совпадёт с токеном в куках, поэтому форма будет отвергнута как поддельная. он не совпадет, потому что хакеру неизвестен id сессии, на основе котороой генерируется токен? Я имел в виду, что у хакера может быть свой токен, который корректен для него. Но этот токен не совпадёт с тем токеном, который лежит у другого пользователя в куках. А из чужих кук он его достать не может. Т.е. получается бразузер шлет каждый раз "abcdef" в куках как Id-сесии, и также шлет токен. Сервер еще раз генерирует токен, используя этот id сессии, и сверяет с тем, что пришел от формы? Не совсем ясно, как это помогает, если токен можно подглядеть(если можно?) Сайт не может залезть в чужие куки, поэтому подсмотреть нельзя.

Ответ 2



Зачем нужно соль "1234" склеивать со сгенерированной md5-функцией строкой? Ведь соль 1234 становится явно видна в токене! А ее и не нужно прятать. Она не для прямой секретности, а для двух других целей: Если результирующий хэш берется от пароля в чистом виде, то этот пароль можно подгадать по словарю. Случайная соль гарантирует, что по словарю этот секрет подобрать не удастся, а сгенерированный для одной соли словарь не подойдет для другой. В этом случае, конечно, предполагается что секрет у каждого клиента свой, иначе в случае подбора конкретного хэша есть риск, что там совпадет не только хэш, но и исходные данные, и тогда секрет будет известен атакующему Ресурсы, затрачиваемые хэш-функцией на создание хэша зависят от длины входной последовательности. Искусственно увеличивая входную последовательность, защищающийся повышает стоимость подбора совпадений для атакующего, потому что из-за маловероятности подбора коллизии случайным образом атакующий будет пробовать подобрать хэш, используя соль.

пятница, 27 декабря 2019 г.

Кросс-доменный Ajax POST

#jquery #ajax #django #csrf #cors


Добрый день. 
Пытаюсь передать простой текст из min.example.com на api.example.com

var data = $("form").serialize()

$.ajax({
    url: 'http://api.example.com/v1/' + dept,
    type: 'POST',
    crossDomain: true,
    success: function(response){
        alert('ajax complide');  
        alert(response);
    },
    error: function (responseData, textStatus, errorThrown) {
        console.log('POST failed.');
    }
});


Получаю -> [HTTP/1.0 403 FORBIDDEN 1036мс]

Заголовок ответа на мой запрос

X-Frame-Options:    SAMEORIGIN
Server: WSGIServer/0.2 CPython/3.4.3
Date:   Wed, 01 Jul 2015 06:35:49 GMT
Content-Type:   text/html
Access-Control-Allow-Origin:    *
Access-Control-Allow-Methods:   POST,GET,OPTIONS,PUT,DELETE
Access-Control-Allow-Headers:   Content-Type,*
Access-Control-Allow-Credentials:   true

    


Ответы

Ответ 1



Оказалось, что django запрещал POST запросы без прикрепленного к нему специального токена выдаваемого самим django(читайте про CSRF). Если оба сайта написаны на django, то читать здесь на djbook.ru Если же приложение с которого идет POST запрос не django app(в моем случае Yii), то нужно отключить CSRF для view принимающего запрос, написав перед ним(view) @csrf_exempt

воскресенье, 15 декабря 2019 г.

Rails + Devise + Angular2: аутентификация

#ruby_on_rails #безопасность #devise #csrf #angular2


У меня просто огромное количество вопросов... Но перед этим нужно описать, что собственно
происходит.

Преамбула

Я пытаюсь научить Angular2 авторизироваться в Rails 5 beta 3 приложении. При этом
Rails работает в режиме API. Как я понимаю, для того, чтобы Devise работал в режиме
API, нужен gem devise_token_auth. Гем я установил и настроил, но вот после этого начались
проблемы:

Unpermitted parameters: user, session

Первое, что я попробовал сделать после настройки, послать простой POST запрос и посмотреть,
что произойдёт. А произошло следующее: 

Processing by DeviseTokenAuth::SessionsController#create as *json*
  Parameters: {"user"=>{"username"=>"demo", "password"=>"[FILTERED]"}, "session"=>{"user"=>{"username"=>"demo",
"password"=>"[FILTERED]"}}}
Unpermitted parameters: user, session
  Rendered devise_token_auth/sessions/create.json (0.2ms)
Completed 401 Unauthorized in 18ms (Views: 3.2ms | ActiveRecord: 0.0ms)


И вот с этого момента я немного не понимаю, с каких это пор я должен прописывать
permit для параметров, которые ActionController::ParamsWrapper создаёт динамически
в качестве обёртки? Более того:

devise_parameter_sanitizer.permit(:sign_in) do |user_params|
  user_params.permit(:user, :session)
end


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

Authentication header

Покопавшись в документации в поисках решения проблемы я нашёл другую:


  The authentication information should be included by the client in the headers
of each request. The headers follow the RFC 6750 Bearer Token format


Т.е. в заголовке запроса должны быть следующие данные:

"access-token": "wwwww",
"token-type":   "Bearer",
"client":       "xxxxx",
"expiry":       "yyyyy",
"uid":          "zzzzz"


В AngularJS они генерировались модулем ng-token-auth, но как мне сгенерировать их
посредством Angular2, у которого данного модуля нет?



Подводя промежуточный итог (потому что чувствую я, после этих проблем я наткнусь
на их ещё большее количество), вот список моих вопросов:


Как исправить unpermitted parameters: user, session в devise_token_auth?
Как сгенерировать access-token?
Как высчитать expiry и есть ли "rails-way" для времени жизни токена?
Что следует писать в client? Раз уж этот идентификатор позволяет пользователю одновременно
авторизироваться с нескольких устройств, то как он должен выглядить? Что туда следует
записать?





Какие данные следует возвращать клиенту после авторизации (помимо 200)?
Какие ещё подводные камни могут меня ждать при попытке авторизироваться через Rails API?




UPD

Вот запрос, который я шлю:

private _user_session_url = 'auth/sign_in';

login (username: string, password: string) : Observable {
    let body = JSON.stringify({ username: username, password: password });
    // TODO: generate header data.
    let headers = new Headers({
        'Content-Type': 'application/json',
        'accept': 'json'
    });
    let options = new RequestOptions({ headers: headers});

    return this.http.post(this._user_session_url, body, options)
        .map((data: any) => data.json())
        .catch(this.handleError)
}


Как видите, session в теле запроса нет. Его создаёт ActionController::ParamsWrapper

В подтверждение привожу Request Payload:

{"username":"demo","password":"123"}    

    


Ответы

Ответ 1



Согласно краткой документации к devise_token_auth (DTA) POST /sign_in принимает только параметры email и password прямо в корне. То есть, параметры должны выглядеть так: { "email": "foo@example.com", "password": "bar" } Никакой вложенности. Не надо дополнительно настраивать санитайзер и придумывать ненужные ключи. А теперь ответ разом на все оставшиеся вопросы: Token-Type, Access-Token, Client и Expiry. В AngularJS они генерировались модулем ng-token-auth Нет, они генерируются сервером, клиент ничем умным не занимается. Хранятся они в БД (в частично искажённом, но проверяемом виде, как пароли), а обмен ими происходит с помощью заголовков. /sign_in возвращает все интересующие заголовки. Все эти значения вы просто будете передавать туда-сюда (и обновлять, ведь по умолчанию после каждого отдельного запроса токен меняется). Кроме, разве что, Expiry, значением которого можно следить, "когда токен отвалится и потребует обновления", что можно использовать для выкидывания на форму входа. Пользоваться DTA без ng-token-auth можно, но прежде чем за это браться, стоит прочесть документацию к используемым методам. Я пользовался этим гемом ранее и не рекомендую его для случаев, где важна настраиваемость принимаемых и отдаваемых этой системой данных, потому что расширяемость гема близка к нулю.

среда, 10 июля 2019 г.

стандартная проверка токена laravel - странное условие

в Laravel есть класс VerifyCsrfToken, в котором определена функция handle, которая ловит request и проверяет соответствие токена клиента и сервера, после добавляет в response куку, иначе throw error.
public function handle($request, Closure $next) { if ( $this->isReading($request) || //проверяет метод формы на соотв. из массива ['HEAD','GET',...] $this->runningUnitTests() || $this->shouldPassThrough($request) || $this->tokensMatch($request) ){ return $this->addCookieToResponse($request, $next($request)); }
throw new TokenMismatchException; }
Смотрим на условие и видим, что проверяется 4 функции, приоритет при || начинается слева, то есть (здесь скорее всего, я ошибаюсь) у tokenMatch самый маленький приоритет и в случае, если первые 3 функции вернут true, тогда эта функция не будет учитываться в условии, что плохо.
И собственно вопрос, для гуру-laravel, мои рассуждения верны?


Ответ

Главное - да, ваши рассуждения верны. Если любой из первых 3 методов вернёт true, то tokensMatch даже не будет вызываться. Это применимо к любому PHP-коду.
Теперь посмотрим, является ли это ошибкой или так запланировано. Если запланировано, то зачем и не является ли это угрозой.
$this->isReading($request) || //проверяет метод формы на соотв. из массива ['HEAD','GET',...]
Проверяет, является ли текущий запрос читающим. Напомню, что согласно спецификации HTTP запросы типов GET, HEAD не должны изменять состояние системы. А раз они не меняют состояние системы - то их банально и не нужно защищать от CSRF атак.
$this->runningUnitTests() ||
На самом деле костыль, обычно код не должен различать, запущен он в окружении юнит-тестов или нет. Видимо, так было проще. Во всяком случае суть в том, что для запуска из юнит-тестов проверка всегда проходит.
$this->shouldPassThrough($request) ||
Здесь без чтения исходника мне сложно сказать, что проверяется. Исходник Ага, проверяется список исключений для URI, для которых проверку CSRF надо пропустить. По-умолчанию список пуст, но возможность отстрелить себе ногу есть.
$this->tokensMatch($request)
Наконец, если запрос что-то хочет изменить, не в окружении юнит-тестов и не в списке исключений, то проверяем валидность токена.
На мой взгляд всё логично, ошибки в безопасности здесь нет. tokensMatch действительно должен вызываться в самом конце.
Потенциальная проблема с isReading. Многие люди не учитывают семантику GET запросов и делают, например, удаление сущности ссылкой из-за удобства написания простой ссылки, а не целой формы. Не надо так делать.

пятница, 21 июня 2019 г.

CSRF-токен в куках

почему некоторые сайты хранят CSRF-токен в куках?
Ведь если отправить, к примеру, GET-запрос - или я что-то не понимаю??


Ответ

Само по себе это действительно бесполезно, если сервер помнит соответствие токен-пользователь и проверяет только его.
Но в сочетании с передачей такого же токена в параметрах это становится быстрым и простым способом защиты от CSRF: вы ставите пользователю в куки совершенно случайный токен и проверяете, что в параметрах запроса впоследствии приходит точно такой же. Проверяется соответствие запрос-кука
Потенциальный атакующий не сможет достать его из-за Same Origin Policy (можно ещё досыпать сверху HttpOnly, чтобы не получить дыр из-за JS), а потому не сможет его продублировать в запросе. И нет необходимости запоминать что-либо на сервере.

среда, 20 февраля 2019 г.

Смысл CSRF Token`а

Доброго времени суток!
Изучаю csrf и не могу понять одну вещь, помогите пожалуйста разобраться. На сайте learn.javascript есть статья по этому поводу. Там сказано:
Типичный способ защиты сайтов – это «секретный ключ» (secret), специальное значение, которое генерируется случайным образом и сохраняется в сессии посетителя. Его знает только сервер, посетителю мы его даже не будем показывать. Затем на основе ключа генерируется «токен» (token). Токен делается так, чтобы с одной стороны он был отличен от ключа, в частности, может быть много токенов для одного ключа, с другой – чтобы было легко проверить по токену, сгенерирован ли он на основе данного ключа или нет. Для каждого токена нужно дополнительное случайное значение, которое называют «соль» salt. Формула вычисления токена: token = salt + ":" + MD5(salt + ":" + secret) Например: В сессии хранится secret="abcdef", это значение создаётся один раз. Для нового токена сгенерируем salt, например пусть salt="1234". token = "1234" + ":" + MD5("1234" + ":" + "abcdef") = "1234:5ad02792a3285252e524ccadeeda3401". Это значение – с одной стороны, случайное, с другой – имея такой token, мы можем взять его первую часть 1234 в качестве salt и, зная secret, проверить по формуле, верно ли он вычислен. Не зная secret, невозможно сгенерировать token, который сервер воспримет как правильный. Далее, токен добавляется в качестве скрытого поля к каждой форме, генерируемой на сервере.При её отправке сервер проверит поле csrf, удостоверится в правильности токена, и лишь после этого отошлёт сообщение. «Злая страница» при всём желании не сможет сгенерировать подобную форму, так как не владеет secret, и токен будет неверным.
Мне непонятно вот что:
secret хранится в сессии, а сессия хранится в куках на клиентской машине, и каждый раз отправляется веб серверу. Разве хакер не сможет получить доступ к кукам? К ним вообще можно же получить доступ? Они хранятся же в файле где-то? Если токен приходит каждый раз с формой, разве хакер не сможет этот токен из html который пришел выдрать, заюзать его и отправить для своего запроса? Я так понимаю смысл всей этой ерунды есть только тогда, когда либо
Сайт разрешает кросс-доменные запросы от всех Origin: * либо Злоумышленник мог ранее уже встроить форму на тот же сайт через XSS, чтобы оставаться в том же домене, так как во всех остальных случаях у нас есть CORS, зачем нам CSRF? Не понял, почему формула токена именно такая:
token = "1234" + ":" + MD5("1234" + ":" + "abcdef") = "1234:5ad02792a3285252e524ccadeeda3401"
Зачем нужно соль "1234" склеивать со сгенерированной md5-функцией строкой? Ведь соль 1234 становится явно видна в токене! Т.е. получается бразузер шлет каждый раз "abcdef" в куках как Id-сесии, и также шлет токен. Сервер еще раз генерирует токен, используя этот id сессии, и сверяет с тем, что пришел от формы? Не совсем ясно, как это помогает, если токен можно подглядеть(если можно?)
Спасибо заранее за помощь! Отличие от данного вопроса в том, что там так и не понятно, зачем нужен CSRF если есть CORS


Ответ

secret хранится в сессии, а сессия хранится в куках на клиентской машине, и каждый раз отправляется веб серверу. Разве хакер не сможет получить доступ к кукам? К ним вообще можно же получить доступ? Они хранятся же в файле где-то?
Только если у него есть доступ к компьютеру пользователя. Но тогда ему вообще CSRF не нужен, т. к. есть куда более простые способы отправить запрос самому.
Если токен приходит каждый раз с формой, разве хакер не сможет этот токен из html который пришел выдрать, заюзать его и отправить для своего запроса?
Только в случае MITM. При CSRF у хакера нет доступа к серверу, клиенту или каналу связи.
я имею в виду разве он не может получить форму просто еще раз скриптом и этот токен потом отправить в своем запросе?
Не может - тут сработает CORS. Если запрос get, то сервер его получит, но если он не пошлёт разрешающие заголовки, то запрашивающему скрипту браузер ответ не отдаст. А на POST вообще будет OPTIONS-запрос сначала.
А если послать запрос не из браузера, а с сервера, то не будет авторизации пользователя и токен он опять же не получит.
Я так понимаю смысл всей этой ерунды есть только тогда, когда либо Сайт разрешает кросс-доменные запросы от всех Origin: * либо ...
Нет. Браузер позволит отправит форму, вот:


Не понял, почему формула токена именно такая:
Форма может быть разной.
Зачем нужно соль "1234" склеивать со сгенерированной md5-функцией строкой? Ведь соль 1234 становится явно видна в токене!
Если соли нет, то ключ одного клиента подойдёт другому и хакер сможет использовать свой, поэтому нужна соль.
Хм.. А вообще, что-то я задумался, есть тут сомнения...
Разобрался: даже если хакер встроит свой валидный токен в форму, он не совпадёт с токеном в куках, поэтому форма будет отвергнута как поддельная.
он не совпадет, потому что хакеру неизвестен id сессии, на основе котороой генерируется токен?
Я имел в виду, что у хакера может быть свой токен, который корректен для него. Но этот токен не совпадёт с тем токеном, который лежит у другого пользователя в куках. А из чужих кук он его достать не может.
Т.е. получается бразузер шлет каждый раз "abcdef" в куках как Id-сесии, и также шлет токен. Сервер еще раз генерирует токен, используя этот id сессии, и сверяет с тем, что пришел от формы? Не совсем ясно, как это помогает, если токен можно подглядеть(если можно?)
Сайт не может залезть в чужие куки, поэтому подсмотреть нельзя.

суббота, 27 октября 2018 г.

Rails + Devise + Angular2: аутентификация

У меня просто огромное количество вопросов... Но перед этим нужно описать, что собственно происходит.
Преамбула
Я пытаюсь научить Angular2 авторизироваться в Rails 5 beta 3 приложении. При этом Rails работает в режиме API. Как я понимаю, для того, чтобы Devise работал в режиме API, нужен gem devise_token_auth. Гем я установил и настроил, но вот после этого начались проблемы:
Unpermitted parameters: user, session
Первое, что я попробовал сделать после настройки, послать простой POST запрос и посмотреть, что произойдёт. А произошло следующее:
Processing by DeviseTokenAuth::SessionsController#create as *json* Parameters: {"user"=>{"username"=>"demo", "password"=>"[FILTERED]"}, "session"=>{"user"=>{"username"=>"demo", "password"=>"[FILTERED]"}}} Unpermitted parameters: user, session Rendered devise_token_auth/sessions/create.json (0.2ms) Completed 401 Unauthorized in 18ms (Views: 3.2ms | ActiveRecord: 0.0ms)
И вот с этого момента я немного не понимаю, с каких это пор я должен прописывать permit для параметров, которые ActionController::ParamsWrapper создаёт динамически в качестве обёртки? Более того:
devise_parameter_sanitizer.permit(:sign_in) do |user_params| user_params.permit(:user, :session) end
ничего не меняет. Кто-нибудь сталкивался с подобным ранее?
Authentication header
Покопавшись в документации в поисках решения проблемы я нашёл другую:
The authentication information should be included by the client in the headers of each request. The headers follow the RFC 6750 Bearer Token format
Т.е. в заголовке запроса должны быть следующие данные:
"access-token": "wwwww", "token-type": "Bearer", "client": "xxxxx", "expiry": "yyyyy", "uid": "zzzzz"
В AngularJS они генерировались модулем ng-token-auth, но как мне сгенерировать их посредством Angular2, у которого данного модуля нет?

Подводя промежуточный итог (потому что чувствую я, после этих проблем я наткнусь на их ещё большее количество), вот список моих вопросов:
Как исправить unpermitted parameters: user, session в devise_token_auth? Как сгенерировать access-token? Как высчитать expiry и есть ли "rails-way" для времени жизни токена? Что следует писать в client? Раз уж этот идентификатор позволяет пользователю одновременно авторизироваться с нескольких устройств, то как он должен выглядить? Что туда следует записать?

Какие данные следует возвращать клиенту после авторизации (помимо 200)? Какие ещё подводные камни могут меня ждать при попытке авторизироваться через Rails API?

UPD
Вот запрос, который я шлю:
private _user_session_url = 'auth/sign_in';
login (username: string, password: string) : Observable { let body = JSON.stringify({ username: username, password: password }); // TODO: generate header data. let headers = new Headers({ 'Content-Type': 'application/json', 'accept': 'json' }); let options = new RequestOptions({ headers: headers});
return this.http.post(this._user_session_url, body, options) .map((data: any) => data.json()) .catch(this.handleError) }
Как видите, session в теле запроса нет. Его создаёт ActionController::ParamsWrapper
В подтверждение привожу Request Payload:
{"username":"demo","password":"123"}


Ответ

Согласно краткой документации к devise_token_auth (DTA) POST /sign_in принимает только параметры email и password прямо в корне. То есть, параметры должны выглядеть так:
{ "email": "foo@example.com", "password": "bar" }
Никакой вложенности. Не надо дополнительно настраивать санитайзер и придумывать ненужные ключи.

А теперь ответ разом на все оставшиеся вопросы:
Token-Type, Access-Token, Client и Expiry
В AngularJS они генерировались модулем ng-token-auth
Нет, они генерируются сервером, клиент ничем умным не занимается. Хранятся они в БД (в частично искажённом, но проверяемом виде, как пароли), а обмен ими происходит с помощью заголовков. /sign_in возвращает все интересующие заголовки. Все эти значения вы просто будете передавать туда-сюда (и обновлять, ведь по умолчанию после каждого отдельного запроса токен меняется).
Кроме, разве что, Expiry, значением которого можно следить, "когда токен отвалится и потребует обновления", что можно использовать для выкидывания на форму входа.
Пользоваться DTA без ng-token-auth можно, но прежде чем за это браться, стоит прочесть документацию к используемым методам.

Я пользовался этим гемом ранее и не рекомендую его для случаев, где важна настраиваемость принимаемых и отдаваемых этой системой данных, потому что расширяемость гема близка к нулю.