mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 10:27:49 +00:00
fix(security): SRI для CDN-скрипта сканера QR
Автоматическое ревью справедливо отметило загрузку html5-qrcode с CDN без проверки целостности: подмена файла на стороне CDN исполнилась бы в кабинете с полными правами страницы (доступ к сессии, initData Telegram). Скрипт подключается с integrity (sha384 сверен по фактическому файлу html5-qrcode@2.3.8), crossorigin=anonymous и referrerPolicy=no-referrer. При несовпадении хеша браузер откажется исполнять скрипт, и сканер деградирует в «камера недоступна» вместо запуска чужого кода. Заодно закрыт тот же изъян в исходном месте, откуда пришёл паттерн: TvQuickConnect грузил тот же файл с jsdelivr вообще без проверки. Он переведён на общий хелпер (файлы на unpkg и jsdelivr побайтово идентичны — сверил), дублирующийся загрузчик и интерфейс удалены. Виджеты telegram.org намеренно оставлены без SRI: это самообновляемые эндпоинты Telegram, фиксация хеша сломала бы логин при их обновлении.
This commit is contained in:
@@ -12,6 +12,11 @@ import {
|
||||
const TG_MOBILE_PLATFORMS = new Set(['ios', 'android', 'android_x', 'ios_x']);
|
||||
|
||||
export const HTML5_QRCODE_CDN = 'https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js';
|
||||
// SRI-хеш ИМЕННО этого файла (sha384 от html5-qrcode@2.3.8/html5-qrcode.min.js).
|
||||
// Без него подмена на стороне CDN исполнилась бы в кабинете с полными правами
|
||||
// страницы. При несовпадении браузер откажется исполнять скрипт — сканер
|
||||
// деградирует в «камера недоступна», а не запускает чужой код.
|
||||
const HTML5_QRCODE_SRI = 'sha384-c9d8RFSL+u3exBOJ4Yp3HUJXS4znl9f+z66d1y54ig+ea249SpqR+w1wyvXz/lk+';
|
||||
|
||||
export interface Html5QrcodeInstance {
|
||||
start: (
|
||||
@@ -62,9 +67,14 @@ export async function loadHtml5Qrcode(): Promise<WindowWithHtml5['Html5Qrcode']
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = HTML5_QRCODE_CDN;
|
||||
script.integrity = HTML5_QRCODE_SRI;
|
||||
script.crossOrigin = 'anonymous';
|
||||
script.referrerPolicy = 'no-referrer';
|
||||
document.head.appendChild(script);
|
||||
await new Promise<void>((resolve) => {
|
||||
script.onload = () => resolve();
|
||||
// onerror срабатывает и при провале проверки целостности — вызывающий
|
||||
// получит undefined и покажет «камера недоступна».
|
||||
script.onerror = () => resolve();
|
||||
});
|
||||
return w.Html5Qrcode;
|
||||
|
||||
Reference in New Issue
Block a user