Подтвердить что ты не робот

Являются ли классы java Inner опасными для безопасности?

Недавно группа безопасности в моем проекте выпустила документ с рекомендациями по безопасному коду, предназначенный для использования в рамках наших обзоров кода. Первое, что меня поразило, это предмет, который сказал "Не использовать внутренние классы". Я подумал, что это похоже на очень тяжелое рутинное выражение. Внутренние классы хороши, если их правильно использовать правильно, но я немного поработал с поиском и нашел этот, приведенный здесь для удобства.

Правило 5: Не используйте внутренние классы

Некоторые книги на Java говорят, что к внутренним классам можно получить доступ только внешние классы, которые их охватывают. Это неправда. Байт Java-кода имеет нет понятия внутренних классов, поэтому внутренние классы переводятся компилятором в обычные классы, которые происходят с быть доступным для любого кода в том же пакет. Правило 4 говорит, что не зависеть на упаковке для защиты.

Но подождите, все становится хуже. Внутренний класс получает доступ к полям охватывающий внешний класс, даже если эти поля объявляются закрытыми. И внутренний класс переводится в отдельный класс. Чтобы это разрешить отдельный доступ класса к полям внешний класс, компилятор молча изменяет эти поля от частного до область упаковки! Это плохо, что внутренний класс разоблачен, но это еще хуже, что компилятор тихо отменяя ваше решение сделать некоторые поля частными. Не использовать если вы можете это сделать. (По иронии судьбы, новая Java 2 doPrivileged() API предположим, что вы используете внутренний класс для пишите привилегированный код. Вон тот причина, по которой нам не нравится doPrivileged().)

Мои вопросы

  • Сохраняется ли такое поведение в java 5/6?
  • Является ли это на самом деле угрозой безопасности, учитывая, что какой-либо класс, кроме внешних и внутренних классов, пытающихся получить доступ к закрытым членам внешнего класса, не будет компилироваться?
  • Означает ли он достаточный риск для безопасности, чтобы подтвердить "рекомендацию" "Не использовать внутренние классы"?
4b9b3361

Ответ 1

Эта информация примерно за десять лет устарела. Широкое использование анонимных внутренних классов с AccessController.doPrivileged должно быть ключом. (Если вам не нравится API, рассмотрите долю блоков try - finally, которые неправильно отсутствуют в JDK.)

Политика заключается в том, что ни один из двух классов не может использовать один и тот же пакет, если они загружаются разными загрузчиками классов или имеют разные сертификаты. Для большей защиты отметьте пакеты как запечатанные в манифесте ваших банок. Таким образом, с точки зрения безопасности "Правило 4" является фиктивным и, следовательно, также этим правилом.

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

Ответ 2

  • Да, это поведение все еще существует.
  • Это риск безопасности, потому что класс изгоев может быть создан с чем-то другим, чем стандартный javac.
  • Это зависит от того, насколько вы параноидальны:) Если вы не разрешаете чужим классам работать в вашей JVM, я не вижу проблемы. И если вы это сделаете, у вас больше проблем (песочницы и все)
  • Я знаю, что у вас было только 3 вопроса, но, как и другие люди, я думаю, что это глупое ограничение.

Ответ 3

Сохраняется ли такое поведение в java 5/6?

Не так, как описано; Я никогда не видел компилятор, где это было правдой:

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

Вместо этого IIRC Sun Java 3/4 создал аксессуар, а не изменял поле.

Sun Java 6 (javac 1.6.0_16) создает статический аксессор:

public class InnerExample {
    private int field = 42; 

    private class InnerClass {
        public int getField () { return field; };
    }

    private InnerClass getInner () { 
        return new InnerClass();
    }

    public static void main (String...args) {
        System.out.println(new InnerExample().getInner().getField());
    }
}


$ javap -classpath bin -private InnerExample
Compiled from "InnerExample.java"
public class InnerExample extends java.lang.Object{
    private int field;
    public InnerExample();
    private InnerExample$InnerClass getInner();
    public static void main(java.lang.String[]);
    static int access$000(InnerExample);
}


$ javap -classpath bin -c -private InnerExample
static int access$000(InnerExample);
  Code:
   0:   aload_0
   1:   getfield    #1; //Field field:I
   4:   ireturn

Является ли это на самом деле угрозой безопасности, учитывая, что любой класс, кроме внешних и внутренних классов, которые пытались получить доступ к закрытым членам внешнего класса, не стал бы [p] ile?

Я немного размышляю здесь, но если вы компилируете против класса, это не так, но если вы добавите access$000, тогда вы можете скомпилировать код, который использует аксессор.

import java.lang.reflect.*;

public class InnerThief {
    public static void main (String...args) throws Exception {
        for (Method me : InnerExample.class.getDeclaredMethods()){
            System.out.println(me);
            System.out.printf("%08x\n",me.getModifiers());
        }

        System.out.println(InnerExample.access$000(new InnerExample()));
    }
}

Интересно, что у синтезированного аксессора есть флаги-модификаторы 00001008, где, если вы добавляете статический метод уровня пакета, у него есть флаги 00000008. Во втором выпуске спецификации JVM для значения этого флага нет ничего, но он, кажется, мешает тому, что метод рассматривается javac.

Итак, похоже, что там есть функция безопасности, но я не могу найти на ней никакой документации.

(отсюда этот пост в CW, если кто-то знает, что означает 0x1000 в файле класса)

Ответ 4

Сохраняется ли такое поведение в java 5/6?

Вы можете использовать инструмент javap, чтобы определить, что ваши двоичные файлы выставляют и как.

package demo;
public class SyntheticAccessors {
  private boolean encapsulatedThing;

  class Inner {
    void doSomething() {
      encapsulatedThing = true;
    }
  }
}

Вышеприведенный код (скомпилированный с Sun Java 6 javac) создает эти методы в SyntheticAccessors.class:

Compiled from "SyntheticAccessors.java"
public class demo.SyntheticAccessors extends java.lang.Object{
    public demo.SyntheticAccessors();
    static void access$0(demo.SyntheticAccessors, boolean);
}

Обратите внимание на новый метод access$0.

Ответ 5

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

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

Если вы размещаете свой код, то почему вы беспокоитесь о том, что кто-то соскучился по вашим внутренним классам?

Если вы собираетесь связать какой-то сторонний код, которому вы не доверяете, и не можете проверить его во время выполнения, тогда выложите его.

Как я уже сказал выше, если это действительно политика в вашей компании, незамедлительно сообщите вашей компании thedailywtf.com

Ответ 6

Вам следует подумать о том, какую защиту может предоставить ваше приложение. Приложение с защищенной архитектурой не будет работать в этих именованных проблемах.

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

Помните, что вы всегда можете декомпилировать файлы классов java. И не полагайтесь на "безопасность безвестности". Даже обфускационный код может быть проанализирован, понят и изменен.

Ответ 7

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

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

Ответ 8

"Это на самом деле риск для безопасности, учитывая, что какой-либо класс, кроме внешних и внутренних классов, которые пытались получить доступ к закрытым членам внешнего класса, не собирался?"

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

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

Ответ 9

Обратите внимание, что перечисленные недостатки не сохраняются для внутренних классов static, поскольку они не имеют неявного доступа к их окружающему классу (или объекту действительно).

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

@Tom, цитируя спецификацию языка Java," классы-члены могут быть static, и в этом случае они имеют без доступа к переменным экземпляра окружающего класса

Ответ 10

нонсенс! следовать той же логике, не пишите общедоступные методы, потому что они имеют доступ к закрытым полям, gush!