Як обмежити setAccessible лише "законними" використаннями?


100

Чим більше я дізнався про силу java.lang.reflect.AccessibleObject.setAccessible, тим більше я здивований тим, що вона може зробити. Це адаптовано з моєї відповіді на питання ( Використання відображення для зміни статичного остаточного File.separatorChar для тестування одиниць ).

import java.lang.reflect.*;

public class EverythingIsTrue {
   static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }
   public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

Ви можете робити справді неприємні речі:

public class UltimateAnswerToEverything {
   static Integer[] ultimateAnswer() {
      Integer[] ret = new Integer[256];
      java.util.Arrays.fill(ret, 42);
      return ret;
   }   
   public static void main(String args[]) throws Exception {
      EverythingIsTrue.setFinalStatic(
         Class.forName("java.lang.Integer$IntegerCache")
            .getDeclaredField("cache"),
         ultimateAnswer()
      );
      System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42"
   }
}

Імовірно, розробники API усвідомлюють, наскільки зловживаючими setAccessibleможуть бути, але, мабуть, визнали, що він має законне використання для його надання. Отже, мої запитання:

  • Для чого справді законне використання setAccessible?
    • Чи може Java була розроблена таким чином, щоб НЕ мала в першу чергу цієї потреби?
    • Якими можуть бути негативні наслідки (якщо такі є) такого дизайну?
  • Чи можете ви обмежитися setAccessibleлише законним використанням?
    • Чи тільки через SecurityManager?
      • Як це працює? Білий список / чорний список, деталізація тощо?
      • Чи часто це потрібно налаштувати у своїх програмах?
    • Чи можу я записати свої заняття на setAccessibleнезахищені незалежно від SecurityManagerконфігурації?
      • Або я на розсуд того, хто керує конфігурацією?

Думаю, ще одне важливе питання: чи потрібно мені працювати над цим ???

Жоден із моїх занять не має виду дотримання конфіденційності як ніколи. Однотонну закономірність (відкладаючи сумніви щодо її достоїнств) тепер неможливо застосувати. Як показують мої фрагменти вище, навіть деякі основні припущення про те, як працює фундаментальна програма Java, навіть не гарантуються.

ЦІ ПРОБЛЕМИ НЕ РЕАЛЬНІ ???


Гаразд, я щойно підтвердив: завдяки setAccessible, рядки Java НЕ непорушні.

import java.lang.reflect.*;

public class MutableStrings {
   static void mutate(String s) throws Exception {
      Field value = String.class.getDeclaredField("value");
      value.setAccessible(true);
      value.set(s, s.toUpperCase().toCharArray());
   }   
   public static void main(String args[]) throws Exception {
      final String s = "Hello world!";
      System.out.println(s); // "Hello world!"
      mutate(s);
      System.out.println(s); // "HELLO WORLD!"
   }
}

Я єдиний, хто думає, що це ВЕЛИЧЕЗНА турбота?


30
Ви повинні побачити, що вони можуть робити на C ++! (О, і, мабуть, C #.)
Том Хоутін - тайклін

Відповіді:


105

ЧИ МОЖЕ ТВОРИТИ ПРО ЦЕЙ ???

Це повністю залежить від того, які типи програм ви пишете та для якої архітектури.

Якщо ви розповсюджуєте програмний компонент, який називається foo.jar, людям світу, ви все одно в їх повноті. Вони можуть змінювати визначення класів всередині вашого .jar (за допомогою зворотної інженерії або прямого маніпулювання байт-кодом). Вони можуть запустити ваш код у власному JVM тощо. У цьому випадку турбота не принесе вам користі.

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

Якщо ваша майбутня робота - це написання коду в Sun Microsystems / Oracle і вам покладено завдання написати код для ядра Java або інших надійних компонентів, це вам слід знати. Тим не менш, хвилювання просто змусить тебе втратити волосся. У будь-якому випадку вони, ймовірно, змусять вас прочитати Правила безпечного кодування разом із внутрішньою документацією.

Якщо ви збираєтеся писати Java-аплети, рамки безпеки - це те, про що слід пам’ятати. Ви побачите, що безпідписані аплети, які намагаються викликати setAccessible, просто призведуть до SecurityException.

setAccessible - не єдине, що стосується звичайних перевірок цілісності. Існує не API, основний клас Java під назвою sun.misc.Unsafe, який може робити майже все, що завгодно, включаючи прямий доступ до пам'яті. Рідний код (JNI) може також обходити такий спосіб управління.

У середовищі з пісочницею (наприклад, Java Applets, JavaFX) кожен клас має набір дозволів і доступ до Unsafe, setAccessible та визначаючі власні реалізації контролюються SecurityManager.

"Модифікатори доступу до Java не мають бути механізмом захисту."

Це дуже залежить від того, де виконується запуск коду Java. Основні класи Java використовують модифікатори доступу в якості механізму захисту для застосування пісочної скриньки.

Які справді легальні можливості використання setAccessible?

Основні класи Java використовують його як простий спосіб отримати доступ до матеріалів, які повинні залишатися приватними з міркувань безпеки. Як приклад, рамка серіалізації Java використовує її для виклику приватних конструкторів об'єктів при десеріалізації об'єктів. Хтось згадав System.setErr, і це був би хороший приклад, але цікаво, що методи системного класу setOut / setErr / setIn усі використовують нативний код для встановлення значення остаточного поля.

Іншим очевидним законним використанням є рамки (наполегливість, веб-фреймворки, впорскування), які потрібно зазирнути до нутрощів об’єктів.

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

Чи може Java була розроблена таким чином, щоб НЕ мала в першу чергу цієї потреби?

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

Чи можете ви обмежити setAccessible лише законним використанням?

Найбільш прямолінійне обмеження OOTB, яке ви можете застосувати, - це мати SecurityManager і дозволяти setAccessible лише для коду, що надходить з певних джерел. Це те, що вже робить Java - стандартним Java-класам, які походять від вашого JAVA_HOME, дозволяється робити setAccessible, тоді як непідписаним класам аплетів з foo.com заборонено робити setAccessible. Як було сказано раніше, цей дозвіл є двійковим, в тому сенсі, що він має чи ні. Немає очевидного способу дозволити setAccessible змінювати певні поля / методи, забороняючи інші. Використовуючи SecurityManager, ви, однак, можете заборонити класам повністю посилатися на певні пакети, з відображенням або без них.

Чи можу я записати свої класи, щоб бути встановленимидоступними незалежно від конфігурації SecurityManager? ... Або я на милість того, хто керує конфігурацією?

Ти не можеш, і ти, безумовно, є.


"Якщо ви розповсюджуєте програмний компонент під назвою foo.jar серед людей світу, то все одно ви повністю їм дозволені. Вони можуть змінювати визначення класів всередині вашого .jar (за допомогою зворотної інженерії або прямого маніпулювання байтовим кодом). Вони може запустити свій код у власному JVM тощо. У цьому випадку турбота не принесе вам нічого корисного ". Це все-таки так? Будь-які поради щодо втручання програмного забезпечення (сподіваємось значно)? Через це важко просто викинути java настільні програми через вікно.
Sero

@naaz Я б сказав, що нічого не змінилося. Просто архітектура працює проти вас. Будь-яке програмне забезпечення, яке працює на моїй машині, над яким я маю повний контроль, я можу змінити. Найсильніший стримуючий засіб може бути законним, але я не думаю, що він буде працювати у всіх сценаріях. Я думаю, що бінарні файли Java є одними з найпростіших для зворотного зв’язку, але люди, які мають досвід, можуть щось підробити. Неясність допомагає, утрудняючи великі зміни, але все-таки булеве (на зразок перевірки авторських прав) все-таки банально обійти. Ви можете спробувати помістити ключові частини на сервер.
Самі Койву

Як щодо денуво?
Sero

15
  • Для чого справді законне використання setAccessible?

Тестування одиниць, внутрішніх даних СВМ (наприклад, впровадження System.setError(...)) тощо.

  • Чи може Java була розроблена таким чином, щоб НЕ мала в першу чергу цієї потреби?
  • Якими можуть бути негативні наслідки (якщо такі є) такого дизайну?

Багато речей було б незмінним. Наприклад, різні наполегливості Java, серіалізація та ін'єкції залежності залежать від роздумів. І майже все, що покладається на конвенції JavaBeans під час виконання.

  • Чи можете ви обмежитися setAccessibleлише законним використанням?
  • Чи тільки через SecurityManager?

Так.

  • Як це працює? Білий список / чорний список, деталізація тощо?

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

  • Чи часто це потрібно налаштувати у своїх програмах?

Немає.

  • Чи можу я записати свої заняття на setAccessibleнезахищені незалежно від SecurityManagerконфігурації?
    • Або я на розсуд того, хто керує конфігурацією?

Ні, ти не можеш, і так, ти є.

Інша альтернатива - це "застосувати" це за допомогою інструментів аналізу вихідного коду; наприклад, звичай pmdабо findbugsправила. Або вибірковий огляд коду, ідентифікований (скажімо) grep setAccessible ....

У відповідь на подальші дії

Жоден із моїх занять не має виду дотримання конфіденційності як ніколи. Однотонну закономірність (відкладаючи сумніви щодо її достоїнств) тепер неможливо застосувати.

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

З іншого боку, якщо ви маєте на увазі "конфіденційність", щоб охопити значення захисту конфіденційної інформації від розкриття, ви гавкаєте неправильне дерево. Спосіб захисту конфіденційних даних у програмі Java - це не допускати ненадійного коду у пісочну скриньку безпеки, яка обробляє чутливі дані. Модифікатори доступу до Java не є механізмом захисту.

<Строковий приклад> - Я єдиний, хто вважає це ВЕЛИЧЕЗНОЮ турботою?

Напевно, не єдиний :-). Але ІМО, це не хвилює. Прийнято те, що ненадійний код повинен виконуватися в пісочниці. Якщо ви довіряєте коду / довіреному програмісту, що робить подібне, то ваші проблеми гірші, ніж несподівано змінні Strings. (Подумайте логічні бомби, ексфільтрація даних через приховані канали та ін.)

Існують способи вирішити (або пом'якшити) проблему "поганого актора" у вашій команді з розробки чи операцій. Але вони затратні та обмежуючі ... та надмірні для більшості випадків використання.


1
Також корисно для таких речей, як наполегливі реалізації. Роздуми не дуже корисні для налагоджувачів (хоча спочатку це було призначено). Ви не можете корисно змінити (підклас) SecurityManagerдля цього, оскільки все, що ви отримаєте, - це перевірка дозволу - все, що ви дійсно можете зробити, це подивитися на акаунт і, можливо, пройти стек перевірити поточну нитку). grep java.lang.reflect.
Том Хотін - тайклін

@Stephen C: +1 уже, але я також буду вдячний за ваше ще одне запитання, яке я тільки що додав. Заздалегідь спасибі.
полігенмастильні матеріали

Зауважте, що греп - це жахлива ідея. Існує так багато способів динамічного виконання коду на Java, що ви майже напевно пропустите його. Код чорного списку взагалі - це рецепт відмови.
Сурма

"Модифікатори доступу до Java не мають бути механізмом захисту." - насправді, так вони є. Java створена для того, щоб дозволити коді існувати довірений і ненадійний код в одній віртуальній машині - це було частиною його основних вимог з самого початку. Але лише тоді, коли система працює із заблокованим SecurityManager. Можливість коду пісочниці повністю залежить від виконання специфікаторів доступу.
Жуль

@Jules - Більшість експертів Java не погодиться з цим; наприклад, stackoverflow.com/questions/9201603/…
Стівен C

11

Роздуми дійсно є ортогональними щодо безпеки / безпеки в цій перспективі.

Як ми можемо обмежити рефлексію?

У Java є менеджер з безпеки і ClassLoaderє основою для її моделі безпеки. У вашому випадку, я думаю, вам потрібно подивитися java.lang.reflect.ReflectPermission.

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

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

Чи варто турбуватися про наддержаву, яку дає нам відображення? Так і ні.

Так, у тому сенсі, що платформа Java повинна бути захищена Classloaderі менеджером безпеки. Здатність возитися з роздумом можна сприймати як порушення.

Ні в тому сенсі, що більшість систем так чи інакше не є безпечними. Багато класів часто можна підкласифікувати, і ви потенційно вже можете зловживати системою саме цим. Звичайно, класи можуть бути складені finalабо запечатані так, щоб їх не можна було класифікувати в іншій банці. Але згідно з цим лише кілька класів забезпечені правильно (наприклад, String).

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

Модель безпеки Java може вважатись недостатньою для певної точки зору. Деякі мови, такі як NewSpeak, застосовують ще більш радикальний підхід до модульності, де ви маєте доступ лише до того, що вам явно надається інверсією залежності (за замовчуванням нічого).

Важливо також зазначити, що безпека так чи інакше відносна . На мовному рівні ви, наприклад, не можете запобігти формуванню модуля, який споживає 100% процесора або споживає всю пам'ять до а OutOfMemoryException. Такі проблеми потрібно вирішувати іншими способами. Ми можемо побачити, що в майбутньому Java розширить квоти на використання ресурсів, але це не завтра :)

Я міг би більше розширити цю тему, але, думаю, я зробив свою думку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.