Как ограничить 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 #.)
Том Хотин - tackline

Ответы:

105

НУЖНО ли мне беспокоиться об этом ???

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

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

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

Если ваша будущая работа заключается в написании кода в 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 использует его для вызова конструкторов частных объектов при десериализации объектов. Кто-то упомянул 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? ... Или я во власти того, кто управляет конфигурацией?

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

Сами Койву
источник
«Если вы распространяете программный компонент под названием foo.jar среди людей во всем мире, вы в любом случае полностью в их власти. Они могут изменить определения классов внутри вашего .jar (посредством обратного проектирования или прямого изменения байт-кода). Они могут запускать ваш код в собственной JVM и т. д. В этом случае беспокойство не принесет вам пользы ". Так ли это до сих пор? Какие-нибудь советы по усложнению взлома программного обеспечения (надеюсь, значительно)? Из-за этого сложно просто выбросить из окна настольные java-приложения.
Серо
@naaz Я бы сказал, что ничего не изменилось. Просто архитектура работает против вас. Я могу изменить любую программу, работающую на моей машине, над которой я полностью контролирую. Самым сильным сдерживающим фактором может быть законный, но я не думаю, что он будет работать для всех сценариев. Я думаю, что двоичные файлы Java одними из самых простых в обращении, но люди с опытом могут вмешаться во что угодно. Обфускация помогает, затрудняя внесение больших изменений, но любые логические значения (например, проверка авторских прав) по-прежнему легко обойти. Вместо этого вы можете попробовать разместить ключевые части на сервере.
Сами Койву
Как насчет денуво?
Серо
15
  • Для чего действительно законное использование setAccessible?

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

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

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

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

Да.

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

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

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

Нет.

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

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

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

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

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

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

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

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

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

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

Стивен С
источник
1
Также полезно для таких вещей, как реализации сохраняемости. Отражения не очень полезны для отладчиков (хотя изначально это было задумано). Вы не можете с пользой изменить (подкласс) SecurityManagerдля этого, потому что все, что вы получаете, - это проверка прав доступа - все, что вы действительно можете сделать, это посмотреть на acc и, возможно, пройтись по стеку, проверить текущий поток). grep java.lang.reflect.
Tom Hawtin - tackline
@Stephen C: +1 уже, но я также был бы признателен за ваш вклад в еще один вопрос, который я только что добавил. Заранее спасибо.
polygenelubricants
Обратите внимание, что grep - ужасная идея. Существует так много способов динамического выполнения кода на Java, что вы почти наверняка упустите один. В общем, внесение кода в черный список - это рецепт неудачи.
Antimony
«Модификаторы доступа Java не предназначены для использования в качестве механизма безопасности». - на самом деле, да, они есть. Java спроектирована таким образом, чтобы позволить надежному и ненадежному коду сосуществовать на одной виртуальной машине - это было частью ее основных требований с самого начала. Но только когда система работает с заблокированным SecurityManager. Возможность изолировать код полностью зависит от применения спецификаторов доступа.
Жюль
@Jules - Большинство экспертов по Java с этим не согласны; например, stackoverflow.com/questions/9201603/…
Stephen C
11

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

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

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

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

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

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

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

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

Посмотри это ответ о последнем классе для хорошего объяснения. Смотрите также блог Сами Койву чтобы больше о java-хакерских атаках.

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

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

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

Ewernli
источник