В своей предыдущей компании я немного работал с краулерами и помогал инженерам-краулерам решать некоторые проблемы. Затем я написал несколько статей и разместил их в Интернете.После этого некоторые люди просили меня заняться аутсорсингом поисковых роботов.Контент, вероятно, должен был сканировать пользовательские данные и данные о продуктах Xiaohongshu, но я этого не сделал. Я думаю, что в Китае есть несколько компаний, занимающихся большими данными, которые имеют настоящие большие данные, но сканируют данные с помощью команды инженеров-краулеров, поэтому не думайте, что наши данные бесполезны.Для компаний, основанных на контенте, данные являются надежной конкурентоспособностью. Далее я хочу поговорить о безопасности сети и данных. Для контент-ориентированных компаний важна безопасность данных. Для контент-компаний важность данных невозможно переоценить. Например, если вы являетесь платформой для онлайн-обучения, данные темы очень важны, но они были украдены другими с помощью технологии сканирования? Если убрать основную конкурентоспособность, будет круто. Другой пример: независимый разработчик хочет скопировать ваш продукт, забрать ваши основные данные с помощью захвата пакетов и сканирования, а затем создать веб-сайт и приложение за короткий период времени и стать вашим сильным врагом в краткосрочной перспективе.
задний план
В настоящее время, после анализа веб-страницы в приложении, наша безопасность данных плохая, и есть проблемы в следующих пунктах:
- Данные веб-сайта реализованы за счет самого раннего разделения интерфейса и сервера. Инженеры, немного изучившие веб-интерфейс, могут анализировать веб-сайт с помощью артефакта Chrome, а затем сканировать необходимые данные. Откройте "Сеть" и вы увидите все сетевые запросы сайта. Упс, что я случайно увидел? Да, информацию об интерфейсе веб-сайта можно увидеть. Например, «detail.json?itemId=141529859». Или интерфейс вашего веб-сайта имеет специальную обработку суждений и хранит некоторую информацию в sessionStorage, cookie и localStorage.Инженер-краулер с некоторым опытом работы с интерфейсом думает: «Эй, эй, разве это не чередование данных?» Или какие-то параметры временно генерируются функцией через JavaScript. Это не большая проблема. Инженеры также могут искать элементы веб-страницы, находить идентификатор ключа или имя класса CSS, а затем искать в «Поиске», чтобы найти соответствующий код JS-кода, щелкните, чтобы просмотреть код, если это режим ранней фронтенд-разработки, то Код бежит. Это то же самое, что разработчики видят в своей IDE. Опытные краулеры могут использовать это, чтобы что-то делать. Поэтому проблема безопасности должна быть решена срочно.
Хотите узнать больше советов по отладке для Chrome, см.эта статья
- Даже если данные приложения используют HTTPS, данные могут быть получены напрямую профессиональными инструментами захвата пакетов, поэтому проблемы безопасности приложения также могут быть улучшены.Конкретные стратегии будут обсуждаться ниже. Если вы хотите узнать больше о навыках использования Чарльза, вы можете проверить это.эта статья
рептилия означает
- В настоящее время технология сканирования напрямую находит интересующий узел на отображаемой html-странице, а затем получает соответствующий текст.
- Некоторые веб-сайты хорошо защищены. Например, страницу списка может быть легко получить, но на странице сведений необходимо щелкнуть соответствующий элемент на странице списка, отправить itemId через форму формы, сервер генерирует соответствующие параметры и затем перенаправляет на страницу сведений (повторно после того, как направленный адрес приходит с параметром detailID страницы сведений), этот шаг может перехватить некоторых разработчиков сканеров
решение
упражнятьсяТехническое решение для защиты от сканирования на стороне веб-сайта
С этих двух точек зрения (то, что вы видите на веб-странице, — это не то, что вы получаете, и бесполезно проверять интерфейсный запрос), я сформулировал следующий план защиты от сканирования.
-
Использовать HTTPS-протокол
-
Если количество запросов слишком сильно ограничено в единицу времени, учетная запись будет заблокирована.
-
Ограничения передних технологий (следующая базовая технология)
# 比如需要正确显示的数据为“19950220”
1. 先按照自己需求利用相应的规则(数字乱序映射,比如正常的0对应还是0,但是乱序就是 0 <-> 1,1 <-> 9,3 <-> 8,...)制作自定义字体(ttf)
2. 根据上面的乱序映射规律,求得到需要返回的数据 19950220 -> 17730220
3. 对于第一步得到的字符串,依次遍历每个字符,将每个字符根据按照线性变换(y=kx+b)。线性方程的系数和常数项是根据当前的日期计算得到的。比如当前的日期为“2018-07-24”,那么线性变换的 k 为 7,b 为 24。
4. 然后将变换后的每个字符串用“3.1415926”拼接返回给接口调用者。(为什么是3.1415926,因为对数字伪造反爬,所以拼接的文本肯定是数字的话不太会引起研究者的注意,但是数字长度太短会误伤正常的数据,所以用所熟悉的 Π)
```
1773 -> “1*7+24” + “3.1415926” + “7*7+24” + “3.1415926” + “7*7+24” + “3.1415926” + “3*7+24” -> 313.1415926733.1415926733.141592645
02 -> "0*7+24" + "3.1415926" + "2*7+24" -> 243.141592638
20 -> "2*7+24" + "3.1415926" + "0*7+24" -> 383.141592624
```
# 前端拿到数据后再解密,解密后根据自定义的字体 Render 页面
1. 先将拿到的字符串按照“3.1415926”拆分为数组
2. 对数组的每1个数据,按照“线性变换”(y=kx+b,k和b同样按照当前的日期求解得到),逆向求解到原本的值。
3. 将步骤2的的到的数据依次拼接,再根据 ttf 文件 Render 页面上。
- Серверной части необходимо зашифровать данные в соответствии с протоколом, разработанным на предыдущем шаге.
Ниже сNode.jsВ качестве примера, чтобы объяснить, что должен делать бэкэнд
-
Сначала установите маршрутизацию интерфейса на бэкэнде
-
Получить параметры за маршрутом
-
Создавайте соответствующие данные на основе операторов SQL в соответствии с потребностями бизнеса. Если это цифровая часть, ее необходимо преобразовать в соответствии с методом, согласованным выше.
-
Преобразуйте сгенерированные данные в JSON и верните их вызывающей стороне
// json var JoinOparatorSymbol = "3.1415926"; function encode(rawData, ruleType) { if (!isNotEmptyStr(rawData)) { return ""; } var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var encodeData = ""; for (var index = 0; index < rawData.length; index++) { var datacomponent = rawData[index]; if (!isNaN(datacomponent)) { if (ruleType < 3) { var currentNumber = rawDataMap(String(datacomponent), ruleType); encodeData += (currentNumber * month + day) + JoinOparatorSymbol; } else if (ruleType == 4) { encodeData += rawDataMap(String(datacomponent), ruleType); } else { encodeData += rawDataMap(String(datacomponent), ruleType) + JoinOparatorSymbol; } } else if (ruleType == 4) { encodeData += rawDataMap(String(datacomponent), ruleType); } } if (encodeData.length >= JoinOparatorSymbol.length) { var lastTwoString = encodeData.substring(encodeData.length - JoinOparatorSymbol.length, encodeData.length); if (lastTwoString == JoinOparatorSymbol) { encodeData = encodeData.substring(0, encodeData.length - JoinOparatorSymbol.length); } }
//字体映射处理 function rawDataMap(rawData, ruleType) { if (!isNotEmptyStr(rawData) || !isNotEmptyStr(ruleType)) { return; } var mapData; var rawNumber = parseInt(rawData); var ruleTypeNumber = parseInt(ruleType); if (!isNaN(rawData)) { lastNumberCategory = ruleTypeNumber; //字体文件1下的数据加密规则 if (ruleTypeNumber == 1) { if (rawNumber == 1) { mapData = 1; } else if (rawNumber == 2) { mapData = 2; } else if (rawNumber == 3) { mapData = 4; } else if (rawNumber == 4) { mapData = 5; } else if (rawNumber == 5) { mapData = 3; } else if (rawNumber == 6) { mapData = 8; } else if (rawNumber == 7) { mapData = 6; } else if (rawNumber == 8) { mapData = 9; } else if (rawNumber == 9) { mapData = 7; } else if (rawNumber == 0) { mapData = 0; } } //字体文件2下的数据加密规则 else if (ruleTypeNumber == 0) { if (rawNumber == 1) { mapData = 4; } else if (rawNumber == 2) { mapData = 2; } else if (rawNumber == 3) { mapData = 3; } else if (rawNumber == 4) { mapData = 1; } else if (rawNumber == 5) { mapData = 8; } else if (rawNumber == 6) { mapData = 5; } else if (rawNumber == 7) { mapData = 6; } else if (rawNumber == 8) { mapData = 7; } else if (rawNumber == 9) { mapData = 9; } else if (rawNumber == 0) { mapData = 0; } } //字体文件3下的数据加密规则 else if (ruleTypeNumber == 2) { if (rawNumber == 1) { mapData = 6; } else if (rawNumber == 2) { mapData = 2; } else if (rawNumber == 3) { mapData = 1; } else if (rawNumber == 4) { mapData = 3; } else if (rawNumber == 5) { mapData = 4; } else if (rawNumber == 6) { mapData = 8; } else if (rawNumber == 7) { mapData = 3; } else if (rawNumber == 8) { mapData = 7; } else if (rawNumber == 9) { mapData = 9; } else if (rawNumber == 0) { mapData = 0; } } else if (ruleTypeNumber == 3) { if (rawNumber == 1) { mapData = ""; } else if (rawNumber == 2) { mapData = ""; } else if (rawNumber == 3) { mapData = ""; } else if (rawNumber == 4) { mapData = ""; } else if (rawNumber == 5) { mapData = ""; } else if (rawNumber == 6) { mapData = ""; } else if (rawNumber == 7) { mapData = ""; } else if (rawNumber == 8) { mapData = ""; } else if (rawNumber == 9) { mapData = ""; } else if (rawNumber == 0) { mapData = ""; } } else{ mapData = rawNumber; } } else if (ruleTypeNumber == 4) { var sources = ["年", "万", "业", "人", "信", "元", "千", "司", "州", "资", "造", "钱"]; //判断字符串为汉字 if (/^[\u4e00-\u9fa5]*$/.test(rawData)) { if (sources.indexOf(rawData) > -1) { var currentChineseHexcod = rawData.charCodeAt(0).toString(16); var lastCompoent; var mapComponetnt; var numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; var characters = ["a", "b", "c", "d", "e", "f", "g", "h", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; if (currentChineseHexcod.length == 4) { lastCompoent = currentChineseHexcod.substr(3, 1); var locationInComponents = 0; if (/[0-9]/.test(lastCompoent)) { locationInComponents = numbers.indexOf(lastCompoent); mapComponetnt = numbers[(locationInComponents + 1) % 10]; } else if (/[a-z]/.test(lastCompoent)) { locationInComponents = characters.indexOf(lastCompoent); mapComponetnt = characters[(locationInComponents + 1) % 26]; } mapData = "&#x" + currentChineseHexcod.substr(0, 3) + mapComponetnt + ";"; } } else { mapData = rawData; } } else if (/[0-9]/.test(rawData)) { mapData = rawDataMap(rawData, 2); } else { mapData = rawData; } } return mapData; }
//api module.exports = { "GET /api/products": async (ctx, next) => { ctx.response.type = "application/json"; ctx.response.body = { products: products }; }, "GET /api/solution1": async (ctx, next) => { try { var data = fs.readFileSync(pathname, "utf-8"); ruleJson = JSON.parse(data); rule = ruleJson.data.rule; } catch (error) { console.log("fail: " + error); } var data = { code: 200, message: "success", data: { name: "@杭城小刘", year: LBPEncode("1995", rule), month: LBPEncode("02", rule), day: LBPEncode("20", rule), analysis : rule } } ctx.set("Access-Control-Allow-Origin", "*"); ctx.response.type = "application/json"; ctx.response.body = data; }, "GET /api/solution2": async (ctx, next) => { try { var data = fs.readFileSync(pathname, "utf-8"); ruleJson = JSON.parse(data); rule = ruleJson.data.rule; } catch (error) { console.log("fail: " + error); } var data = { code: 200, message: "success", data: { name: LBPEncode("建造师",rule), birthday: LBPEncode("1995年02月20日",rule), company: LBPEncode("中天公司",rule), address: LBPEncode("浙江省杭州市拱墅区石祥路",rule), bidprice: LBPEncode("2万元",rule), negative: LBPEncode("2018年办事效率太高、负面基本没有",rule), title: LBPEncode("建造师",rule), honor: LBPEncode("最佳奖",rule), analysis : rule } } ctx.set("Access-Control-Allow-Origin", "*"); ctx.response.type = "application/json"; ctx.response.body = data; }, "POST /api/products": async (ctx, next) => { var p = { name: ctx.request.body.name, price: ctx.request.body.price }; products.push(p); ctx.response.type = "application/json"; ctx.response.body = p; } };
//路由 const fs = require("fs"); function addMapping(router, mapping){ for(var url in mapping){ if (url.startsWith("GET")) { var path = url.substring(4); router.get(path,mapping[url]); console.log(`Register URL mapping: GET: ${path}`); }else if (url.startsWith('POST ')) { var path = url.substring(5); router.post(path, mapping[url]); console.log(`Register URL mapping: POST ${path}`); } else if (url.startsWith('PUT ')) { var path = url.substring(4); router.put(path, mapping[url]); console.log(`Register URL mapping: PUT ${path}`); } else if (url.startsWith('DELETE ')) { var path = url.substring(7); router.del(path, mapping[url]); console.log(`Register URL mapping: DELETE ${path}`); } else { console.log(`Invalid URL: ${url}`); } } } function addControllers(router, dir){ fs.readdirSync(__dirname + "/" + dir).filter( (f) => { return f.endsWith(".js"); }).forEach( (f) => { console.log(`Process controllers:${f}...`); let mapping = require(__dirname + "/" + dir + "/" + f); addMapping(router,mapping); }); } module.exports = function(dir){ let controllers = dir || "controller"; let router = require("koa-router")(); addControllers(router,controllers); return router.routes(); };
-
Внешний интерфейс расшифровывает данные в обратном порядке в соответствии с данными, возвращаемыми сервером.
$("#year").html(getRawData(data.year,log)); // util.js var JoinOparatorSymbol = "3.1415926"; function isNotEmptyStr($str) { if (String($str) == "" || $str == undefined || $str == null || $str == "null") { return false; } return true; } function getRawData($json,analisys) { $json = $json.toString(); if (!isNotEmptyStr($json)) { return; } var date= new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var datacomponents = $json.split(JoinOparatorSymbol); var orginalMessage = ""; for(var index = 0;index < datacomponents.length;index++){ var datacomponent = datacomponents[index]; if (!isNaN(datacomponent) && analisys < 3){ var currentNumber = parseInt(datacomponent); orginalMessage += (currentNumber - day)/month; } else if(analisys == 3){ orginalMessage += datacomponent; } else{ //其他情况待续,本 Demo 根据本人在研究反爬方面的技术并实践后持续更新 } } return orginalMessage; }
Например, бэкенд возвращает 323.14743.14743.1446, по нашему согласованному алгоритму результат может быть 1773
-
Отрисовка страницы в соответствии с файлом ttf
Вычисленный выше 1773, а то по ttf файлу страница видит 1995 -
Затем, чтобы сканеры не могли просматривать проблемы исследования JS, файлы JS шифруются. Если ваш технологический стек Vue, React и т. д., webpack предоставляет вам плагин для шифрования JS, который также очень удобен в обращении.
-
Лично я не считаю этот метод очень безопасным. Поэтому я подумал о комбинации различных планов. Например
Модернизация против переползания
Лично я чувствую, что если дело касается опытного разработчика краулеров, вышеприведенное решение все еще может быть взломано, поэтому обновленная версия делается на основе предыдущей.
- Комбинация 1: файл шрифта не должен быть исправлен.Хотя запрошенная ссылка та же самая,модуль берется в соответствии с последним числом текущей метки времени.Например,модуль 4 берется в Демо, а есть 4 значения 0, 1, 2 и 3. Эти 4 значения соответствуют разным файлам шрифтов, поэтому, когда краулер ломал голову, чтобы доползти до шрифта в одном случае, он не ожидал запросить его повторно, и правила файла шрифта изменились 😂
- Комбинация 2: Предыдущее правило заключается в том, что проблема со шрифтом не в порядке, но нарушается только соответствие чисел. Например1 -> 4, 5 -> 8. Следующая процедура заключается в том, что каждое число соответствуетюникод код, а затем сделать нужный вам шрифт, который может быть .ttf, .woff и т.д.
Эти виды комбинаций пробиваются. Для общих рептилий он заброшен.
Антизабота, а затем модернизация средств
Перечисленные выше методы в основном направлены наколичествоЧто мне делать, если я хочу отменить восхождение китайских иероглифов? Далее несколько вариантов
-
план 1:Для облака слов с наибольшей частотой на вашем сайте сделайте карту китайских иероглифов, то есть файл пользовательского шрифта, шаги такие же, как и числа. Сначала создайте соответствующие файлы ttf для часто используемых китайских иероглифов; преобразуйте файлы ttf в файлы svg по приведенным ниже ссылкам, а затем выберите ранее созданные файлы svg на веб-сайте, где щелкнута ссылка «сопоставление шрифтов» ниже, и поместите файлы svg в файл svg.Сделайте сопоставление для каждого китайского символа , то есть китайский символ является специальным кодом юникода (обратите внимание, что код юникода здесь не должен быть напрямую сгенерирован онлайн, потому что напрямую сгенерированные вещи также Метод, который я даю, заключается в том, чтобы сначала использовать веб-сайт для его создания, а затем внести простое изменение в полученный результат, например, преобразовать «e342» в «e231»); затем данные, возвращаемые интерфейсом, отображаются в обратном порядке в соответствии с к правилам нашего файла шрифта.
-
Сценарий 2:Важные шрифты веб-сайта генерируются в изображения из html-части, поэтому сканеру необходимо идентифицировать требуемый контент с большими затратами и использовать OCR. КПД тоже низкий. Таким образом, вы можете перехватить некоторые сканеры
-
Сценарий 3:См. Обмен технологиями Ctrip «Наивысший уровень защиты от лазания — это отпечаток Canvas. Принцип заключается в том, что разные машины и разное оборудование всегда имеют ошибки на уровне пикселей в картинках, нарисованных Canvas. Поэтому мы судим, что когда большое количество холсты обращаются для доступа, всегда есть ошибки на уровне пикселей.Если отпечатки пальцев совпадают, это считается рептилией, и ее можно заблокировать».
Я реализовал решение 1 в демо.
ключевой шаг
- Сначала найдите часто используемые ключевые слова в соответствии с вашими продуктами, сгенерируйтеоблако слов
- В соответствии с облаком слов сгенерируйте соответствующий код юникода для каждого слова.
- Превратите китайские иероглифы, включенные в облако слов, в библиотеку шрифтов.
- Сделайте библиотеку шрифтов .ttf в формате svg и загрузите ее наicomoonСоздавайте пользовательские шрифты, но с правилами, например"год"соответствующийюникод кодда">74", но нам нужно сделатьШифрование Цезаря, например, мы устанавливаемКомпенсироватьравно 1, то послеШифрование Цезаря "год"соответствующийunicodeкод">75". Используйте это правило, чтобы сделать нужную нам библиотеку шрифтов
- Что сервер делает каждый раз, когда вызывается интерфейс: сервер инкапсулирует метод и оценивает, находятся ли данные в облаке слов с помощью метода Если это символ в облаке слов, используйте правила (найдите код юникода соответствующий китайскому иероглифу) , а затем установить соответствующее смещение по шифрованию Цезаря, 1 в Демо, зашифровать каждый китайский иероглиф) и вернуть данные после шифрования
- Что делает клиент:
- Сначала импортируйте библиотеку шрифтов китайских иероглифов, которую мы создали ранее.
- Вызовите интерфейс, чтобы получить данные и отобразить их на соответствующем узле Dom.
- Если это китайский текст, мы устанавливаем класс css соответствующего узла в класс китайских символов, а семейство шрифтов, соответствующее этому классу, представляет собой библиотеку шрифтов китайских символов, которую мы представили выше.
//style.css
@font-face {
font-family: "NumberFont";
src: url('http://127.0.0.1:8080/Util/analysis');
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@font-face {
font-family: "CharacterFont";
src: url('http://127.0.0.1:8080/Util/map');
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h2 {
font-family: "NumberFont";
}
h3,a{
font-family: "CharacterFont";
}
портал
Шаги по созданию шрифтов,ttf в svg,правила сопоставления шрифтов
достигнутый эффект
- Данные, отображаемые на странице, не соответствуют результатам, отображаемым элементом проверки.
- Просмотр данных интерфейса не соответствует элементам аудита и увиденному интерфейсу
- Более противоречивые результаты перед каждым обновлением страницы
- Методы обработки чисел и китайских иероглифов несовместимы.
Такие комбинации сбивают с толку. Для обычных рептилий он заброшен.
Предыдущий веб-сайт ttf to svg будет ограничивать преобразование, когда файл ttf слишком велик, и позволит вам купить его.Новая ссылка будет размещена ниже.
Демонстрационный адрес
бег шаг
//客户端。先查看本机 ip 在 Demo/Spider-develop/Solution/Solution1.js 和 Demo/Spider-develop/Solution/Solution2.js 里面将接口地址修改为本机 ip
$ cd Demo
$ ls
REST Spider-release file-Server.js
Spider-develop Util rule.json
$ node file-Server.js
Server is runnig at http://127.0.0.1:8080/
//服务端 先安装依赖
$ cd REST/
$ npm install
$ node app.js
Решения для обеспечения безопасности на стороне приложений
-
В настоящее время сетевая связь Приложения в основном использует службу HTTPS, но любой инструмент захвата пакетов может видеть подробные данные интерфейса HTTPS.Чтобы предотвратить захват пакетов и невозможность имитации интерфейса, мы принимаем следующие меры:
- Если посредник украдет данные, мы можем использовать двустороннюю аутентификацию сертификата HTTPS.В результате этого, когда посредник запускает программное обеспечение для захвата пакетов для анализа сетевого запроса приложения, сеть будет автоматически отключена, и запрос анализа не может быть просмотрен.
- Чтобы пользователи не могли имитировать наши запросы и снова делать запросы, мы можем принять «стратегию защиты от повторов», пользователи больше не могут имитировать наши запросы и снова получать данные.
- Для ресурсов H5 в приложении решение для защиты от сканирования может использовать указанное выше решение.Сетевой запрос внутри H5 может использовать гибридный уровень, чтобы позволить Native выполнять сетевой запрос, а затем возвращать данные в JS после завершения. . Цель этого заключается в том, что наш собственный уровень часто имеет полную систему учетных записей и сетевой уровень, а также хорошую политику безопасности, систему аутентификации и так далее.
- Позже мы обсудим более глубокий игровой процесс безопасности приложений, например, как защитить безопасность приложения с обратной точки зрения. Дайте статью по обратной безопасности заранеестатья
Для получения дополнительной информации о гибриде ознакомьтесь с этой статьей.Awesome Hybrid
-
Например, JS должен инициировать сетевой запрос, затем позволить Native выполнить сетевой запрос в соответствии с приведенным выше, а затем перезвонить JS.
JS побочный код
var requestObject = { url: arg.Api + "SearchInfo/getLawsInfo", params: requestparams, Hybrid_Request_Method: 0 }; requestHybrid({ tagname: 'NativeRequest', param: requestObject, encryption: 1, callback: function (data) { renderUI(data); } })
Нативный код (например, iOS)
[self.bridge registerHandler:@"NativeRequest" handler:^(id data, WVJBResponseCallback responseCallback) { NSAssert([data isKindOfClass:[NSDictionary class]], @"H5 端不按套路"); if ([data isKindOfClass:[NSDictionary class]]) { NSDictionary *dict = (NSDictionary *)data; RequestModel *requestModel = [RequestModel yy_modelWithJSON:dict]; NSAssert( (requestModel.Hybrid_Request_Method == Hybrid_Request_Method_Post) || (requestModel.Hybrid_Request_Method == Hybrid_Request_Method_Get ), @"H5 端不按套路"); [HybridRequest requestWithNative:requestModel hybridRequestSuccess:^(id responseObject) { NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil]; responseCallback([self convertToJsonData:@{@"success":@"1",@"data":json}]); } hybridRequestfail:^{ LBPLog(@"H5 call Native`s request failed"); responseCallback([self convertToJsonData:@{@"success":@"0",@"data":@""}]); }]; } }];
Выше приведена сводка безопасности первого этапа, и ее следует обновить позже (реверс приложения, антиповтор, сервер и т. д.).