slides.com
/jabher
/paranoid-service-worker-kharkivjs/live
goo.gl/pDcpSy
Web is insecure?
- Код приходит каждый раз с сервера
- Потенциально небезопасный контекст
- Внешние скрипты: CDN, аналитика, etc
- Нет явного управления IO (только CSP)
- Браузерные плагины
- Привычки и стереотипы
- Мы не строим модели атакующих
- Мы не строим рубежи безопасности
web is insecure
Последние взломы
- OnePlus
- Yahoo
- Ebay
- 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
- MITM
- может перехватить или изменить любой low-level запрос
- 3rd-party
- Может дергать API с любой частотой и параметрами
- Может дергать внутреннее API
- Мы сами
- Можем отдавать любые файлы
- Можем отдавать любые заголовки
ATTACKER ABILITIES
4. Браузерный плагин
- Может внедрять скрипты в любую http/https страницу
- Скрипты работают в отдельном контексте, но могут делать все то, что можем мы
- Не отключаются через CSP sandbox
- Могут перехватывать запросы
- Могут читать/писать заголовки запроса/ответа
- Не могут читать заголовки Host, Authorization
- Могут делать 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
- Перехват DNS => DNSSEC
- Подмена содержимого => HTTPS
- Первоначальное переключение HTTP -> HTTPS
- HTTP заголовок: Strict-Transport-Security
- Список предзагрузки HSTS
- TLS 1.3
- Определение TLS MitM
- HTTP заголовок: Expect-CT
MAN-in-the-middle
- Chinese Certificate Authority 'mistakenly' gave out SSL Certs for GitHub Domains
- Kazakhstan makes it Mandatory for its Citizens to Install Internet Backdoor
- Symantec’s issued a pre-certificate for the domains google.com and www.google.com. This pre-certificate was neither requested nor authorized by Google.
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
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