Ми відкриваємо API, який партнери можуть використовувати лише в доменах, які вони зареєстрували у нас. Його вміст частково є загальнодоступним (але бажано лише показуватися у відомих нам доменах), але переважно є приватним для наших користувачів. Так:
Щоб визначити, що відображається, наш користувач повинен увійти з нами, але це обробляється окремо.
Для визначення місця відображення даних використовується відкритий ключ API для обмеження доступу до відомих нам доменів, і перш за все для того, щоб приватні користувацькі дані не були вразливими до CSRF .
Цей ключ API дійсно видимий для всіх, ми не автентифікуємо свого партнера іншим способом, і нам не потрібен REFERER . Все-таки це безпечно:
Коли наш get-csrf-token.js?apiKey=abc123
запит:
Знайдіть ключ abc123
у базі даних та отримайте список дійсних доменів для цього ключа.
Шукайте файл cookie для перевірки CSRF. Якщо його не існує, генеруйте захищене випадкове значення та покладіть його в cookie - сеанс, призначений лише для HTTP . Якщо файл cookie існував, отримайте наявне випадкове значення.
Створіть маркер CSRF з ключа API та випадкове значення з файлу cookie та підпишіть його . (Замість того, щоб зберігати список жетонів на сервері, ми підписуємо значення. Обидва значення будуть читабельні у підписаному маркері, це добре.)
Встановіть відповідь не кешована, додайте файл cookie та поверніть сценарій, наприклад:
var apiConfig = apiConfig || {};
if(document.domain === 'expected-domain.com'
|| document.domain === 'www.expected-domain.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Примітки:
Вищезазначене не заважає скрипту на стороні сервера підробляти запит, а лише гарантує, що домен збігається за запитом браузера.
Та сама політика щодо JavaScript забезпечує, що браузер не може використовувати XHR (Ajax) для завантаження та перевірки джерела JavaScript. Натомість звичайний браузер може завантажувати його лише за допомогою <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">
(або динамічного еквівалента), а потім запускає код. Звичайно, ваш сервер не повинен підтримувати крос-походження ресурсів та JSONP для створеного JavaScript.
Сценарій браузера може змінити значення, document.domain
перш ніж завантажити вищезазначений сценарій. Але та ж політика походження тільки дозволяє скоротити домен шляхом видалення префіксів, як переписування subdomain.example.com
просто example.com
, або myblog.wordpress.com
на wordpress.com
, або в деяких браузерах , навіть bbc.co.uk
в co.uk
.
Якщо файл JavaScript вибирається за допомогою певного сценарію на стороні сервера, сервер також отримує файл cookie. Однак сторонній сервер не може змусити браузер користувача пов’язувати це cookie з нашим доменом. Отже, файли cookie CSRF та файли перевірки, отримані за допомогою серверного скрипту, можуть використовуватися лише при наступних викликах на стороні сервера, а не в браузері. Однак такі дзвінки на стороні сервера ніколи не включають файли cookie користувача, а отже, можуть отримувати лише загальнодоступні дані. Це ті самі дані, які скрипт на стороні сервера може викреслити безпосередньо з веб-сайту партнера.
Коли користувач входить у систему, встановіть певне печиво користувача будь-яким способом. (Користувач, можливо, вже увійшов до введення в систему JavaScript.)
Усі наступні запити API на сервер (включаючи запити GET та JSONP) повинні містити маркер CSRF, файл cookie для перевірки CSRF та (якщо увійшов у систему) файл cookie користувача. Тепер сервер може визначити, чи потрібно довіряти запиту:
Наявність дійсного маркера CSRF забезпечує завантаження JavaScript з очікуваного домену, якщо він завантажений браузером.
Наявність маркера CSRF без файлу cookie підтверджує підробку.
Наявність як маркера CSRF, і файлу cookie для перевірки CSRF нічого не забезпечує: це може бути або запит на стороні підробленого сервера, або дійсний запит від браузера. (Це не може бути запит браузера, зроблений із непідтримуваного домену.)
Наявність файлу cookie користувача гарантує, що користувач увійшов у систему, але не гарантує, що він є членом даного партнера, а також, що користувач переглядає правильний веб-сайт.
Наявність файлу cookie користувача без файлу cookie для перевірки CSRF вказує на підробку.
Наявність файлу cookie користувача забезпечує поточний запит через браузер. (Передбачається , що користувач не вводитиме свої облікові дані на невідомому сайті, і припускаючи , що ми не дбаємо про користувачів , використовуючи свої власні облікові дані , щоб зробити запит який - то на боці сервера.) Якщо ми також є куки перевірки CSRF, то , що перевірка CSRF печиво було також отриманий за допомогою браузера. Далі, якщо у нас також є маркер CSRF з дійсним підписом, івипадкове число у файлі cookie валідації CSRF відповідає номеру цього маркера CSRF, тоді JavaScript для цього маркера також був отриманий під час того самого самого попереднього запиту, під час якого було встановлено файл cookie CSRF, отже, також за допомогою браузера. Це також означає, що вищевказаний код JavaScript був виконаний до встановлення маркера, і що в той час домен був дійсним для даного ключа API.
Отже: сервер тепер може безпечно використовувати ключ API з підписаного маркера.
Якщо в будь-який момент сервер не довіряє запиту, то повертається 403 Заборонено. Віджет може відповісти на це, показавши попередження користувачеві.
Підписувати файли cookie для перевірки CSRF не потрібно, оскільки ми порівнюємо його з підписаним маркером CSRF. Якщо не підписувати файл cookie, кожен запит HTTP стає коротшим, а перевірка сервера - трохи швидшою.
Створений маркер CSRF дійсний нескінченно, але лише в поєднанні з файлом cookie, тому ефективно, поки браузер не закриється.
Ми могли обмежити термін служби підпису маркера. Ми можемо видалити файл cookie для перевірки CSRF, коли користувач вийде з системи, щоб відповідати рекомендації OWASP . А щоб не ділити випадкове число на кожного користувача між декількома партнерами, можна додати ключ API до імені файлу cookie. Але навіть тоді неможливо легко оновити файл валідації CSRF, коли запитується новий маркер, оскільки користувачі можуть переглядати той самий сайт у кількох вікнах, обмінюючись одним файлом cookie (який, при оновленні, буде оновлений у всіх вікнах, після чого Маркер JavaScript в інших вікнах більше не буде відповідати цьому одному файлу cookie).
Для тих, хто використовує OAuth, див. Також OAuth та віджети на стороні клієнта , з яких я отримав ідею JavaScript. Для серверного використання API, в якому ми не можемо покластися на код JavaScript для обмеження домену, ми використовуємо секретні ключі замість відкритих ключів API.