slides.com

/jabher

/paranoid-service-worker-kharkivjs/live

goo.gl/pDcpSy

Web is insecure?

  • Код приходит каждый раз с сервера
  • Потенциально небезопасный контекст
    • Внешние скрипты: CDN, аналитика, etc
    • Нет явного управления IO (только CSP)
    • Браузерные плагины
  • Привычки и стереотипы
    • Мы не строим модели атакующих
    • Мы не строим рубежи безопасности

web is insecure

Последние взломы

  • Facebook
  • OnePlus
  • Yahoo
  • Ebay
  • LinkedIn
  • Dropbox
  • Wordpress
  • Ashley Madison
  • Adobe

Web is insecure by design?

  • Интернет был создан для обмена информацией, а не как платформа приложений
  • Веб - самая популярная платформа, и самая "вкусная" для атак
  • Веб становится платформой для приложений
    • PWA
    • WebAssembly
    • WebAuthentication, WebUSB, WebNFC

Mozilla warning

  • Security is a process that constantly evaluates the risks a system incurs in its context of use.
  • The context and the risks experienced evolve over time.
  • When dealing with security, the whole system must be considered.
  • The overall security can't be stronger than the security of the weakest part of the overall system.

The Problem

data application on client

trusted platform

features

  • API с доступом к хранилищу уровня браузера
  • Нет доступа к самим данным
  • Предоставляет доступ к
    • const PublicKey
    • function sign (Data) => Signature
  • Доступ через библиотеку
    • + function verify (Data, Signature) => PublicKey

Trusted platform

interface PlatformAPI {
    sensitive: never;
    getKeys (): Key[];
    sign (Data, Key): Signature;
    verify (Data, Signature): Key;
}

platform model

  • Доступ с любого сайта, удовлетворяющего условиям
    • HTTPS
  • Доверенная платформа
    • не-рутованный Android/IOS
    • не-взломанный Win/MacOS/Linux
  • Свежий браузер, соответствующий стандартам
    • Chrome/FF/Edge
    • Возможно, модифицированный (маскирующийся)
    • Может иметь плагины (и не давать их ставить)

Attacker abilities

  1. MITM
    1. может перехватить или изменить любой low-level запрос
  2. 3rd-party
    1. Может дергать API с любой частотой и параметрами
    2. Может дергать внутреннее API
  3. Мы сами
    1. Можем отдавать любые файлы
    2. Можем отдавать любые заголовки

ATTACKER ABILITIES

4. Браузерный плагин

  1. Может внедрять скрипты в любую http/https страницу
    1. Скрипты работают в отдельном контексте, но могут делать все то, что можем мы
    2. Не отключаются через CSP sandbox
  2. Могут перехватывать запросы
    1. Могут читать/писать заголовки запроса/ответа
    2. Не могут читать заголовки Host, Authorization 
    3. Могут делать redirect и отмену любого запроса

attacker model

  • 3rd party
    • вызывать API (в том числе флудить)
    • выполнять фаззинг API
  • MitM/мы
    • отдавать плохие файлы/заголовки
  • плагины
    • выполнять код в любом фрейме
    • переадресовывать любой запрос
    • удалять ненужные заголовки
    • добавлять нужные

The Threats

private key leak

  • Owasp A3 - sensitive data exposure
  • Может быть украден
    •  Вредоносным кодом
      • С сервера
      • MitM
      • Плагином в фрейме
    • Из внутренностей браузера
    • Из внутренностей ОС

Denial of service

  • Spam/fuzzy вызовы API
    • Перегрузка сервиса
    • Перегрузка оповещениями
    • Кто атакует: 3rd party, плагины
  • Плохие редиректы: MITM, плагины
  • Испорченные ответы: MITM, плагины
    • Данные и заголовки могут поменяться

public data exposure

  • Данные:​
    • Публичный ключ
    • Аргументы вызова подписи
    • Подпись
  • Кто атакует: все

sum up

  • 😱 Украли приватные ключи
  • 😩 API не работает
  • 🤔 Подсмотрели вызов API

the solution

Disclaimer: аудит безопасности не завершен. Не пытайтесь повторить в домашних условиях реальных проектах

baseline

<iframe 
id="frame"
src="https://secure.app/sign?data=0xDEADBEEF">
    
    <!doctype html>
    <script src="/handler.js"
        integrity="sha384-..."/>
    <script>
        window.parent.postMessage(
            await handle(location)
        )
    </script>

</iframe>

untrusted network

  1. Перехват DNS => DNSSEC
  2. Подмена содержимого => HTTPS
  3. Первоначальное переключение HTTP -> HTTPS
    • HTTP заголовок: Strict-Transport-Security
    • Список предзагрузки HSTS
    • TLS 1.3
    • Определение TLS MitM
    • HTTP заголовок: Expect-CT

MAN-in-the-middle

TLS/SSL vulnerabilities

TLS/SSL vulnerabilities

  • DROWN
  • CRIME
  • BEAST
  • BREACH
  • FREAK
  • Logjam
  • NOMORE
  • Bar Mitzvah
  • SWEET32
  • POODLE
  • Heartbleed​

untrusted frame

async injectIframe(frameUrl, query) => {
    const defer = new Deferred()
    const frame = createIframe(frameUrl, query)
    const mo = new MutationObserver(
        mutations => deferred.reject()
    )
    mo.observe(iframe, {
        attributes: true
    })
    document.body.appendChild(frame)
    
    return defer
}

untrusted frame

const key = createKey()

const blobFrameUrl = URL.createObjectURL(new Blob([
`<!doctype html>
<iframe 
        id="iframe"
        src="${iframeUrl}" 
        sandbox="allow-same-origin allow-scripts">
<script>
    iframe.contentWindow.onmessage = ({data}) => 
        window.parent.postMessage(
            encrypt(data, ${key})
    )`
], {
    type: 'text/html'
}))

untrusted frame

untrusted server

async verifyUrl (frameUrl, integrity) => {
const {ok, headers} = await fetch(frameUrl, {
   integrity,
   redirect: 'manual',
   cache: 'force-cache',
   mode: 'cors'
})
assert(
   headers.get('Cache-Control') === 'max-age=84600',
   headers.has('pragma') === false
)
await injectIframe(frameUrl, query)
}

untrusted server

untrusted cache

//iframe.html
<script>
await navigator.storage.persist()
await navigator.serviceWorker.register('sw.js')
//sw.js
self.addEventListener('fetch', event =>
    event.respondWith(new Response(`
<!doctype html>
<script src="/handler.js" integrity="sha384-..."/>
<script>
    window.parent.postMessage(
        await handle(location))
</script>`)))

service worker

service worker

service worker

untrusted cache

//iframe.html
<script>
await navigator.storage.persist()
await navigator.serviceWorker.register('sw.js')
//sw.js
self.addEventListener('fetch', event =>
    event.respondWith(new Response(`
<script src="/handler.js" integrity="sha384-..."/>
<script>
    window.parent.postMessage(
        await handle(location))
</script>`)))

untrusted args

handlers.doSomethingImportant = async (...args) => {
    await notification('confirm?', {
        requireInteraction: true,
        data: args,
        actions: [  
           {action: 'res', title: 'yes'},  
           {action: 'rej', title: 'no'}
        ]
    })
    ...
}

untrusted args

// sw.js
self.addEventListener(
    'notificationclick', 
    ({notification, action}) => {  
        event.notification.close()
        actions.get(notification.data).call(action)
    }, 
    false
)

untrusted args

self.addEventListener('fetch', async event =>
    event.respondWith(new Response(`
<!doctype html>
<script>
window.parent.postMessage(
${
    JSON.stringify(
        await handle(event.request.url)
    )
})</script>`)))

untrusted plugins

origin betrayal

only allow-same-origin should be required for SW interception within a sandboxed iframe

 

By the time the request gets to fetch, it doesn't know it came from a sandboxed iframe

w3c/ServiceWorker/issues/648

origin: null

  • iframe sandbox=not allow-same-origin
  • data: URI
  • blob: URI (в SW не работает)
  • file:// URI
  • chrome-extension://

origin: null

self.addEventListener('fetch', async event =>
    event.respondWith(Response.redirect(

        'data:text/html,' +
        encodeURIComponent(`
        <!doctype html>
            <script>
            parent.postMessage(${
                JSON.stringify(
                    await handle(event.request.url)
                )
            })`))))

the result

what's secured

  • не дает загрузить с сервера недействительный/незнакомый контент - защита от всех
  • не дает подписывать данные без разрешения пользователя - защита от неожиданного подписания
  • не дает выполнить скрипты в контексте фрейма - защита от плагинов

what's not mentioned

  • как импортировать/экспортировать private keys?
  • как добавлять функционал/обновлять библиотеку по необходимости?

  • защита от DoS?

  • защита от публичного доступа?

the end

Всеволод Родионов, JS-шалун

@jabher

Paranoid Service Worker

By jabher

Paranoid Service Worker

  • 1,871