Безопасность: ограничить внутренний доступ сторонним программным обеспечением

У меня есть Java-приложение, в котором сторонние "плагины" могут загружаться пользователями для улучшения взаимодействия с пользователем. Для этих плагинов существует API, но стороннее программное обеспечение должно быть ограничено в доступе к внутренним классам приложений в целях безопасности. Ограниченным пакетом для плагинов будет "com.example", а разрешенным будет "com.example.api". Классы API делают вызовы к внутренним, запутанным классам.

Изучив это, я натолкнулся на пару методов SecurityManager: checkMemberAccess (Class, int) и checkPackageAccess (String), которые, казалось, были жизнеспособными путями к моей цели. Однако после выполнения некоторых тестов и дальнейших исследований я обнаружил, что checkMemberAccess применяется только к вызовам отражения, а checkPackageAccess вызывается только тогда, когда загрузчик классов вызывает loadClass.

Каков разумный способ ограничить доступ к пакету (com.example, но не com.example.api)?

1 ответ

Решение

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

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

class RestrictingClassLoader extends URLClassLoader {
  @Override
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    if (!name.startsWith("com.example.") || name.startsWith("com.example.api."))
      return super.loadClass(name);
    return findClass(name);
  }
}

class RestrictingSecurityManager extends SecurityManager {
  private boolean isRestricted() {
    for (Class<?> cls: getClassContext())
      if (cls.getClassLoader() instanceof RestrictingClassLoader)
        return true;
    return false;
  }
  // Implement other checks based on isRestricted().
}
Другие вопросы по тегам