Может ли секрет быть скрыт в "безопасном" классе java, предлагающем учетные данные доступа?

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

вот что я имею в виду до сих пор:

public final class Safe {

    private String secret;
    private HashMap<String, Credentials> validCertificates
            = new HashMap<String, Credentials>();

    public Safe(String aSecret) {
        this.secret = aSecret;
    }

    public final class Credentials {
        private String user;
        private Credentials(String user) {
            this.user = user;
        }
    }

    public final Credentials getCredential(String user) {
        // Following test is just for illustrating the intention...
        if ( "accepted".equals(user) ) {
            return new Credentials(user);
        } else {
            return null;
        }
    }

    public String gimmeTheSecret(Credentials cred) {
        if ( this.validCertificates.get(cred.user) == cred ) {
            return secret;
        } else {
            return null;
        }
    }

    private void writeObject(ObjectOutputStream stream) throws IOException {
        throw new RuntimeException("No no no no no no no!!!");
    }

}

можно ли его улучшить? Следует ли его улучшить? Идея замок в секрете в безопасном класса невозможно достичь?

редактировать

актуальность:

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

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

пояснение:

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

факты:

  • чтобы реализовать безопасность в приложении Java, необходимо установить экземпляр SecurityManager где проверка методы переопределяются по мере необходимости
  • это приложение может загружать ненадежный код с помощью безопасных загрузчиков классов и назначать домен защиты для загружаемых классов. Этот домен не должен включить RuntimePermission ("setSecurityManager").
  • ненадежный код может попытаться изменить SecurityManager, но так как загрузчик безопасного класса не предоставил разрешение setSecurityManager, будет создано исключение SecurityException.

решены вопросы:

Что касается среды выполнения, нам нужно различать два случая:

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

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

  • неконтролируемой среды: хакер получает, чтобы запустить приложение, которое использует ненадежный код, пытаясь сломать наш "сейф".

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

  • как установлено, в отдельный вопрос, солнца.разное.Unsafe не может сломать менеджер безопасности

12 ответов


нет, это не безопасно от другого кода Java. Ваш секрет может быть извлечен из экземпляра Safe такой:

Field field = safe.getClass().getDeclaredField("secret");
field.setAccessible(true);
String secret = (String) field.get(safe);

обновление: если вы контролируете загрузку другого кода Java, от которого вы хотите скрыть секрет, вы, вероятно, можете использовать пользовательский SecurityManager или ClassLoader для предотвращения доступа к нему. Вам нужно контролировать среду, в которой это работает, чтобы работать, например, сервер, к которому вы ограничиваете доступ.

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

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


эта "безопасность" смехотворна.

где он работает? На моем рабочем столе? Я подключаюсь к JVM с отладчиком и просматриваю все секреты в открытом тексте.

или я помещаю свой код рядом с ним и использую отражение для сброса содержимого.

или я ввожу свою собственную модификацию кода через BCEL и изменяю конструктор Safe, чтобы сбросить значение "secret" в файл.

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

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

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

реальный вопрос в любом дизайне безопасности:кто атакующий? Какова модель угрозы? Без ответа эта тема бессмысленна.


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


http://code.google.com/p/joe-e/ является подмножеством объектов java, которое предназначено для обеспечения разлагаемой безопасности - способность одной части программы сохранять свои свойства безопасности, даже когда другие части программы контролируются, скомпрометированы или манипулируются злоумышленником.

тем не менее, допустимым JVMs разрешено расширять семантику языка с помощью дополнительных языковых средств, таких как возможность присоединения отладчика во время выполнения. Код, который может использовать Runtime для вызова shell access может присоединить отладчик ко многим jvms и работать вокруг private ограничения доступа, даже если JVM настроен так, что нормальное отражение уважает поле privateНесс.

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


Я думаю, вы можете это сделать, но вы в конечном итоге толкаете проблему безопасности в другом месте. Есть ли причина, по которой "секрет" не может быть зашифрован с использованием (для простоты) вашего любимого алгоритма симметричного ключа? Метод gimmeTheSecret () должен был бы принять дополнительный параметр, являющийся секретным ключом для расшифровки секрета.

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


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

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

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

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

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

этот поток может быть вполне приемлемым для вас, если Вы доверяете компьютерам, участвующим в связи. Это относится к exemple при подключении к банковскому счету.

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

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

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

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


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

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

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

окончательное решение-разместить ненадежный код на фиктивной локальной сети собственный. (Что - то они сделали, чтобы проанализировать червя stuxnet. ;)


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

один из способов сделать это, если вы только пытаетесь проверить его, - это сохранить в нем хэш. После того, как вы хотите проверить ввод, хэш и сравнить хэши.


вы можете скрыть от .class file с помощью proguard или аналогичных инструментов. Вы также можете подписать JAR, чтобы другие пакеты не могли получить к нему доступ. Подписание вашей банки тоже может помочь.


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

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

2) Secret keeper предоставляет доступ со своим ключом, ключ temp удаляется, и создается объект temp Safe, который работает для ключа temp.

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

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

public final class Safe {
    private String secret;

    public Safe(String secret, String key){
        this.secret = encode(secret, key}

    public String getSecret(String key){
        return decode(this.secret, credentials);
    }

    public Safe shareSecret(String fromKey, String toKey){
        return new Safe(decode(this.secret, fromKey), toKey); 
    }

    private String encode(String secret, String key){
       //Code to encode the secret based on key here...
    }

    private String decode(String secret, String key){
       //Code to decode the secret based on key here...
    }
}

есть еще один угол к этой проблеме: разрешения Java delivered. В контексте управления средой можно назначить набор разрешений классам, загруженным с помощью безопасного загрузчика классов.

Java предоставляет 3 объекта разрешений, связанных с реализацией безопасности:

PrivateCredentialPermission SecurityPermission AuthPermission

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


добавление комментария для всех, кто натыкается на эту старую тему....

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

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

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