Безопасные сборки C # от неавторизованных абонентов

Есть ли способ защитить вашу сборку до уровня class / property & class / method, чтобы предотвратить использование / вызов из другой сборки, которая не подписана нашей компанией?

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

Например:

Первый сценарий

Foo.dll подписывается моей компанией, и Bar.dll не подписан вообще.

Foo имеет class A, имеет class B

Класс A имеет открытый метод GetSomething () Класс B пытается вызвать Foo.A.GetSomething () и отклоняется

Отклонение может быть исключением или быть проигнорировано каким-то образом

Сценарий два

Foo.dll подписана моей компанией, а Moo.dll также подписана моей компанией.

Foo имеет class A Moo имеет class C

Класс A имеет открытый метод GetSomething () Класс C пытается вызвать Foo.A.GetSomething () и не отклоняется

Если вы хотите ограничить вызывающих абонентов только кодом, который был аутентифицирован определенным сертификатом, вы все равно можете использовать CAS (только не StrongNameIdentityPermission).

Используйте PublisherIdentityPermission так же, как вы использовали бы разрешения CAS. Или, если вы хотите сделать это декларативно, используйте атрибут .

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

Из метода, который вы можете использовать

new StackTrace().GetFrame(1).GetMethod().Module.Assembly 

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

 callingAssembly.GetName().GetPublicKey() 

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

Но есть одна петлевая дыра – assembly сторонних производителей может задерживаться под подпиской с вашим открытым ключом компаний и исключаться из проверки цифровой подписи. Вследствие этого загрузчик будет загружать стороннюю сборку с сильным именем и открытым ключом ваших компаний, даже если он еще не подписан. Чтобы закрыть это отверстие петли, вы должны проверить подпись. Нет управляемого API, и вы должны P / Invoke

 Boolean StrongNameSignatureVerificationEx( String wszFilePath, Boolean fForceVerification, ref Boolean pfWasVerified) 

с параметром fForceVerification установите значение true и проверьте, true ли результат.

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

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

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

В модели CR вам нужно будет передать какой-то токен каждому вызову метода (или, возможно, просто создать экземпляр объекта). Токен будет classом, который только ваш код может создать экземпляр – я бы сделал это внутренним classом сборки, который вы хотите использовать, и сделать его доступным с помощью InternalsVisibleTo – таким образом нужно управлять только одним classом:

 // SharedAssembly.dll // marks ConsumingAssembly.dll as having access to internals... internal sealed class AccessToken { } public class SecuredClass { public static bool WorkMethod( AccessToken token, string otherParameter ) { if( token == null ) throw new ArgumentException(); // you may want a custom exception. // do your business logic... return true; } } // ConsumingAssembly.dll (has access via InternalsVisibleTo) public class MainClass { public static void Main() { var token = new AccessToken(); // can create this because of IVT access SecuredClass.WorkMethod( token, "" ); // tada... } } 

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

Создание механизма CR для каждого метода является громоздким и утомительным. Он также не является на 100% надежным – кто-то с достаточным временем и терпением мог бы найти способ обойти его.

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

Я видел DLL, написанные компаниями (в первую очередь Pegasus Imaging), которые используют систему вызова / ответа, чтобы разблокировать сборку. Покупателю DLL предоставляется «Лицензионный код», привязанный к имени покупателя, который потребитель DLL использует для разблокировки определенного подмножества функций DLL.

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

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

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

Я думаю, что это слишком шумно! Если вам действительно нужна безопасность, поместите свой код за сервер и используйте архитектуру клиент-сервер. Или веб-сервисы. Или что-то среднее между WCF или удалением. Затем используйте аутентификацию для аутентификации клиента.

Если вы можете сделать все приватным, выставить открытый API и локально локализовать вызовы.

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

Я вижу, что некоторые конвенции появляются. И это может сработать для вас. Но это не дает вам «полной безопасности», которую вы требуете. Если у вас есть assembly, которая должна быть скрыта от клиентов, не помещайте ее в GAC. Используйте пространства имен с надписью «ВНУТРЕННИЙ».

Я не думаю, что есть способ сделать это, если вы не контролируете среду выполнения, в которой выполняется код. Код, работающий с полным доверием на машине пользователя, сможет обойти любые введенные вами ограничения. Например, полный код доверия может вызывать частные или внутренние методы с API-интерфейсом отражения, поэтому даже использование InternalsVisibleToAttribute не будет работать.

Если вы контролируете среду исполнения, вы можете создать AppDomain, где ваш код полностью доверен, а сторонний код частично доверен и не может вызывать ваш код, если вы не поместите в сборку атрибут AllowPartiallyTrustedCallersAttribute ( APTCA ). Вы можете ограничить, какие методы могут быть вызваны в сборке APTCA с атрибутами SecurityCritical и SecuritySafeCritical .

Практическое руководство. Запуск частично доверенного кода в песочнице