ÐелÑÐ·Ñ Ð³Ð¾Ð²Ð¾ÑиÑÑ Ð¿Ñо AJAX и не ÑпомÑнÑÑÑ Ð¿Ñо важнейÑÑÑ Ð´ÐµÑÐ°Ð»Ñ ÐµÐ³Ð¾ ÑеализаÑии â заÑиÑÑ Ð¾Ñ CSRF-аÑак.
CSRF (Cross-Site Request Forgery, Ñакже XSRF) â опаÑнейÑÐ°Ñ Ð°Ñака, коÑоÑÐ°Ñ Ð¿ÑÐ¸Ð²Ð¾Ð´Ð¸Ñ Ðº ÑомÑ, ÑÑо Ñ Ð°ÐºÐµÑ Ð¼Ð¾Ð¶ÐµÑ Ð²ÑполниÑÑ Ð½Ð° неподгоÑовленном ÑайÑе маÑÑÑ ÑазлиÑнÑÑ Ð´ÐµÐ¹ÑÑвий Ð¾Ñ Ð¸Ð¼ÐµÐ½Ð¸ дÑÑÐ³Ð¸Ñ , заÑегиÑÑÑиÑованнÑÑ Ð¿Ð¾ÑеÑиÑелей.
Ðакие ÑÑо дейÑÑÐ²Ð¸Ñ â оÑпÑавка ли ÑообÑений, пеÑевод денег Ñо ÑÑÑÑа на ÑÑÑÑ Ð¸Ð»Ð¸ Ñмена паÑолей â завиÑÑÑ Ð¾Ñ ÑайÑа, но в лÑбом ÑлÑÑае ÑÑа аÑака Ð²Ñ Ð¾Ð´Ð¸Ñ Ð² обÑазоваÑелÑнÑй минимÑм веб-ÑазÑабоÑÑика.
ÐÐ»Ð°Ñ ÑоÑма
«ÐлаÑÑиÑеÑкий» ÑÑенаÑий аÑаки Ñаков:
-
ÐаÑÑ ÑвлÑеÑÑÑ Ð·Ð°Ð»Ð¾Ð³Ð¸Ð½ÐµÐ½Ð½Ñм на ÑайÑ, допÑÑÑим,
mail.com. У него еÑÑÑ ÑеÑÑÐ¸Ñ Ð² кÑÐºÐ°Ñ . -
ÐаÑÑ Ð¿Ð¾Ð¿Ð°Ð» на «злÑÑ ÑÑÑаниÑÑ», напÑÐ¸Ð¼ÐµÑ Ñ Ð°ÐºÐµÑ Ð¿ÑиглаÑил его ÑделаÑÑ ÑÑо пиÑÑмом или как-Ñо инаÑе.
-
Ðа злой ÑÑÑаниÑе Ð½Ð°Ñ Ð¾Ð´Ð¸ÑÑÑ ÑоÑма Ñакого вида:
<form action="http://mail.com/send" method="POST"> <input type="hidden" name="message" value="СообÑение"> ... </form> -
ÐÑи Ð·Ð°Ñ Ð¾Ð´Ðµ на злÑÑ ÑÑÑаниÑÑ JavaScript вÑзÑваеÑ
form.submit, оÑпÑавлÑÑ Ñаким обÑазом ÑоÑÐ¼Ñ Ð½Ð°mail.com. -
СайÑ
mail.comпÑовеÑÑÐµÑ ÐºÑки, видиÑ, ÑÑо поÑеÑиÑÐµÐ»Ñ Ð°Ð²ÑоÑизован и обÑабаÑÑÐ²Ð°ÐµÑ ÑоÑмÑ. Рданном пÑимеÑе ÑоÑма пÑÐµÐ´Ð¿Ð¾Ð»Ð°Ð³Ð°ÐµÑ Ð¿Ð¾ÑÑÐ»ÐºÑ ÑообÑениÑ.
ÐÑог аÑаки â ÐаÑÑ, Ð·Ð°Ð¹Ð´Ñ Ð½Ð° злÑÑ ÑÑÑаниÑÑ, ненаÑоком оÑпÑавил пиÑÑмо Ð¾Ñ Ñвоего имени. СодеÑжимое пиÑÑма ÑÑоÑмиÑовано Ñ Ð°ÐºÐµÑом.
ÐаÑиÑа
РпÑимеÑе вÑÑе аÑака иÑполÑзовала Ñлабое звено авÑоÑизаÑии.
ÐÑки позволÑÑÑ ÑайÑÑ mail.com пÑовеÑиÑÑ, ÑÑо пÑиÑÑл именно ÐаÑÑ, но ниÑего не говоÑÑÑ Ð¿Ñо даннÑе, коÑоÑÑе он оÑпÑавлÑеÑ.
ÐнаÑе говоÑÑ, кÑки не гаÑанÑиÑÑÑÑ, ÑÑо ÑоÑÐ¼Ñ Ñоздал именно ÐаÑÑ. Ðни ÑолÑко ÑдоÑÑовеÑÑÑÑ Ð»Ð¸ÑноÑÑÑ, но не даннÑе.
ТипиÑнÑй ÑпоÑоб заÑиÑÑ ÑайÑов â ÑÑо «ÑекÑеÑнÑй клÑÑ» (secret), ÑпеÑиалÑное знаÑение, коÑоÑое генеÑиÑÑеÑÑÑ ÑлÑÑайнÑм обÑазом пÑи авÑоÑизаÑии и ÑоÑ
ÑанÑеÑÑÑ Ð² ÑеÑÑии поÑеÑиÑелÑ. Ðго Ð·Ð½Ð°ÐµÑ ÑолÑко ÑеÑвеÑ, поÑеÑиÑÐµÐ»Ñ Ð¼Ñ ÐµÐ³Ð¾ даже не бÑдем показÑваÑÑ.
РазÑмееÑÑÑ, Ð´Ð»Ñ ÑазнÑÑ
поÑеÑиÑелей secret бÑÐ´ÐµÑ ÑазнÑм.
ÐаÑем на оÑнове клÑÑа генеÑиÑÑеÑÑÑ Â«Ñокен» (token). Токен делаеÑÑÑ Ñак, ÑÑÐ¾Ð±Ñ Ñ Ð¾Ð´Ð½Ð¾Ð¹ ÑÑоÑÐ¾Ð½Ñ Ð¾Ð½ бÑл оÑлиÑен Ð¾Ñ ÐºÐ»ÑÑа secret, в ÑаÑÑноÑÑи, Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¼Ð½Ð¾Ð³Ð¾ Ñокенов Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ клÑÑа, Ñ Ð´ÑÑгой â ÑÑÐ¾Ð±Ñ Ð±Ñло легко пÑовеÑиÑÑ Ð¿Ð¾ ÑокенÑ, ÑгенеÑиÑован ли он на оÑнове данного клÑÑа или неÑ.
ÐÐ»Ñ ÑÑого Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ Ñокена иÑполÑзÑеÑÑÑ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸ÑелÑное ÑлÑÑайное знаÑение, коÑоÑое назÑваÑÑ Â«ÑолÑ» salt.
ФоÑмÑла вÑÑиÑÐ»ÐµÐ½Ð¸Ñ Ñокена:
token = salt + ":" + MD5(salt + ":" + secret)
ÐапÑимеÑ:
- Ð ÑеÑÑии Ñ
ÑаниÑÑÑ
secret="abcdef", ÑÑо знаÑение ÑоздаÑÑÑÑ Ð¾Ð´Ð¸Ð½ Ñаз. - ÐÐ»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ñокена ÑгенеÑиÑÑем
salt, напÑÐ¸Ð¼ÐµÑ Ð¿ÑÑÑÑsalt="1234". token = "1234" + ":" + MD5("1234" + ":" + "abcdef") = "1234:5ad02792a3285252e524ccadeeda3401".
ÐÑо знаÑение â Ñ Ð¾Ð´Ð½Ð¾Ð¹ ÑÑоÑонÑ, ÑлÑÑайное, Ñ Ð´ÑÑгой â полÑÑив Ñакой token Ð¾Ñ Ð¿Ð¾Ð»ÑзоваÑелÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ его пÑовеÑиÑÑ: ÑазбиÑÑ Ð¿Ð¾ ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ Ð´Ð²Ð¾ÐµÑоÑиÑ, взÑÑÑ ÐµÐ³Ð¾ левÑÑ ÑаÑÑÑ 1234 в каÑеÑÑве salt и, Ð·Ð½Ð°Ñ secret, запÑÑÑиÑÑ Ð²ÑÑиÑление по ÑоÑмÑле вÑÑе. ÐÑли Ð¼Ñ Ð¿Ð¾Ð»ÑÑили Ñо же Ñамое, Ñо token пÑавилÑнÑй, ÑÑо не Ñ
акеÑ.
ÐбÑаÑим внимание: не Ð·Ð½Ð°Ñ secret, невозможно ÑгенеÑиÑоваÑÑ token, коÑоÑÑй ÑеÑÐ²ÐµÑ Ð²Ð¾ÑпÑÐ¸Ð¼ÐµÑ ÐºÐ°Ðº пÑавилÑнÑй.
Ðалее, Ñокен добавлÑеÑÑÑ Ð² каÑеÑÑве ÑкÑÑÑого Ð¿Ð¾Ð»Ñ Ðº каждой ÑоÑме, генеÑиÑÑемой на ÑеÑвеÑе.
То еÑÑÑ, «ÑеÑÑнаÑ» ÑоÑма Ð´Ð»Ñ Ð¾ÑÑÑлки ÑообÑений, ÑÐ¾Ð·Ð´Ð°Ð½Ð½Ð°Ñ Ð½Ð° http://mail.com, бÑÐ´ÐµÑ Ð²ÑглÑдеÑÑ Ñак:
<form action="http://mail.com/send" method="POST">
<input type="hidden" name="csrf" value="1234:5ad02792a3285252e524ccadeeda3401">
<textarea name="message">
...
</textarea>
</form>
ÐÑи ÐµÑ Ð¾ÑпÑавке ÑеÑÐ²ÐµÑ Ð¿ÑовеÑÐ¸Ñ Ð¿Ð¾Ð»Ðµ csrf, ÑдоÑÑовеÑиÑÑÑ Ð² пÑавилÑноÑÑи Ñокена, и лиÑÑ Ð¿Ð¾Ñле ÑÑого оÑоÑлÑÑ ÑообÑение.
«ÐÐ»Ð°Ñ ÑÑÑаниÑа» пÑи вÑÑм желании не ÑÐ¼Ð¾Ð¶ÐµÑ ÑгенеÑиÑоваÑÑ Ð¿Ð¾Ð´Ð¾Ð±Ð½ÑÑ ÑоÑмÑ, Ñак как не Ð²Ð»Ð°Ð´ÐµÐµÑ secret, и Ñокен бÑÐ´ÐµÑ Ð½ÐµÐ²ÐµÑнÑм.
Такой Ñокен Ñакже назÑваÑÑ Â«Ð¿Ð¾Ð´Ð¿Ð¸ÑÑÑ» ÑоÑмÑ, коÑоÑÐ°Ñ ÑдоÑÑовеÑÑеÑ, ÑÑо ÑоÑма ÑгенеÑиÑована именно на ÑеÑвеÑе.
ÐÑа подпиÑÑ Ð³Ð¾Ð²Ð¾ÑÐ¸Ñ Ð¾ Ñом, ÑÑо авÑÐ¾Ñ ÑоÑÐ¼Ñ â ÑеÑвеÑ, но ниÑего не гаÑанÑиÑÑÐµÑ Ð¾ÑноÑиÑелÑно ÐµÑ ÑодеÑжаниÑ.
ÐÑÑÑ ÑиÑÑаÑии, когда Ð¼Ñ Ñ Ð¾Ñим бÑÑÑ ÑвеÑенÑ, ÑÑо некоÑоÑÑе из полей ÑоÑÐ¼Ñ Ð¿Ð¾ÑеÑиÑÐµÐ»Ñ Ð½Ðµ изменил ÑамоволÑно. Тогда Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ вклÑÑиÑÑ Ð² MD5 Ð´Ð»Ñ ÑоÑмÑÐ»Ñ Ñокена ÑÑи полÑ, напÑимеÑ:
token = salt + ":" + MD5(salt + ":" + secret + ":" + fields.money)
ÐÑи оÑпÑавке ÑоÑÐ¼Ñ ÑеÑÐ²ÐµÑ Ð¿ÑовеÑÐ¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÑ, подÑÑавив в Ð½ÐµÑ Ð¸Ð·Ð²ÐµÑÑнÑй ÐµÐ¼Ñ secret и пÑиÑланное знаÑение fields.money. ÐÑи неÑовпадении либо secret не ÑÐ¾Ñ (Ñ
акеÑ), либо fields.money изменено.
Токен и AJAX
ТепеÑÑ Ð¿ÐµÑейдÑм к AJAX-запÑоÑам.
ЧÑо еÑли поÑÑлка ÑообÑений в наÑем инÑеÑÑейÑе ÑеализÑеÑÑÑ ÑеÑез XMLHttpRequest?
Ðак и в ÑлÑÑае Ñ ÑоÑмой, Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Â«Ð¿Ð¾Ð´Ð¿Ð¸ÑаÑÑ» запÑÐ¾Ñ Ñокеном, ÑÑÐ¾Ð±Ñ Ð³Ð°ÑанÑиÑоваÑÑ, ÑÑо его ÑодеÑжимое пÑиÑлано на ÑеÑÐ²ÐµÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ инÑеÑÑейÑом ÑайÑа, а не «злой ÑÑÑаниÑей».
ÐдеÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ Ð²Ð°ÑианÑÑ, ÑамÑй пÑоÑÑой â ÑÑо дополниÑелÑÐ½Ð°Ñ ÐºÑка.
-
ÐÑи авÑоÑизаÑии ÑеÑÐ²ÐµÑ ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ ÐºÑÐºÑ Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼
CSRF-TOKEN, и пиÑÐµÑ Ð² Ð½ÐµÑ Ñокен. -
Ðод, оÑÑÑеÑÑвлÑÑÑий XMLHttpRequest, полÑÑÐ°ÐµÑ ÐºÑÐºÑ Ð¸ ÑÑÐ°Ð²Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº
X-CSRF-TOKENÑ Ð½ÐµÐ¹:var request = new XMLHttpRequest(); var csrfCookie = document.cookie.match(/CSRF-TOKEN=([\w-]+)/); if (csrfCookie) { request.setRequestHeader("X-CSRF-TOKEN", csrfCookie[1]); } -
СеÑÐ²ÐµÑ Ð¿ÑовеÑÑеÑ, еÑÑÑ Ð»Ð¸ заголовок и ÑодеÑÐ¶Ð¸Ñ Ð»Ð¸ он пÑавилÑнÑй Ñокен.
ÐаÑиÑа дейÑÑвÑÐµÑ Ð¿Ð¾ÑомÑ, ÑÑо пÑоÑиÑаÑÑ ÐºÑÐºÑ Ð¼Ð¾Ð¶ÐµÑ ÑолÑко JavaScript Ñ Ñого же домена. «ÐÐ»Ð°Ñ ÑÑÑаниÑа» не ÑÐ¼Ð¾Ð¶ÐµÑ Â«Ð¿ÐµÑеложиÑÑ» кÑÐºÑ Ð² заголовок.
ÐÑли нÑжно ÑделаÑÑ Ð½Ðµ XMLHttpRequest, а, к пÑимеÑÑ, динамиÑеÑки ÑгенеÑиÑоваÑÑ ÑоÑÐ¼Ñ Ð¸Ð· JavaScript â она Ñакже подпиÑÑваеÑÑÑ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸ÑнÑм обÑазом, ÑкÑÑÑое поле или дополниÑелÑнÑй URL-паÑамеÑÑ Ð³ÐµÐ½ÐµÑиÑÑеÑÑÑ Ð¿Ð¾ кÑке.
ÐÑого
-
CSRF-аÑака â ÑÑо когда Â«Ð·Ð»Ð°Ñ ÑÑÑаниÑа» оÑпÑавлÑÐµÑ ÑоÑÐ¼Ñ Ð¸Ð»Ð¸ запÑÐ¾Ñ Ð½Ð° ÑайÑ, где поÑеÑиÑелÑ, пÑедположиÑелÑно, залогинен.
ÐÑли ÑÐ°Ð¹Ñ Ð¿ÑовеÑÑÐµÑ ÑолÑко кÑки, Ñо он ÑакÑÑ ÑоÑÐ¼Ñ Ð¿ÑинимаеÑ. РделаÑÑ ÑÑо не ÑледÑеÑ, Ñак как ÐµÑ ÑгенеÑиÑовал злой Ñ Ð°ÐºÐµÑ.
-
ÐÐ»Ñ Ð·Ð°ÑиÑÑ Ð¾Ñ Ð°Ñаки ÑоÑмÑ, коÑоÑÑе генеÑиÑÑеÑ
mail.com, подпиÑÑваÑÑÑÑ ÑпеÑиалÑнÑм Ñокеном. Ðожно подпиÑÑваÑÑ Ð½Ðµ вÑе ÑоÑмÑ, а ÑолÑко Ñе, коÑоÑÑе оÑÑÑеÑÑвлÑÑÑ Ð´ÐµÐ¹ÑÑÐ²Ð¸Ñ Ð¾Ñ Ð¸Ð¼ÐµÐ½Ð¸ поÑеÑиÑелÑ, Ñо еÑÑÑ Ð¼Ð¾Ð³ÑÑ ÑлÑжиÑÑ Ð¾Ð±ÑекÑом аÑаки. -
ÐÐ»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñи XMLHttpRequest Ñокен дополниÑелÑно запиÑÑваеÑÑÑ Ð² кÑкÑ. Тогда JavaScript Ñ Ð´Ð¾Ð¼ÐµÐ½Ð°
mail.comÑÐ¼Ð¾Ð¶ÐµÑ Ð¿ÑоÑиÑаÑÑ ÐµÑ Ð¸ добавиÑÑ Ð² заголовок, а ÑеÑÐ²ÐµÑ â пÑовеÑиÑÑ, ÑÑо заголовок еÑÑÑ Ð¸ ÑодеÑÐ¶Ð¸Ñ ÐºÐ¾ÑÑекÑнÑй Ñокен. -
ÐинамиÑеÑки ÑгенеÑиÑованнÑе ÑоÑÐ¼Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÑваÑÑÑÑ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñно: Ñокен из кÑки добавлÑеÑÑÑ ÐºÐ°Ðº URL-паÑамеÑÑ Ð¸Ð»Ð¸ дополниÑелÑное поле.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)