Как работает HttpOnly cookie и почему к нему нельзя достучаться с JavaScript
Что проверяют
Понимаете ли вы, от чего HttpOnly защищает, а от чего нет. Следом обычно спрашивают, где тогда хранить токен авторизации в SPA и спасает ли HttpOnly от CSRF.
Как отвечать
HttpOnly это атрибут, который сервер ставит в заголовке ответа: Set-Cookie: sid=abc; HttpOnly; Secure; SameSite=Lax; Path=/. Браузер сохраняет куку и сам прикладывает её к подходящим запросам в заголовке Cookie, но скриптам страницы она недоступна:
document.cookie: возвращает только куки без флага, а создать или перезаписать HttpOnly-куку через него нельзя;fetchи XHR: заголовокSet-Cookieв ответе входит в список запрещённых, скрипт его не прочитает;- Cookie Store API: такие куки тоже не отдаёт.
Защита держится на браузере, а не на шифровании: кука лежит в его хранилище, просто движок не пускает к ней JavaScript. Смысл в том, что при XSS внедрённый скрипт не сможет вытащить идентификатор сессии и увезти его на свой сервер.
Для фронтенда отсюда следуют практические вещи. На другой origin куки уходят только с credentials: 'include' в fetch или withCredentials: true в axios, а сервер должен ответить Access-Control-Allow-Credentials: true и конкретным origin вместо *. Узнать из кода, залогинен ли пользователь, по такой куке нельзя, для этого заводят эндпоинт вроде /me.
Где спотыкаются
Считают, что HttpOnly закрывает XSS. Скрипт злоумышленника не прочитает куку, но может слать запросы от имени пользователя прямо со страницы, и браузер приложит сессию сам. От XSS защищают экранирование вывода и CSP.
Путают с CSRF. От подделки запросов с чужого сайта защищают SameSite (Chrome без явного атрибута ведёт себя как при Lax) и CSRF-токены, HttpOnly тут ни при чём.
Забывают про Secure. Без него кука уходит и по обычному HTTP, её можно перехватить в сети, и никакой HttpOnly этому не помешает.