Рекомендації з безпеки для iOS-застосунку

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Мене звуть Дар’я Частоколенко, і 4 роки я в iOS-розробці. Працюю у команді Geniusee, і серед останніх улюблених проєктів компанії — розробка необанку для нашого партнера. Хочу поділитися з вами рекомендаціями нашої команди щодо безпеки мобільних застосунків у 2021-му, які ми використовували у цьому проєкті. Також буду рада почути ваші поради щодо безпеки мобільних застосунків.

Проєкт і команда

Спершу розповім трохи про проєкт. Наш клієнт — фінтех-компанія, розташована у США, яка займається розробкою необанку для геймерів і покоління Z. Основною місією компанії є створення цифрової альтернативи традиційній фінансовій системі та можливості для кожного клієнта мати більший контроль над власними фінансами.

Розпочався проєкт майже 2 роки тому, а команда Geniusee долучилася до нього пів року тому у форматі Dedicated Team. Саме тоді закінчилися етапи бізнес-аналізу та Discovery-фаза та розпочалася активна трансформація рішення — були зроблені перші кроки для створення iOS і Android мобільних застосунків, а також сформована нова команда.

До нашої команди входить два iOS-розробники, два Android-розробники, три DevOps-інженери, три бекенд-розробники, бізнес-аналітик, два проджект-менеджери, 3 QA-інженери, техлід, CTO. Ми співпрацюємо з in-house командою нашого клієнта, яка знаходиться в Каліфорнії. До речі, саме таке розташування команд допомагає нам дотримуватися моделі Follow the sun, під час якої розробка продукту здійснюється майже 24 години на добу:)

Проблема

60% фінансових компаній зараз розробляють саме застосунки для телефонів, оскільки більшість мобільних користувачів проводить 90% свого часу у мобільних програмах. Таким чином, стає все важливіше дбати про безпеку і гарантувати, що інформація про користувача залишиться захищеною.

Будь-які порушення у мобільному застосунку можуть потенційно зашкодити всій системі, тому важливо знати, як забезпечити безпеку. Звісно, процес виявлення всіх загроз та визначення рівня безпеки величезного застосунку може бути непростим. Однак, враховуючи всю серйозність цього питання, ми створили перелік потенційних загроз, а також методи їх вирішення.

За статистикою OWASP, є 10 основних ризиків безпеки мобільних застосунків:

  • Неправильне використання платформи (Improper Platform Usage).
  • Небезпечне зберігання даних (Insecure Data Storage).
  • Небезпечна комунікація (Insecure Communication).
  • Небезпечна автентифікація (Insecure Authentication).
  • Неефективна криптографія (Insufficient Cryptography).
  • Несек’юрна авторизація (Insecure Authorization).
  • Якість коду клієнта (Client Code Quality).
  • Підробка коду (Code Tampering).
  • Зворотна інженерія (Reverse Engineering).
  • Сторонній функціонал (Extraneous Functionality).

Аби дотримуватися всіх елементів безпеки застосунку, наша команда використовує систему MASVAS (Mobile App Security Verification Guide). Це стандарт, що визначає сек’юріті-вимоги, які можуть застосовуватись до мобільних програм як на iOS, так і на Android. Всі вимоги тут поділені на два рівні L1 і L2. L1 — базовий рівень вимог, яким мають відповідати абсолютно всі аплікації, незалежно від їхнього призначення. До рівня L2 належать аплікації, що мають highly sensitive дані (це наш застосунок). Крім стандарту щодо безпекових вимог, цей гайд також містить чеклист для тестування всіх вимог MSTG (Mobile Security Testing Guide), який можуть використовувати команди тестувальників.

Наші рішення

Щоб максимально покрити всі проблеми безпеки мобільного застосунку, ми провели спеціальний окремий воркшоп, виписали список усіх можливих безпекових проблем і рішень для iOS-аплікацій. Нижче я пропоную вам більш детально пройтись по кожному пункту.

Запобігти копіюванню та вставці конфіденційних даних через буфер обміну

Проблема: Як iOS, так і Android підтримують копіювання / вставлення даних через буфер обміну. Конфіденційні дані можуть зберігатися, відновлюватися або бути зміненими в буфері обміну, незалежно від того, чи було зашифроване джерело даних спочатку. Якщо він міститься у формі відкритого тексту на момент, коли користувач його копіює, то він так само буде у формі відкритого тексту, коли інші програми отримують доступ до буфера обміну.

Рішення: під час введення сенсітів інформації, такої як паролі, дані кредитної картки та іншої, обов’язково потрібно маскувати input поля та запобігати зберіганню цих даних до кешу.

textField.isSecureTextEntry = true
textField.autocorrectionType = .no

SSL Pinning

Проблема: за замовчуванням, встановлюючи SSL-з’єднання за протоколом HTTPS, клієнт перевіряє сертифікат сервера. Але клієнт не перевіряє, чи точно даний сертифікат є саме тим сертифікатом, який використовує ваш сервер. Зіставлення клієнтом SSL-сертифікатів, які містяться в сховищі довірених сертифікатів пристрою, і тих, які використовуються на віддаленому сервері, відкриває потенційну діру в безпеці. Більшість iOS-аплікацій використовує TLS (Transport Layer Security) протокол для комунікації із сервером. Зазвичай застосунки не визначають, яким сертифікатам довіряти, і покладаються на iOS-сертифікат. Але все ж сховище сертифікатів на пристрої можна легко скомпрометувати: користувач може встановити небезпечний сертифікат і тим самим допустити man-in-the-middle атак, коли зловмисник підміняє сертифікат та отримує доступ до трафіку (атака «людина посередині»). Зловмисники можуть використовувати атаки MitM для викрадення облікових даних для входу або особистої інформації для саботажу комунікацій або пошкодження даних.

Рішення: якщо HTTP-трафік не зашифрований, будь-хто у вашій мережі може його бачити. Тож як можна перевірити, чи дозволений він? Це можна перевірити в info.plist файлі:

SSL-пінінг — процес, який дозволяє асоціювати сервер з його сертифікатом або публічним ключем. В такому випадку аплікація відкидає всі сертифікати, крім тих, які «запінені», тобто коли починається комунікація з сервером, відбувається перевірка серверного сертифіката із «запіненим» сертифікатом/ключем. Якщо вони збігаються, встановлюється клієнт-серверний зв’язок. Є два способи реалізації пінінгу: через сертифікат або хеш публічного ключа. Apple рекомендує пінити хеші ключів не сервера, а CA (certificate authority) для того, щоб в разі зміни ключів не було необхідності деплоїти аплікацію знову.

Ви можете ознайомитись з рішенням цієї проблеми в офіційному гайді від Apple.

Витік даних

Проблема: не можна заперечувати той факт, що зі збільшенням кількості мобільних застосунків зловмисники також поліпшили способи злому і крадіжки конфіденційної інформації користувачів. Витік даних може статися під час випадкового шерингу даних, наприклад, під час скриншерингу в мультитаскінг або під час переходу аплікації в бекграунд, коли система робить снепшот екрану і зберігає його на диск.

Рішення: можливими варіантами вирішення проблеми витоку даних може бути:

  1. Скрин оверлей, тобто додавання зображення логотипа або блакитного екрана при переході в бекграунд.
  2. Для кращого UX також можна чистити поля, що містять сек’юрну інформацію.
  3. Перенаправляти юзера на попередній екран.

var splash: UIImageView?

func sceneWillResignActive(_ scene: UIScene) {
        splash = UIImageView(frame: UIScreen.main.bounds)
        splash?.image = UIImage(named: "splash")
        splash?.isUserInteractionEnabled = false
        UIApplication.shared.windows.first?.addSubview(splash ?? UIImageView())
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        splash?.removeFromSuperview()
        splash = nil
    }

Face ID / Touch ID

Проблема: викрадення даних через фізичне викрадення телефона. Для злочинців використання викраденої інформації стає настільки ж привабливим, як і збут самого пристрою. Викрадач може отримати доступ до вашої персональної інформації, тому вона повинна бути надійно захищена.

Рішення: у такому випадку ми реалізували біометричний логін з пін-кодом при кожному лаунчі аплікації.

Але при неправильному використанні даного API зловмисники можуть легко пройти біометричний чек. Приклад схожого кейсу показаний на аплікаціях Dropbox та Evernote.

Огляд API для зберігання даних:

  1. UserDefaults — не зашифрований сторедж, доступний навіть на заблокованому девайсі. Жодні сенсітів дані не можуть зберігатись в юзер дефолтс.
  2. Keychain — зашифрована SQLite база даних. Розшифровується при розблокуванні девайсу.
  3. Secure Enclave — ізольований від основного процесор, що використовується для додаткового рівня безпеки, зберігання ключів для шифрування Keychain entries.

Важливим поінтом при використанні Keychain є поліси доступу, тобто умови, при яких дані будуть розшифровані.

Приклад реалізації біометричного логіну з документації Apple:

Цей підхід є вразливим, оскільки зловмисникам досить просто засвізлити булеву перевірку та підмінити значення, яке повертається на true.

Більш безпечний спосіб реалізації — це зберігати пароль в Keychain зі специфічним контролем доступу, що дозволяє прив’язувати біометрику.

При створенні контролю доступу вказуються дві умови, за яких дані з Keychain можуть бути отримані.

1. Це рівень доступу:

Наприклад, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly означає, що дані можуть бути доступні тільки тоді, коли девайс розблокований, а також тоді і тільки тоді, коли паскод засетаплений на девайсі.

ThisDeviceOnlyозначає, що ці дані не будуть розшарені через iCloud Keychain або бекап.

2. Аутентифікаційний рівень. Цей флаг дозволяє засетапити біометричну перевірку для отримання доступу до даних з Keychain.

Створення контролю доступу відбувається так:

Другий параметр якраз визначає рівень доступу, який нам потрібен. Третій параметр визначає аутентифікаційний рівень, можна вказати більш гнучкий варіант SecAccessControlCreateFlags.userPresence, який також передбачає біометричну перевірку, проте в разі невдалої спроби автоматично відбувається фолбек до перевірки паскоду.

Додавання даних:

Отримання даних:

Перевірити, чи є джейлбрейк

Проблема: на девайсі з джейлбрейком користувач може отримати рут-доступ, що означає інсталяцію софту, який не верифікований Apple та може бути шкідливим програмним забезпеченням. Тому зловмисники можуть викрасти або продати sensitive data, паролі та інші аутентифікаційні деталі.

Рішення:

1. Перевірити існування шляхів, наприклад /bin/bash.

private func checkPathExists() -> Bool {
        return access("/Applications/Cydia.app", F_OK) != -1 || access("/bin/bash", F_OK) != -1
    }

private func checkPathExists() -> Bool {
        let paths = [
            "/usr/sbin/frida-server",
            "/bin/bash",
            "/Applications/Cydia.app"
        ]
        
        let pathExists = paths.map { FileManager.default.fileExists(atPath: $0) }
        
        return !pathExists.contains(false)
    }

2. Перевірити наявність динамічних бібліотек у пам’яті через _dyld_image_count() та _dyld_get_image_name().

private func checkDyld() -> Bool {
        let libs = [
            "frida",
            "cynject",
            "libcycript"
        ]
        
        for libraryIndex in 0..<_dyld_image_count() {
            guard let loadedLibrary = String(validatingUTF8: _dyld_get_image_name(libraryIndex)) else {
                return false
            }
            
            let validation = libs.map { loadedLibrary.lowercased().contains($0.lowercased()) }
            
            return !validation.contains(false)
        }
        
        return false
    }

3. Викликати fork() або popen(). Third-party, що можна використати.

Висновок

Створення безпечних і надійних застосунків для iOS — непросте заняття. Мобільні аплікації з усіма їхніми функціями є невіддільною частиною життя майже кожного, тому важливо, щоб ми, розробники, ставилися до безпеки, а отже, до даних користувачів — з максимальною увагою.

На цьому проєкті нам вдалось з самого початку розробити стратегію щодо захисту мобільного застосунку, а також продумати якомога більше вразливостей, котрі надалі могли б стати реальними загрозами та проблемами з безпекою. Саме тому ми рекомендуємо продумувати систему безпеки на етапі створення архітектури проєкту, щоб всі нюанси заздалегідь були враховані перед написанням коду.

Які практики ви використовуєте для захисту мобільних додатків? Пишіть у коментарях.

👍НравитсяПонравилось14
В избранноеВ избранном5
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Все хорошо написано.
Забыли про человеческий фактор.

Але тут проблема, що хацкери можуть написати обхід перевірки на джейлбрейк через заміну

access()

FileManager.default.fileExists()

_dyld_image_count()

_dyld_get_image_name()

fork()

popen()

за допомогою Frida на кастомні реалізації, які не будуть знаходити Frida та джейл ))

Як вихід, можна використовувати syscall-и, виклики яких складніше перехопити та замінити на свої.

Мало того, если кракер(реверс-инженер) задастся целю, то сможет код который отвечает за проверки на взломанном девайсе заменить на nop-пы(выключит проверки).

Так что в теории эти все проверки на наличие файлов решает лишь часть проблем.

У Apple есть более продвинутое решение для такого: developer.apple.com/...​hing_your_app_s_integrity

Но глубоко еще не копал, может у кого-то есть больше информации об этом?

Для этого хакер должен получить рута, а не только присутствовать на рутованом девайсе, правильно? Но если у него рут, его в принципе мало что остановит от банальнейшего слива данных хозяину или тупейшей подмены приложения на фишинговую версию.

Було корисно. Дякую 🙂

👏 дякую, гарна стаття.

Трохи уточню :)

* MASVS — mobile application security verification standard
* MSTG — mobile security testing guide

Це проєкти-сестри.

Перший (MASVS) це такий собі величезний чеклист вимог (security requirements). Його можна скачати як таблицю та відмічати «є / немає». Другий (MSTG) це «книга» для розробників та тестувальників, яка пояснює, як саме перевіряти, чи застосунок відповідає конкретній вимозі.

Подписаться на комментарии