Безопасность сборочного плагина .NET - PullRequest
11 голосов
/ 31 мая 2009

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

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

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

private void LoadPlugins(string pluginsDirectory)
{
    List<IPluginFactory> factories = new List<IPluginFactory>();

    foreach (string path in Directory.GetFiles(pluginsDirectory, "*.dll"))
    {
        Assembly assembly = Assembly.LoadFile(path);
        foreach (Type type in assembly.GetTypes())
        {
            IPluginEnumerator instance = null;
            if (type.GetInterface("IPluginEnumerator") != null)
                instance = (IPluginEnumerator)Activator.CreateInstance(type);
            if (instance != null)
            {
                factories.AddRange(instance.EnumerateFactories());
            }
        }
    }

    // Here, I would usually collate the plugins into List<ISpecificPlugin>, etc.
}

Первые несколько проблем у меня есть:

  1. Эта функция читает весь каталог и не заботится о том, какие сборки он загружает, а просто загружает все из них. Есть ли способ определить, является ли сборка действительной, функциональной сборкой .NET, прежде чем загружать ее с помощью Assembly.LoadFile ()?
  2. Какой тип обработки исключений следует добавить в функцию, чтобы предотвратить остановку моего кода при инициализации сборки?
  3. Если я хочу отказать сборке в праве сделать следующее: чтение / запись файлов, чтение / запись в реестр и т. Д., Как мне это сделать?

Есть ли какие-либо другие проблемы безопасности, о которых мне следует беспокоиться?

РЕДАКТИРОВАТЬ: имейте в виду, что я хочу, чтобы кто-нибудь мог написать плагин, но я все еще хочу быть в безопасности.

Ответы [ 6 ]

13 голосов
/ 29 августа 2009

1) строгое название сборки с определенным ключом.

  • вы не должны поместить его в GAC
  • вы можете повторно использовать ключ для подписи более чем одной сборки
  • при повторном использовании ключа вы получаете один и тот же «открытый ключ» на каждой подписанной сборке

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

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

Пример: * * один тысяча двадцать-одна

public static StrongName GetStrongName(Assembly assembly)
{
    if(assembly == null)
        throw new ArgumentNullException("assembly");
    AssemblyName assemblyName = assembly.GetName();

    // get the public key blob
    byte[] publicKey = assemblyName.GetPublicKey();
    if(publicKey == null || publicKey.Length == 0)
       throw new InvalidOperationException( String.Format("{0} is not strongly named", assembly));

    StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);

    // create the StrongName
    return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
}


// load the assembly:
Assembly asm = Assembly.LoadFile(path);
StrongName sn = GetStrongName(asm);

// at this point
// A: assembly is loaded
// B: assembly is signed
// C: we're reasonably certain the assembly has not been tampered with
// (the mechanism for this check, and it's weaknesses, are documented elsewhere)

// all that remains is to compare the assembly's public key with 
// a copy you've stored for this purpose, let's use the executing assembly's strong name
StrongName mySn = GetStrongName(Assembly.GetExecutingAssembly());

// if the sn does not match, put this loaded assembly in jail
if (mySn.PublicKey!=sn.PublicKey)
    return false;

примечание: код не был протестирован или скомпилирован, может содержать синтаксические ошибки.

3 голосов
/ 28 июля 2009

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

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

Таким образом, ваша единственная текущая защита - это защита доступа к каталогу на уровне ОС. Это может сработать для вас, но это только один уровень защиты, и вы зависите от состояния безопасности, которое не можете контролировать.

Вот две вещи, на которые вы можете посмотреть:

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

Однако я сомневаюсь, что вы хотите установить эти плагины в GAC, чтобы вы могли сделать это следующим образом:

  1. В вашем приложении предоставьте пользователям возможность зарегистрировать плагин, по сути, мини-GAC. Когда они хранятся, вы сохраняете местоположение и название сборки, а также открытый ключ. Это требует, чтобы Ассамблея носила строгое имя.

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

    private bool DoPublicKeysCompare(Assembly assembly, byte[] expectedPublicKey)
    {
        byte[] assemblyKey = assembly.GetName().GetPublicKey();
        return expectedPublicKey.SequenceEqual(assemblyKey);
    }

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

2 голосов
/ 31 мая 2009
  1. Если вы обеспокоены безопасностью своих сборок, вам следует строго назвать их. Это обеспечивает высокий уровень безопасности, что сборка действительно является той, которую вы намереваетесь сделать.

  2. Исключения, с которыми вы можете столкнуться во время загрузки, следующие. Добавьте try / catch вокруг вашей попытки загрузки в Assembly.Load () и реагируйте в соответствии с типом ошибки:

    • ArgumentNullException
    • FileLoadException
    • FileNotFoundException
    • BadImageFormatException
  3. Сборки, которые вы загружаете динамически, должны иметь те же права, что и учетная запись пользователя, которая их загрузила, если эта сборка не находится в GAC. Создайте учетную запись службы с нужными вам правами и запустите приложение, используя эту учетную запись для контроля доступа.

1 голос
/ 29 августа 2009

Взгляните на платформу AddIn от Microsoft, поскольку она предоставляет некоторые возможности, которые вы ищете, такие как безопасность и изоляция.

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

1 голос
/ 31 мая 2009

Если вы работаете с 3.5, о новом материале System.AddIns можно многое сказать - посмотрите примеры http://www.codeplex.com/clraddins.

-Oisin

0 голосов
/ 31 августа 2009

Я предпочитаю использовать Assembly.ReflectionOnlyLoadFrom (...)

см. Следующую статью MSDN для получения дополнительной информации:

Как: загрузить сборки в контексте только для отражения

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