Как ограничить setAccessible только «законным» использованием? - PullRequest
97 голосов
/ 20 марта 2010

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

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

Ответы [ 3 ]

103 голосов
/ 22 марта 2010

НУЖНО ЛИ БЫТЬ ОБ ЭТОМ БЕСПОКОЙСТВОМ ??? Это полностью зависит от того, какие программы вы пишете и для какой архитектуры.

Если вы распространяете программный компонент под названием 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 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, вы можете запретить классам ссылаться на определенные пакеты полностью, с отражением или без него.

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

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

14 голосов
/ 20 марта 2010
  • Каково действительно законное использование для setAccessible?

Модульное тестирование, внутренние компоненты JVM (например, реализация System.setError(...)) и т. Д.

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

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

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

Да.

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

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

  • Часто ли приходится настраивать его в своих приложениях?

номер

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

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

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

В ответ на продолжение

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

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

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

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

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

11 голосов
/ 20 марта 2010

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

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

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

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

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

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

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

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

См. ответ о заключительном занятии для хорошего объяснения. См. Также блог от Sami Koivu для получения дополнительной информации о безопасности Java.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...