Как ограничить setAccessible только "законным" использованием?

Чем больше я узнал о силе 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!"
   }
}

Я единственный, кто считает, что это ОГРОМНАЯ забота?

Ответ 1

Мне нужно заняться этим?

Это полностью зависит от того, какие типы программ вы пишете и для какой архитектуры.

Если вы распространяете программный компонент, называемый foo.jar для людей в мире, вы все равно полностью по их милости. Они могут изменять определения классов внутри вашего .jar(посредством обратного проектирования или прямого управления байт-кодом). Они могут запускать ваш код в их собственной JVM и т.д. В этом случае беспокойство не принесет вам никакой пользы.

Если вы пишете веб-приложение, которое только взаимодействует с людьми и системами через HTTP, и вы управляете сервером приложений, это также не вызывает беспокойства. Конечно, коллеги-кодеры в вашей компании могут создавать код, который разбивает ваш шаблон singleton, но только если они действительно этого хотят.

Если ваша будущая работа заключается в написании кода в Sun Microsystems/Oracle, и вам поручено писать код для ядра Java или других доверенных компонентов, это то, о чем вы должны знать. Однако беспокойство просто заставит вас потерять ваши волосы. В любом случае они, вероятно, заставят вас читать "Правила безопасного кодирования" вместе с внутренней документацией.

Если вы собираетесь писать Java-апплеты, инфраструктура безопасности - это то, о чем вы должны знать. Вы обнаружите, что неподписанные апплеты, пытающиеся вызвать setAccessible, просто приведут к SecurityException.

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

В изолированной среде (например, Java-апплеты, JavaFX) каждый класс имеет набор разрешений и доступ к Unsafe, setAccessible и определение собственных реализаций контролируется SecurityManager.

"Модификаторы доступа Java не предназначены для обеспечения безопасности".

Это очень зависит от того, где выполняется Java-код. Основные классы Java используют модификаторы доступа в качестве механизма безопасности для принудительной работы песочницы.

Каковы действительно законные способы использования setAccessible?

Ядерные классы Java используют его как простой способ доступа к материалам, которые должны оставаться конфиденциальными по соображениям безопасности. В качестве примера, среда Java Serialization использует ее для вызова частных конструкторов объектов при десериализации объектов. Кто-то упомянул System.setErr, и это был бы хороший пример, но любопытно, что методы класса System setOut/setErr/setIn используют собственный код для установки значения конечного поля.

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

Отладчики, на мой взгляд, не попадают в эту категорию, поскольку они обычно не работают в одном JVM-процессе, а вместо этого интерфейс с JVM использует другие средства (JPDA).

Может ли Java быть спроектирована так, чтобы НЕ иметь эту потребность в первую очередь?

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

Можете ли вы ограничить использование setAccessible только законным использованием?

Самое прямое ограничение OOTB, которое вы можете применить, - это иметь SecurityManager и разрешать setAccessible только код, поступающий из определенных источников. Это то, что уже делает Java - стандартные классы Java, которые поступают из вашего JAVA_HOME, разрешены для выполнения setAccessible, в то время как неподписанные классы апплетов из foo.com не разрешают делать setAccessible. Как уже было сказано, это разрешение двоично, в том смысле, что оно есть или нет. Нет очевидного способа разрешить setAccessible изменять некоторые поля/методы, не разрешая другим. Однако с помощью SecurityManager вы можете запретить классы полностью ссылаться на определенные пакеты с отражением или без него.

Могу ли я написать свои классы, чтобы они были установлены с возможностью доступа, независимо от конфигурации SecurityManager?... Или я во власти того, кто управляет конфигурацией?

Вы не можете, и вы, безусловно, находитесь.

Ответ 2

  • Что такое истинно законное использование для setAccessible?

Тестирование модулей, внутренности JVM (например, реализация System.setError(...)) и т.д.

  • Может ли Java быть спроектирована так, чтобы НЕ иметь эту потребность в первую очередь?
  • Каковы были бы негативные последствия (если таковые имеются) такого дизайна?

Множество вещей было бы невыполнимым. Например, различные настойчивости Java, сериализации и зависимостей зависят от рефлексии. И почти все, что полагается на соглашения JavaBeans во время выполнения.

  • Можете ли вы ограничить setAccessible только законным использованием?
  • Это только через SecurityManager?

Да.

  • Как это работает? Белый список/черный список, зернистость и т.д.

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

  • Обычно ли вам приходится настраивать его в своих приложениях?

Нет.

  • Могу ли я написать свои классы как setAccessible -защищенный, независимо от конфигурации SecurityManager?
    • Или я во власти того, кто управляет конфигурацией?

Нет, вы не можете, и да, вы.

Другой альтернативой является "принудительное исполнение" этого путем использования инструментов анализа исходного кода; например пользовательские правила pmd или findbugs. Или выборочный обзор кода кода, идентифицированного (скажем) grep setAccessible ....

В ответ на следующее

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

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

И если вы имеете в виду "конфиденциальность", чтобы охватить смысл защиты конфиденциальной информации от раскрытия, вы также лаяете неправильное дерево. Способ защиты конфиденциальных данных заключается в том, чтобы не допустить ненадежного кода в изолированную программную среду безопасности, которая имеет дело с конфиденциальными данными. Модификаторы доступа Java не предназначены для обеспечения безопасности.

< Пример строки > - Я единственный, кто думает, что это ОГРОМНАЯ забота?

Вероятно, это не только только:-). Но ИМО, это не проблема. Принято, что ненадежный код должен выполняться в изолированной программной среде. Если у вас есть доверенный код/​​доверенный программист, который делает такие вещи, то ваши проблемы хуже, чем неожиданно изменяемые строки. (Думайте логические бомбы...)

Ответ 3

Отражение действительно ортогонально безопасности/безопасности в этой перспективе.

Как мы можем ограничить отражение?

Java имеет менеджер безопасности и ClassLoader в качестве основы для своей модели безопасности. В вашем случае, я думаю, вам нужно посмотреть java.lang.reflect.ReflectPermission.

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

Одним из подходов, который может стать мейнстримом в будущем, является использование так называемых зеркал для разделения отражающих возможностей от классов. См. Зеркала: принципы проектирования для объектов с метауровнями. Тем не менее существуют различные другие исследования, которые решают эту проблему. Но я согласен, что проблема более жесткая для динамического языка, чем статические языки.

Должны ли мы беспокоиться о сверхдержаве, которую дает нам отражение? Да и нет.

Да в том смысле, что платформа Java должна быть защищена с помощью ClassLoader и диспетчера безопасности. Способность возиться с отражением может рассматриваться как нарушение.

Нет в том смысле, что большинство систем в любом случае не полностью безопасно. Многие классы часто могут быть подклассифицированы, и вы могли бы уже злоупотреблять системой именно этим. Конечно, классы могут быть сделаны final или запечатаны, чтобы они не могли быть подклассифицированы в другой банке. Но только несколько классов обеспечены правильно (например, String) в соответствии с этим.

Обратите внимание на этот ответ о финальном классе для приятного объяснения. См. Также блог из Sami Koivu для более хакинга java вокруг безопасности.

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

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

Я мог бы больше рассказать о предмете, но я думаю, что сделал свою мысль.